Redis设计与实现(一~五整合版)(2)

这个虽然说经常用,但是对于redis来说确实是重中之重。毕竟redis就是一个key-value的数据库,而key被称为键空间(key space),这个键空间就是由字典实现的。第二个用途就是用作hash类型的其中一种底层实现。下面分别来说明。

键空间:redis是一个键值对数据库,数据库中的键值对就由字典保存:每个数据库都有一个与之相对应的字典,这个字典被称为键空间。当用户添加一个键值对到数据库(不论键值对是什么类型),程序就讲该键值对添加到键空间,删除同理。

用作hash类型键的其中一种底层实现:hash底层实现是通过压缩列表和字典实现的,当建立一个hash结构的时候,会优先使用空间占用率小的压缩列表。当有需要的时候会将压缩列表转化为字典

对于字典的实现这里简单说明一下即可,因为很简单。

字典是通过hash表实现的。每个字典含有2个hash表,分别为ht[0]和ht[1],一般情况下使用的是ht[0],ht[1]只有在rehash的时候才用到。为什么呢?因为性能,我们知道,当hash表出现太多碰撞的话,查找会由O(1)增加到O(MAXLEN),redis为了性能,会在碰撞过多的情况下发生rehash,rehash就是扩大hash表的大小,从而将碰撞率降低,当hash表大小和节点数量维持在1:1时候性能最优,就是O(1)。另外的rehashidx字段也比较有看头,redis支持渐进式hash,下面会讲到原理。

下面讲一下rehash的触发条件:

当新插入一个键值对的时候,根据used/size得到一个比例,如果这个比例超过阈值,就自动触发rehash过程。rehash分为两种:

自然rehash:满足used/size >= 1 && dict_can_resize条件触发的

强制rehash:满足used/size >= dict_force_resize_ratio(默认为5)条件触发的。

思考一下,为什么需要两种rehash呢?答案还是为了性能,不过这点考虑的是redis服务的整体性能。当redis使用后台子进程对字典进行rehash的时候,为了最大化利用系统的copy on write机制,子进程会暂时将自然rehash关闭,这就是dict_can_resize的作用。当持久化任务完成后,将dict_can_resize设为true,就可以继续进行自然rehash;但是考虑另外一种情况,当现有字典的碰撞率太高了,size是指针数组的大小,used是hash表节点数量,那么就必须马上进行rehash防止再插入的值继续碰撞,这将浪费很长时间。所以超过dict_force_resize_ratio后,无论在进行什么操作,都必须进行rehash。

rehash过程很简单,分为3步:

给ht[1]分配至少2倍于ht[0]的空间

将ht[0]数据迁移到ht[1]

清空ht[0], 将ht[0]指针指向ht[1],ht[1]指针指向ht[0]

同样是为了性能(当用户对一个很大的字典插入时候,你不能让系统阻塞来完成整个字典的rehash。所以redis采用了渐进式rehash。说白了就是分步进行rehash。具体由下面2个函数完成:

dictRehashStep:从名字可以看出,是按照step进行的。当字典处于rehash状态(dict的rehashidx不为-1),用户进行增删查改的时候会触发dictRehashStep,这个函数就是将第一个索引不为空的全部节点迁移到ht[1],因为一般情况下节点数目不会超过5(超过基本会触发强制rehash),所以成本很低,不会影响到响应时间。

dictRehashMilliseconds:这个相当于时间片轮转rehash,定时进行redis cron job来进行rehash。会在指定时间内对dict的rehashidx不为-1的字典进行rehash

上面讲完了rehash过程,但是以前在组内分享redis的时候遇到过一个问题:

当进行rehash时候,我进行了增删查改怎么办?是在ht[0]进行还是在ht[1]进行呢?

redis采用的策略是rehash过程中ht[0]只减不增,所以增加肯定是ht[1],查找、修改、删除则会同时在ht[0]和ht[1]进行。

Tips: redis为了减少存储空间,rehash还有一个特性是缩减空间,当多次进行删除操作后,如果used/size的比例小于一个阈值(现在是10%),那么就会触发缩减空间rehash,过程和增加空间类似,不详述了。

3. 跳跃表

跳跃表是一种随机化数据结构(它的层是随机产生的),查找、添加、删除操作都是O(logN)级别的。

跳跃表目前在redis的唯一用处就是有序集类型的底层数据结构之一(另外一个还是字典)

当然,根据redis的特性,作者对跳跃表进行了修改

socre可以重复

对比一个元素需要同时检查它的score和member

每个节点带有高度为1的后退指针,用于从表尾方向向表头方向迭代

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/6be4ac5bc3f297e5261a68427fc4282e.html