一:概述
- Redis 源码版本为3.0.0.
- 字典在 Redis 中应用广泛,数据库/哈希表一种方式 底层使用字典实现。
二:字典的基础结构
- 概述
- Redis 字典使用哈希表作为底层实现。
- 一个哈希表中会有多个哈希节点,而每个哈希节点保存了一个键值对。
- 基本结构
-
- 哈希表节点(dict.h/dictEntry)
- dictEntry(哈希表节点) 保存了具体的键值对。
- next 用于解决hash冲突。
-
- 哈希表(dict.h/dictht)
- 用于维护整理哈希表节点。
-
- 字典(dict.h/dict)
- 整个字典的基础外围结构。
- ht 是一个包含两个dictht 的哈希表。一般情况下,只使用dictht[0],但是在 rehash 中,会使用 dictht[1].
-
三:键冲突解决
- 当发生键冲突时候,会在 dictEntry(哈希表节点) next 构成单向链表,解决键冲突。
- 为了速度考虑,新加入的键总是在链表头位置。
-
四:rehash(重新散列)
- 原因
- 随着哈希表的不断进行,哈希表的键值会改变。
- 为了让哈希表维持在一个合适范围,需要对哈希表大小进行相应的扩展或收缩。
- 原理
- 分配 ht[1] 空间
- 如果是扩展操作,分配空间为 ht[0].used * 2 (必须是 2 的 n 次方)
- 如果是收缩操作,收缩空间为 ht[0].used (必须是 2 的 n 次方)
- 将 ht[1] 的键值放入 ht[0] 中,同时释放 ht[1] 。
- 将 ht[1] 置为 ht[0],rehash 完成。
- 缺点
- 在 rehash 中,字典表在转移过程中(键值过多时候),导致不可用。
五:渐进式 rehash
- 概述
- 解决了直接 rehash 中键值过多导致的字典不可用问题。
- 原理
- ht[1] 哈希表分配空间,使得字典同时存在 ht[0], ht[1] 两个哈希表。
- 在字典中维持一个变量 rehashindex, 当 对该字典进行增删改查时,操作 ht[0] 并转移到 ht[1] 中,并 rehashindex + 1
- 直到所有 ht[0] 中元素都转移到 ht[1],释放 ht[0]
- 问题
- 在 ht[0] 和 ht[1] 同时存在时候,查找键值会先去 ht[0] 再去 ht[1] 找。
六:字典表扩展收缩的条件
- 负载因子 = ht[0].used(已保存节点数量) / ht[0].size(哈希表大小)
- 服务器没有进行RDB操作,并负载因子 >= 1
- 服务器进行RDB操作,并负载因子 >= 5
- 负载因子 <= 0.1, 收缩操作