redis字典结构哈希冲突怎么处理?

55 阅读3分钟

1. 哈希冲突解决方式

Redis 字典(Dict)通过 链地址法(Separate Chaining) 解决哈希冲突:当多个键计算出相同的哈希值时,会将这些键值对以 单向链表 的形式存储在同一哈希桶中,后续查找时会遍历该链表匹配目标键。

2. 哈希负载因子(Load Factor)

定义:负载因子是衡量哈希表满度的指标,公式为 负载因子 = 字典已保存的键值对数量(size) / 哈希表大小(capacity)。

Redis 中的作用:用于触发哈希表的 扩容(rehash) 或 缩容,以平衡查询性能和内存占用:

  • 扩容触发:默认负载因子阈值为 1,当负载因子超过 1 时,Redis 会创建新哈希表(容量为原容量的 2 倍,且为 2 的幂),并逐步将原表数据迁移到新表(渐进式 rehash)。

  • 缩容触发:当字典键值对大量删除后,若负载因子低于 0.1,Redis 会创建新哈希表(容量为大于当前 size 的最小 2 的幂),同样通过渐进式 rehash 迁移数据。

3. 具体的rehash过程:

Redis 哈希表的扩容和缩容核心是 渐进式 rehash(重哈希),通过分步骤迁移数据避免单步操作阻塞主线程,具体流程如下:

1. 扩容操作(Expansion)

扩容的触发条件是 负载因子 > 1(默认阈值),步骤如下:

1.创建新哈希表:新表容量为原表容量的 2 倍,且确保是 2 的幂(便于后续哈希计算)。

2.开启渐进式 rehash:

字典中维护一个 rehashidx 索引,初始值设为 0,标记当前正在迁移的哈希桶。

后续每次对字典执行 增删改查 操作时,除了处理当前请求,还会顺带将 rehashidx 指向的原表哈希桶中所有键值对,迁移到新表,然后 rehashidx 加 1。

3.迁移完成:当 rehashidx 等于原表容量时,说明所有数据迁移完毕,此时释放原表内存,将新表设为字典的主哈希表,rehashidx 重置为 -1(表示未进行 rehash)。

2. 缩容操作(Shrinkage)

缩容的触发条件是 负载因子 < 0.1(键值对大量删除后),步骤与扩容类似,核心差异在新表容量:

1.创建新哈希表:新表容量为 大于当前字典键值对数量(size)的最小 2 的幂(避免容量过大导致空间浪费)。

2.渐进式 rehash 迁移:与扩容逻辑一致,通过 rehashidx 分步骤迁移原表数据到新表,迁移期间所有操作会同时作用于新旧两个表(查询先查新表再查旧表,写入只写新表)。

3.完成缩容:迁移结束后,释放原表内存,新表成为主哈希表,rehashidx 重置为 -1

关键特点:渐进式 rehash 的优势

  • 非阻塞:避免一次性迁移大量数据导致 Redis 主线程阻塞,保证服务响应性。

  • 双表并行:rehash 期间,字典同时持有新旧两个哈希表,确保数据读写不中断(写入只写新表,查询/删除会同时检查两个表)。