神奇,Redis存储原理竟然是这样!

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis是一个Key-Value模式的非关系型数据库,那么Key和Value的保存模式我们在这里说一说。其实kv给大家的第一影响是啥?数组?哈希?没错就是哈希,redis其实是用哈希表保存的键值对,键为str,值为数据类型,哈希表中的每一对元素是哈希桶

本文的部分内容参考自《小林Coding》,部分地方根据源代码进行剖析。
Redis源码地址:https://github.com/redis/redis.git

今天继续

Redis存储是如何实现的?

键值对的实现

Redis是一个Key-Value模式的非关系型数据库,那么Key和Value的保存模式我们在这里说一说。

其实kv给大家的第一影响是啥?数组?哈希?

没错就是哈希,redis其实是用哈希表保存的键值对,键为str,值为数据类型,哈希表中的每一对元素是哈希桶

哈希桶存放的是啥?如果学过C++的STL,这里你可以理解为pair<string,T>

下面来西索一下。

img

下面来简单聊聊图上面的东西,具体的聊我会在后文说,真实的存储其实和这个图还是有些区别

Redis的数据库管理

  • redisDb 结构,表示 Redis 数据库的结构,结构体里存放了指向了 dict 结构的指针;

  • dict 结构,结构体里存放了 2 个哈希表,正常情况下都是用「哈希表1」,「哈希表2」只有在 rehash 的时候才用;

  • dictht 结构,表示哈希表的结构,结构里存放了哈希表数组,数组中的每个元素都是指向一个哈希表节点结构(dictEntry)的指针;

    在最新版的dict中已经将dictht改为了数组,这样可能稍微难以理解,一会儿细说

  • dictEntry 结构,表示哈希表节点的结构,结构里存放了 void key 和 void value 指针, key 指向的是 String 对象,而 \value 则可以指向 String 对象,也可以指向集合类型的对象,比如 List 对象、Hash 对象、Set 对象和 Zset 对象

下面我们来说说源代码,我会在保留原注释的条件下进行解释

/* Redis database representation. There are multiple databases identified
 * by integers from 0 (the default database) up to the max configured
 * database. The database number is the 'id' field in the structure. */
//Redis数据库结构,通过ID来识别多个数据库
typedef struct redisDb {
   
    // 当前数据口的所有键值对空间
    dict *dict;                 /* The keyspace for this DB */
    // 存放已设置exptime的key的集合
    dict *expires;              /* Timeout of keys with a timeout set */

    // 客户端等待的阻塞中的key
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP)*/
    // 客户端等待的阻塞中的key,如果key被删除,那么应该取消阻塞,是blocking_keys的子集
    dict *blocking_keys_unblock_on_nokey;    /* Keys with clients waiting for data, 
                                              *and should be unblocked if key is deleted (XREADEDGROUP).
                                              *This is a subset of blocking_keys*/
    // 可以解除阻塞的键(客户端已经收到的key)
    dict *ready_keys;           /* Blocked keys that received a PUSH */
    // 还在被watch命令监视中的键
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
    // 数据库id
    int id;                     /* Database ID */
    // 数据库的平均TTL
    long long avg_ttl;          /* Average TTL, just for stats */
    unsigned long expires_cursor; /* Cursor of the active expire cycle. */
    list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */
    clusterSlotToKeyMapping *slots_to_keys; /* Array of slots to keys. Only used in cluster mode (db 0). */
} redisDb;

//dictEntry的实现很简单,就是个链表
struct dictEntry {
   
    void *key;
    union {
   
        void *val;            //其他
        uint64_t u64;        //整数
        int64_t s64;        //整数
        double d;            //双精度数值
    } v;
    struct dictEntry *next;     /* Next entry in the same hash bucket.  指向下一个hash值相同的entry*/
    void *metadata[];           /* An arbitrary number of bytes (starting at a
                                 * pointer-aligned address) of size as returned
                                 * by dictType's dictEntryMetadataBytes(). */
};


struct dict {
   
    dictType *type;

    dictEntry **ht_table[2];    //第二个表用来进行rehash    
    /**
    *    dictEntry **ht_table[2]; <==>
    *     typedef struct dictht{
    *        dictEntry **table;
    *    }dictht;
    *    ditcht ht[2];
    **/

    unsigned long ht_used[2];    

    //用于保证rehash安全,如果值为1,那么不能执行rehash
    long rehashidx; /* rehashing not in progress if rehashidx == -1 */

