Redis/Dictionary

将哈希表迁移分散到多次操作

Redis 7.2.0 的字典扩缩容保留新旧两张哈希表,逐桶迁移,降低一次性搬迁全部条目的停顿。

7.2.0索引与查找核验 2026-10-08

工程问题#

大字典重新分配桶后,若立刻迁移所有条目,会把整段搬迁成本集中到一次操作。

实际实现#

rehashidx 记录迁移位置;常规查找和更新可推进迁移,迁移期间按需查询两张表,最终释放旧桶数组。

处理流程#

  1. 分配新桶数组并进入迁移状态。
  2. 普通字典操作触发一个迁移步骤。
  3. 把选中桶的整条冲突链移入新表。
  4. 旧表清空后完成表切换。

源码片段#

固定版本 · 原文摘录
src/dict.c7.2.0 · L668–L688
查看原文件
668dictEntry *dictFind(dict *d, const void *key)669{670    dictEntry *he;671    uint64_t h, idx, table;672673    if (dictSize(d) == 0) return NULL; /* dict is empty */674    if (dictIsRehashing(d)) _dictRehashStep(d);675    h = dictHashKey(d, key);676    for (table = 0; table <= 1; table++) {677        idx = h & DICTHT_SIZE_MASK(d->ht_size_exp[table]);678        he = d->ht_table[table][idx];679        while(he) {680            void *he_key = dictGetKey(he);681            if (key == he_key || dictCompareKeys(d, key, he_key))682                return he;683            he = dictGetNext(he);684        }685        if (!dictIsRehashing(d)) return NULL;686    }687    return NULL;688}
dictFind

普通查找先尝试推进一次 rehash,然后按需查询旧表和新表;不在迁移状态时只查第一张表。_dictRehashStep 在迁移未暂停时调用 dictRehash(d,1),其计量单位是桶,桶内可能有多个条目。

完整 dictFind 函数,展示渐进迁移如何接入在线查询。搬迁桶的循环、迁移暂停和最后释放旧表在同文件其它函数。

收益与取舍#

工程分析

带来的收益

  • 把大量搬迁工作摊到多次操作。
  • 迁移期间仍可查找和更新条目。

付出的代价

  • 过渡期同时持有两套桶数组。
  • 一步迁移一个桶,长冲突链仍可能增加单次耗时。

可比较的选择

一次性 rehash状态管理更简单,但迁移工作集中,容易产生延迟尖峰。

版本与适用范围#

每步按桶而非单个键计量,且限制空桶扫描;迁移可被暂停,不能把渐进迁移描述成后台线程或硬实时延迟保证。

参考来源#

源码、官方文档与原始论文
01
Redis 7.2.0 渐进迁移与双表查找

src/dict.c · dictRehash; _dictRehashStep; dictFind; dictRehashMilliseconds

源码7.2.0

相关实现与比较#

4 个相关案例
QEMU·TCG 翻译块v9.2.0

QEMU TCG 用 QHT 保存 TranslationBlock 索引,帮助复用与当前客体执行状态一致的翻译结果。

附源码片段 · 缓存与复用
6 个匹配案例