前言面试
Redis 字典的遍历过程逻辑比较复杂,互联网上对这一块的分析讲解很是少。我也花了很多时间对源码的细节进行了整理,将我我的对字典遍历逻辑的理解呈现给各位读者。也许读者们对字典的遍历过程有比我更好的理解,还请不吝指教。数组
一边遍历一边修改安全
咱们知道 Redis 对象树的主干是一个字典,若是对象不少,这个主干字典也会很大。当咱们使用 keys 命令搜寻指定模式的 key 时,它会遍历整个主干字典。值得注意的是,在遍历的过程当中,若是知足模式匹配条件的 key 被找到了,还须要判断 key 指向的对象是否已通过期。若是过时了就须要从主干字典中将该 key 删除。架构
那么,你是否想到了其中的困难之处,在遍历字典的时候还须要修改字典,会不会出现指针安全问题?函数
重复遍历学习
字典在扩容的时候要进行渐进式迁移,会存在新旧两个 hashtable。遍历须要对这两个 hashtable 依次进行,先遍历完旧的 hashtable,再继续遍历新的 hashtable。若是在遍历的过程当中进行了 rehashStep,将已经遍历过的旧的 hashtable 的元素迁移到了新的 hashtable中,那么遍历会不会出现元素的重复?这也是遍历须要考虑的疑难之处,下面咱们来看看 Redis 是如何解决这个问题的。指针
迭代器的结构对象
Redis 为字典的遍历提供了 2 种迭代器,一种是安全迭代器,另外一种是不安全迭代器。blog
迭代器的「安全」指的是在遍历过程当中能够对字典进行查找和修改,不用感到担忧,由于查找和修改会触发过时判断,会删除内部元素。「安全」的另外一层意思是迭代过程当中不会出现元素重复,为了保证不重复,就会禁止 rehashStep。开发
而「不安全」的迭代器是指遍历过程当中字典是只读的,你不能够修改,你只能调用 dictNext 对字典进行持续遍历,不得调用任何可能触发过时判断的函数。不过好处是不影响 rehash,代价就是遍历的元素可能会出现重复。
安全迭代器在刚开始遍历时,会给字典打上一个标记,有了这个标记,rehashStep 就不会执行,遍历时元素就不会出现重复。
迭代过程
安全的迭代器在遍历过程当中容许删除元素,意味着字典第一维数组下面挂接的链表中的元素可能会被摘走,元素的 next 指针就会发生变更,这是否会影响迭代过程呢?下面咱们仔细研究一下迭代函数的代码逻辑。
值得注意的是在字典扩容时进行rehash,将旧数组中的链表迁移到新的数组中。某个具体槽位下的链表只可能会迁移到新数组的两个槽位中。
迭代器的选择
除了keys指令使用了安全迭代器,由于结果不容许重复。那还有其它的地方使用了安全迭代器么,什么状况下遍历适合使用非安全迭代器呢?
简单一点说,那就是若是遍历过程当中不容许出现重复,那就使用SafeIterator,好比下面的两种状况
bgaofrewrite须要遍历全部对象转换称操做指令进行持久化,绝对不容许出现重复
bgsave也须要遍历全部对象来持久化,一样不容许出现重复
若是遍历过程当中须要处理元素过时,须要对字典进行修改,那也必须使用SafeIterator,由于非安全的迭代器是只读的。
其它状况下,也就是容许遍历过程当中出现个别元素重复,不须要对字典进行结构性修改的状况下一概使用非安全迭代器。
思考
请继续思考rehash对非安全遍历过程的影响,会重复哪些元素,重复的元素会很是多么仍是只是少许重复?
欢迎工做一到五年的Java工程师朋友们加入Java架构开发:744677563
本群提供免费的学习指导 架构资料 以及免费的解答
不懂得问题均可以在本群提出来 以后还会有职业生涯规划以及面试指导