privatadata属性:传给特定函数的可选参数 。
ht属性:长度为2的dictht数组,一般情况下只使用ht[0]散列表,而ht[1]散列表只会在对ht[0]散列表进行rehash时使用
rehashidx属性:记录了rehash目前的进度,如果目前没有进行rehash那么值为-1
DictType的定义
typedef struct dictType{ //哈希函数 unsigned int (*hashFunction)(const void *key); //复制Key的函数 void *(*keyDup)(void *privatedata, const void *key); //复制Value的函数 void *(*valDup)(void *privatedata, const void *obj); //对比Key的函数 int (*keyCompare)(void *privatdata, const void *key1 , const void *key2); //销毁Key的函数 void (*keyDestructor)(void *privatedata, void *key); //销毁Value的函数 void (*valDestructor)(void *privatedata, void *obj);}dictType;3.1 在字典中进行查找、添加、更新、删除操作
在字典中进行查找
以客户端传递的Key作为关键字K,通过dict中的dictType的H(K)散列函数计算散列值,使用dictht[0]的sizemask属性和散列值计算索引,遍历索引对应的链表,判断是否存在Key相同的DictEntry,若存在则返回该DictEntry,否则返回NULL 。
在字典中进行添加和更新操作
以客户端传递的Key作为关键字K,通过dict中的dictType的H(K)散列函数计算散列值,使用dictht[0]的sizemask属性和散列值计算索引,遍历索引对应的链表,判断是否存在Key相同的DictEntry,若不存在Key相同的DictEntry,则创建代表Key的SDS对象和RedisObject以及代表Value的对象和RedisObject,然后创建一个DictEntry并分别指向Key和Value对应的RedisObject,最终将该DictEntry追加到链表的最后一个节点中,若存在Key相同的DictEntry,则判断当前的命令是否满足Value对应的类型,若满足则进行更新,否则报错 。
创建和更新操作是相对的,当不存在则创建否则进行更新 。
在字典中进行删除操作
以客户端传递的Key作为关键字K,通过dict中的dictType的H(K)散列函数计算散列值,使用dictht[0]的sizemask属性和散列值计算索引,遍历索引对应的链表,找到Key相同的DictEntry进行删除 。
3.2 散列表的扩容和缩容
由于散列表的负载因子需要维持在一个合理的范围内,因此当散列表中的元素过多时会进行扩容,过少时会进行缩容 。
一旦散列表的长度发生改变,那么就要进行rehash,即对原先散列表中的元素在新的散列表中重新进行hash 。
Redis中的rehash是渐进式的,并不是一次性完成,因为要考虑性能问题,如果散列表中包含上百万个节点,那么庞大的计算量可能会导致Redis在一段时间内无法对外提供服务 。
在rehash进行期间,每次对字典执行查找、添加、更新、删除操作时,除了会执行指定的操作以外,还会顺带将ht[0]散列表在rehashidx索引上的所有节点rehash到ht[1]上,然后将rehashidx属性的值加1 。
渐进式Rehash的步骤
1.为字典的ht[1]散列表分配空间 。
- 若执行的是扩容操作,那么ht[1]的长度为第一个大于等于ht[0].used*2的2? 。
- 若执行的是缩容操作,那么ht[1]的长度为第一个大于等于ht[0].used的2? 。
3.在rehash进行期间,每次对字典执行查找、添加、更新、删除操作时,除了会执行指定的操作以外,还会顺带将ht[0]散列表在rehashidx索引上的所有节点rehash到ht[1]上,然后将rehashidx属性的值加1 。
4.随着对字典不断的操作,最终在某个时间点上,ht[0]散列表中的所有dictEntry都会被rehash到ht[1]上,当rehash结束之后将rehashidx属性的值设为-1,表示rehash操作已完成 。
- 在进行渐进式rehash的过程中,字典会同时使用ht[0]和ht[1]两个散列表,因此字典的查找、更新、删除操作会在两个散列表中进行,如果在ht[0]计算得到的索引指向NULL则从ht[1]中进行匹配 。

文章插图
4.1 INT编码方式

文章插图
INT编码方式会将RedisObject中的*ptr指针直接改写成long prt,prt属性直接存储整数值 。
4.2 EMBSTR编码方式

文章插图
4.3 ROW编码方式

文章插图
- EMBSTR和ROW编码方式在内存中都会创建一个RedisObject和SDS,区别在于EMBSTR编码方式中RedisObject和SDS共同使用同一块内存单元,Redis内存分配器只需要分配一次内存,而ROW编码方式中需要分别为RedisObject和SDS分配内存单元 。
推荐阅读
- 一文教你读懂如何手工配置DBControl
- Linux循环设备/dev/loop解析
- Nginx为什么高效?一文搞明白Nginx核心原理
- 基本概念+优缺点+美团应用案例 一文看懂逻辑回归算法
- 右眼皮跳测吉凶风水解析
- 武则天亲自教你如何做大佬,一文读懂10条职场生存法则
- 十二时辰吉凶风水解析
- 光纤是怎么到你家的?一文了解清楚
- 一文看懂2021年华为开发者大会
- 一文带你揭穿葡萄酒中的山寨货、水货、假货
