redis:底层数据结构与对象(一)

简单动态字符串 SDS

redis中使用SDS字符串结构,而不是c语言的字符串结构。

image.png SDS结构如上图,SDS遵循c语言中空字符结尾的惯例,这样可以复用c语言的字符串函数,空字符串不算在len中。

相比于c语言中的字符串,SDS的优点是什么呢?

1、获取字符串长度效率更高
c语言中必须遍历字符的个数,而SDS使用len字段记录了字符串长度。
2、杜绝缓冲区溢出 image.png 举个例子,想在redis后面再拼接一个字符串,c语言如果没有事先在s1分配足够的内存,就会溢出到s2中导致s2的数据被意外改变。而SDS中有free字段记录数组中未使用的字节数量,在对SDS进行修改时可以通过free字段判断空间是否足够,所以SDS中完全不会出现缓冲区溢出的问题。
3、减少修改字符串带来的内存重分配次数
c语言中没有记录字符串的长度,所以底层永远是n+1长度的数组,每次字符串新增或减少一位,那么都需要进行内存重分配。
3.1空间的预分配
当SDS进行空间扩展的时候,不仅分配必须的空间,还会分配额外未使用的空间。
3.1.1当SDS修改后小于1MB
会分配于len长度一样大的未使用空间,比如修改后是13字节,那么也会分配13字节未使用的空间,整体就是13+13+1=27个字节。
3.1.2当SDS修改后大于等于1MB
会分配1MB的未使用空间,比如修改后是30MB,那么也会分配1MB未使用的空间,整体就是30MB+1MB+1byte

通过预先分配未使用的空间,这样在字符串连续增长时,就不用反复分配内存了。

3.2惰性空间释放
惰性释放用来优化SDS字符缩短操作,当需要缩短字符串时,程序并不立即使用内存重分配回收掉这部分字节,而是free属性记录这部分没使用的字节,等待将来使用。

image.png 如图,并不释放掉这多出来的8字节,将来如果字符串增加就可以用到。
4、二进制安全
c语言中使用\0空字符,来表示字符串的结尾,那么使得它只能保存文本数据,不能保存图片等等二进制数据。而SDS使用len属性来判断字符串结尾,因此可以保存二进制数据。

链表

image.png 和java中的list非常相像,就不赘述了,直接总结一下。

  1. 双端 :链表节点都用前驱节点和后驱节点
  2. 无环 :表头节点和尾节点都是指向NULL
  3. 带有表头节点和表尾节点
  4. 带链表长度计数器

字典

字典一种保存键值对的数据结构

image.png 如上图是一个字典的整体结构,首先是字典,字典里存着2个hash表,hash表里有一个个的键值对的hash节点。

字典

image.png ht:保存着2个hash表,一般情况下只使用ht[0]的hash表,ht[1]会在rehash的时候使用。
rehashidx:记录了rehash的进度,没有进行rehash,值为-1。

Hash表

image.png

Hash表节点

image.png next:指向另一个hash表节点的指针,这样就可以将多个hash值一样的节点连接在一起,形成一个单链表,解决键冲突问题。

Hash算法

当新增一个新的键值对时,需要根据键算出hash值和索引值,然后放在数组对应的索引上。

image.png

解决键冲突

当2个以上的键被分配到同一个索引时就会产生键冲突,通过next指针形成一个单链表解决冲突,由于没有指向链表表尾的指针,为了效率考虑每次都将新节点添加到表头位置。

rehash

当保存的键值对太多或太少时,就要对hash表进行扩展或收缩,通过rehash(重新散列)完成。
rehash步骤如下:

  1. 为ht[1]分配空间
  2. 为ht[0]中的所有键值对计算索引位置,然后放置在ht[1]上。
  3. 所有的键值对都迁移到ht[1]之后,释放掉ht[0],ht[1]设置为ht[0],然后在ht[1]上新增一个空白的hash表,为一次rehash做准备

rehash的时机

  • 服务器没在执行BGSAVE或BGREWRITEAOF,负载因子大于等于1
  • 服务器没在执行BGSAVE或BGREWRITEAOF,负载因子大于等于5
  • 负载因子小于0.1时,进行收缩操作

负载因子的计算公式: 负载因子=已保存节点数量/哈希表大小

渐进式rehash

rehash的操作并不是一次完成的,因为如果存在大量的键值对,一次性迁移到ht[1]时,可能会造成一段时间内停止服务。是会分多次,渐进式的迁移到ht[1]中。

1)为ht[1]分配空间,让字典同时持有ht[0]和ht[1]两个哈希表。
2)在字典中维持一个索引计数器变量rehashidx,并将它的值设置为0,表示rehash工作正式开始。
3)在rehash进行期间,每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作以外,还会顺带将ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1],当rehash工作完成之后,程序将rehashidx属性的值增一
4)随着字典操作的不断执行,最终在某个时间点上,ht[0]的所有键值对都会被rehash至ht[1],这时程序将rehashidx属性的值设为-1,表示rehash操作已完成。

猜你喜欢

转载自juejin.im/post/7036924630057091079