    //redis6之后新加入,如果值>0则暂停rehash,保证rehash安全
    /* Keep small vars at end for optimal (minimal) struct padding */
    int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error) */

    //哈希的内存指数
    signed char ht_size_exp[2]; /* exponent of size. (size = 1<<exp) */

    //由dictType的dictEntryBytes定义的大小的任意字节数(从指针对齐的地址开始)。
    void *metadata[];           /* An arbitrary number of bytes (starting at a
                                 * pointer-aligned address) of size as defined
                                 * by dictType's dictEntryBytes. */
};

/* If safe is set to 1 this is a safe iterator, that means, you can call
 * dictAdd, dictFind, and other functions against the dictionary even while
 * iterating. Otherwise it is a non safe iterator, and only dictNext()
 * should be called while iterating. */
typedef struct dictIterator {
   
    dict *d;
    long index;
    int table, safe;
    dictEntry *entry, *nextEntry;
    /* unsafe iterator fingerprint for misuse detection. */
    unsigned long long fingerprint;
} dictIterator;

然后redisServer的内容有点多,我就截图了

image-20230720025349861

从这个图我们看出来,不仅数据可以用dict来存储,命令也是。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
4天前
|
缓存 NoSQL Apache
【Redis】布隆过滤器原理与应用
【Redis】布隆过滤器原理与应用
20 1
|
4天前
|
存储 NoSQL 算法
09- Redis分片集群中数据是怎么存储和读取的 ?
Redis分片集群使用哈希槽分区算法,包含16384个槽(0-16383)。数据存储时,通过CRC16算法对key计算并模16383,确定槽位,进而分配至对应节点。读取时,根据槽位找到相应节点直接操作。
73 12
|
4天前
|
存储 缓存 NoSQL
深入解析Redis:一种快速、高效的键值存储系统
**Redis** 是一款高性能的键值存储系统,以其内存数据、高效数据结构、持久化机制和丰富的功能在现代应用中占有一席之地。支持字符串、哈希、列表、集合和有序集合等多种数据结构,适用于缓存、计数、分布式锁和消息队列等场景。安装Redis涉及下载、编译和配置`redis.conf`。基本操作包括键值对的设置与获取,以及哈希、列表、集合和有序集合的操作。高级特性涵盖发布/订阅、事务处理和Lua脚本。优化策略包括选择合适数据结构、配置缓存和使用Pipeline。注意安全、监控和备份策略,以确保系统稳定和数据安全。
312 1
|
4天前
|
存储 监控 NoSQL
Redis处理大量数据主要依赖于其内存存储结构、高效的数据结构和算法,以及一系列的优化策略
【5月更文挑战第15天】Redis处理大量数据依赖内存存储、高效数据结构和优化策略。选择合适的数据结构、利用批量操作减少网络开销、控制批量大小、使用Redis Cluster进行分布式存储、优化内存使用及监控调优是关键。通过这些方法,Redis能有效处理大量数据并保持高性能。
22 0
|
4天前
|
负载均衡 NoSQL 关系型数据库
深入浅出Redis(六):Redis的主从架构与主从复制原理
深入浅出Redis(六):Redis的主从架构与主从复制原理
|
4天前
|
存储 NoSQL 算法
深入浅出Redis(三):Redis数据的存储、删除以及淘汰
深入浅出Redis(三):Redis数据的存储、删除以及淘汰
|
4天前
|
监控 NoSQL 算法
深入剖析Redis哨兵模式的原理和应用
Redis的哨兵模式是实现高可用性和自动故障转移的机制,当主服务器故障时,哨兵能自动检测并进行故障转移,确保服务连续和稳定性。哨兵模式通过监控主从服务器状态、自动故障转移、防止数据不一致,提高容错能力和负载均衡,降低运维成本,实现高可用性。哨兵通过检测主观下线和客观下线状态,以及选举Leader Sentinel来协调故障转移。Raft算法在其中用于领导者选举和状态一致性。哨兵模式通过综合考虑多种因素选举新主服务器并执行故障转移,保障集群稳定运行。
88 0
深入剖析Redis哨兵模式的原理和应用
|
4天前
|
NoSQL Redis
Redis入门到通关之Redis主从数据同步原理
Redis入门到通关之Redis主从数据同步原理
31 0
|
4天前
|
存储 NoSQL Redis
Redis入门到通关之Redission原理
Redis入门到通关之Redission原理
23 0
|
4天前
|
缓存 NoSQL 算法
【redis】布隆过滤器(Bloom Filter)原理解析与应用
【redis】布隆过滤器(Bloom Filter)原理解析与应用
26 1