Redis本身就是一个大字典,当我们使用keys命令查找这个大字典时,它会遍历整个redis,如果找到了符合匹配条件的key,还需要判断key指向的对象是否已经过期,如果过期了就需要进行删除操作。
字典拓容时需要进行渐进式hash,此时存在新旧两个hashtable,需要先遍历old hashtable,然后遍历new hashtable,如果遍历过程中进行了rehash,旧的hashtable迁移到新的hashtable中,遍历可能会产生重复,某些场景下需要避免遍历重复,redis使用安全迭代器解决了这个问题。
redis提供两种迭代器,安全与非安全迭代器,安全指的是遍历过程中可以对字典进行查找和修改,这个过程中还会触发过期删除,同时,安全迭代器可以禁止发生rehash。
不安全迭代器则是,遍历过程中,字典是只读的,不可修改,只能调用 dictNext 对字典持续遍历,不得调用过期删除的函数,好处是不影响rehash,坏处则是可能会出现遍历重复。
安全迭代器在遍历开始时,会给字典加上标记,该标记意味着rehash不会发生。安全迭代器在遍历过程中允许删除元素,这意味着一维数组下的链表中的元素可能会被摘走,元素的next指针就会发生变动。字典拓容时的rehash,将链表迁移到新数组中的时候,某个具体槽位的链表只可能迁移到新数组的两个槽位中。
如果遍历过程中不允许发生重复,需要处理元素过期,也需要对字典进行修改,就选择安全迭代器,否则就选择非安全迭代器。
选择安全迭代器的情况:
keys指令,需要遍历所有对象,不允许重复
bgaofrewrite,需要遍历所有对象持久化,不允许重复
bgsave也需要遍历所有对象,不允许重复
【参考】
《Redis深度历险 核心原理与应用》