链表
首先Redis中存在的抽象结构为List,而链表是其一种实现方式,当list元素较多或者元素都为比较长的字符串时,采用链表作为底层实现。此外,链表还用在Redis服务器保存客户端状态、发布与订阅功能等等。
在Redis中,链表是双向链表,其Node结构定义如左图,List的定义如右图:
观察得知,Redis中链表的特点如下
- 无环(头节点的pre指向null,尾节点的next也指向null)
- 具有头指针和尾指针,找表头和表尾的时间复杂度都是O(1)
- 双端
- 多态(由于多态的存在,使得链表可以保存不同类型的值并正常处理)
- 带有链表长度len
字典
Key-value键值对,key独一无二,Redis的数据库就是用这种数据结构实现的,使用哈希表来实现,其中哈希表节点定义如左图所示,哈希表整个的定义如右图所示:
Hash节点的next指针用来链接同一个hash值的元素(链地址法解决冲突,添加到表头O(1))
下图是整个字典的实现,其中type用于指示键值对的类型,是为了创建多态字典,Redis为用途不同的字典配置不同的类型函数,具体如右图所示:
Redis采用MurmurHash2算法来计算hash值
Rehash操作
- 为hash[1]表分配空间:
- 如果执行扩展操作,则分配为超过hash[0].used*2的最小2^n;
- 如果执行收缩操作,则分配为超过hash[0].used的最小2^n;
- 将hash[0]上所有元素rehash到hash[1]上
- 释放hash[0],将hash[1]置为hash[0],在hash[1]上新建一个空白表
那么到底啥时候进行Rehash扩容呢?
- 服务器目前没在BGSAVE 或则BVGEWRITEAOF,且hash表的负载因子>=1;
- 服务器目前在BGSAVE 或则BVGEWRITEAOF,且hash表的负载因子>=5;
- 缩容是负载因子<0.1时
渐进式Hash
在字典中维护一个rehashIdx属性,在rehash期间,每次进行更新、删除、查找、添加时,除了进行原有操作外,额外还将hash[0]中的rehashIdx对应的项rehash到hash[1]中去,然后将rehashIdx+1;直到所有都移动完毕,rehashIdx恢复-1。
注意,在rehash期间,所有的添加操作都在hash[1]上进行,查找、更新、删除则要从两个表都进行。