深入底层C源码 Redis核心设计原理( 四 )


【2】对于类型改变的需要变换存储空间 。
3.RedisDb 数据结构1)代码展示
//位于server.h文件中typedef struct redisDb {dict *dict;// 保存了当前数据库的键空间dict *expires;//键空间中所有键的过期时间dict *blocking_keys;//客户端等待数据的键(BLPOP)dict *ready_keys;//保存着处于阻塞状态的键 , value为NULLdict *watched_keys;//监视键的MULTI/EXEC CASint id;//数据库IDlong long avg_ttl;//键的平均过期时间unsigned long expires_cursor; //周期性删除过期键的游标list *defrag_later;/* List of key names to attempt to defrag one by one, gradually. */} redisDb;//位于dict.h文件中typedef struct dict {dictType *type;void *privdata;dictht ht[2]; // ht[0] , ht[1] =null //方便渐进的rehash扩容 , dict的hashtable  , 其中?一个哈希表正常存储数据? , ?另一个哈希表为空 , 空哈希表在 rehash 时使用long rehashidx; /* rehash 索引 , 当不在进行 rehash 时 , 值为 -1 */unsigned long iterators; //当前正在运行的迭代器的数量} dict;//位于dict.h文件中/*这是我们的哈希表结构 。每本字典都有两个这样的词 , 实现增量重哈希 , 从旧表到新表 。* /typedef struct dictht {dictEntry **table;unsigned long size; //hashtable 容量unsigned long sizemask;// size -1unsigned long used;// hashtable 元素个数used / size =1} dictht;//位于dict.h文件中typedef struct dictEntry {void *key;union {void *val;uint64_t u64;int64_t s64;double d;} v;struct dictEntry *next;} dictEntry;//位于server.h文件中//redisObject对象 :string , list ,set ,hash ,zset ...typedef struct redisObject {unsigned type:4;//4 bit, sting , hashunsigned encoding:4;//4 bitunsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or LFU data (least significant 8 bits frequency* and most significant 16 bits access time).*24 bit* */int refcount;// 4 bytevoid *ptr;// 8 byte总空间:4 bit + 4 bit + 24 bit + 4 byte + 8 byte = 16 byte} robj;2)视图展示
 

深入底层C源码 Redis核心设计原理

文章插图
3)代码说明
【1】由上可知redisDb , 主要都是将数据存储在字典(dict)中 , 而且还是多个 , 固定存储 , 过期维护等多个字典 。
【2】dict字典结构 , 每个字典有两个哈希表结构的原因是为了用于渐进式扩容 , 当某个哈希表结构过于庞大的时候(按照hashMap的思维 , 必定是需要对数组进行扩容 , 增大数组长度 , 将链表长度缩小 , 加快遍历) , 其实它也需要进行扩容 , 但是再进行扩容操作的同时 , 容易出现阻塞线程的情况(如果时间太久) , 为此 , dict中采用rehashidx标明是否正在处于扩容状态 , 且ht[1]会生成一个新的哈希表结构 , 容量是之前的两倍 , 然后把ht[0]中的数据按槽位一点一点的搬运过来【断断续续的操作 , 这样就不会一直阻塞住线程】 , 新的数据也会落到ht[1]中 , 直到搬完 。然后将ht[1]指针指向ht[0] , 然后自己再指向null , rehashidx变为0 , 就完成了扩容操作 。
【3】dictEntry相当于hashMap中的节点(包含了key , value , 和指向下个节点的指针) , 其中val会被进一步封装成redisObject 。
【4】redisObject中的type用于约束客户端命令 , 如set操作 , 会判断操作的值与操作的类型匹不匹配 。encoding记录了值在redis底层是怎么样的编码形式 。ptr指向内存的真实地址 。
4)分析String类型的编码
【1】会存在:int , raw , embstr三种 。

经验总结扩展阅读