Redis是一个键值对数据库,数据库中的键值对由字典dict保存,每个数据库都有一个对应的字典,这个字典被称之为键空间(key space)。
字典dict涉及的数据类型有:
- dictType
- dictEntry
- dictht
- dict
- dictIterator
- dictScanFunction
从上图我们可以看出dictEntry,dictht,dict之间的关系,字典dict具有两个哈希表dictht(之所以采用两个哈希表,是为了渐进式哈希扩缩容使用),dictEntry对应的就是哈希表中的具体元素,这里采用了开链法来解决哈希冲突。
dictIterator是字典迭代器,有两种模式:安全模式和非安全模式。
dictType通过提供一组自定义函数来处理dict字典中保存的不同类型数据,类似于Java中的泛型机制,提供的方法有:
- hashFunction(*key)
- keyDup(*privdata, *key)
- valDup(*privdata, *obj)
- keyCompare(*privdata, *key1, *key2)
- keyDestructor(*privdata, *key)
- valDestructor(*privdata, *obj)
dictEntry
dictEntry
有三个字段:
- key: 键对应的指针
- v: 值(浮点型、无符号长整型、有符号长整型、指针)
- next: next指针
dictEntry
涉及的宏或方法有:
- dictFreeVal: 调用dictType的valDesctructor()方法释放值空间
- dictSetVal: 调用dictType的valDup()方法设置值
- dictSetSignedIntegerVal: 设置有符号长整型值
- dictSetUnsignedIntegerVal: 设置无符号长整型值
- dictSetDoubleVal: 设置浮点型值
- dictFreeKey: 调用dictType的keyDestructor()方法释放健空间
- dictSetKey: 调用dictType的keyDup()方法设置键
- dictCompareKeys: 两个键比较大小
dictIterator
字典迭代器有两种模式:安全模式和非安全模式
安全模式下,在遍历的过程中我们可以调用dictAdd()
,dictDelete()
等方法;非安全模式下,我们只能使用dictNext()
方法进行遍历。
字典迭代器对应的方法:
- dictFingerprint
- dictGetIterator
- dictGetSafeIterator
- dictNext
- dictReleaseIterator
字典指纹
我们在创建非安全的字典迭代器的时候,会给字典生成一个指纹,这个指纹由dictht.table
, dictht.size
, dictht.used
三个字段进行一系列异或操作生成。
如果在遍历过程中,字典有任何操作导致这三个字段改变的话,字典的指纹也会发生改变。在迭代完成之后,会检查指纹是否一致,如果指纹不一致,说明发生了毁坏性操作,进程会直接退出!
迭代器创建&销毁
直接看代码吧:
遍历过程
整个遍历过程大致如下:
- 初始化工作:iterators++或者保存字典指纹
- 从第一个dictht开始遍历
- 遍历第一个槽位,会依次遍历槽位链上的所有元素
- 遍历剩下的所有槽位
- 如果字典正在进行重哈希,那么继续遍历下一个dictht,否则遍历结束
问题:
dict.iterators
字段的作用?如果iteratos字段大于0,表明字典当前存在安全模式迭代器,它会阻止字典进行重哈希!
问题: 如果在遍历过程中,迭代器的
entry
和nextEntry
都被删除的话,会是什么情况?