一.SDS
以SDS(simple dynamic string)类型作为redis的字符串表示。
redis的key和value对象都是SDS简单动态字符串结构
- len和free
SDS结构包含了len属性表示字符串长度,而不用遍历检查’\0’获取长度
SDS结构拼接时会检查空间是否足够,不足时做sdscat扩展空间,再完成拼接。
SDS用空间预分配减少内存分配次数,当len小于1M时,预分配与len相等的free空间。当len大于等于1M时,预分配1M的的free空间。
SDS字符串缩短时,并未释放空间,而是记入free。 - buf
SDS的buf保存的不是字符,而是一系列二进制数据。以len识别长度,中间可以有空字符,是二进制安全的。
SDS的buf以’\0’结尾,可以兼容部分c函数
二.链表
三.哈希
- 哈希算法
hash=dict->type->hashFunction(key);
index=hash&dict->ht[x].sizemask;
redis使用MurmurHash2算法来计算键的哈希值
- 键冲突
redis的哈希表使用链地址法来解决键冲突。新节点加在表头。
- rehash
服务器没有执行BGSAVE时,并哈希表负载因子大于等于1时,哈希表扩展。
服务器在执行BGSAVE时,并哈希表负载因子大于等于5时,哈希表扩展。
哈希表负载因子小于0.1时,哈希表收缩。
rehash对ht[1]分配空间,根据和ht[0]中used数量操作。扩展时取大于等于used*2的最小2^n。
缩小时取大于等于used的最小2^n。
然后将ht[0]中的键值对rehash到ht[1]上。
最后释放ht[0],将ht[1]设置为ht[0],在ht[1]上新建空白哈希表。 - 渐进rehash
rehash时,若键值对数量巨大,需要渐进式rehash,分多次从ht[0]rehash到ht[1]。在hash表中维护一个变量rehashidx,设置为0时,rehash工作开始。把索引为0的键值对rehash到ht[1],完成后rehashidx增一。所有都完成后rehashidx置为-1。
rehash期间,查找首先在ht[0]中,没有的话在ht[1]中查找,新添加的键值对会保存到ht[1]中。
四.跳跃表
跳跃表效率媲美平衡树,但跳表实现简单,可以代替平衡树。
五.整数集合
整数集合类型可以升级,类型有int16_t,int32_t,int64_t,不能出现重复元素。
contents数组声明为int8_t,但实际类型取决于encoding。
INTSET_ENC_INT16
INTSET_ENC_INT32
INTSET_ENC_INT64
- 类型升级
当添加的新元素类型更长时,整数集合升级。
首先扩展底层数组的大小,为新元素分配空间。
然后将底层元素类型转换,将元素放在正确位置,保持有序性。
最后添加元素到数组。
新元素长度小于所有元素,放在数组首部。
新元素长度大于所有元素,放在数组末尾。
升级策略提升整数集合的灵活性,随意添加各种类型的值。
还可以尽量节省内存。
不支持降级。
六.压缩列表
压缩列表目的是节约内存,是一系列特殊编码的连续内存块组成的顺序型结构。
- 压缩列表节点
previous_entry_length记录前一个节点的长度。如果前一节点长度小于254字节,previous_entry_length长度为1字节。大于等于254字节,previous_entry_length长度为5字节,第一字节为0xFE,后四字节保存前一字节长度。
用表尾指针依次减去前一节点长度,可以表尾向表头遍历。