Redis 渐进式 rehash 是什么?扩容时为什么还能继续处理请求?
下面是一段教学用的模拟面试。
🧑💻 面试官:哈希表扩容,要把所有键搬到新表。Redis 怎么避免一次搬完?
🙋♂️ 我:它会开一个后台线程迁移,所以主线程不会受影响。
🧑💻 面试官:渐进式 rehash 一定由独立线程完成吗?迁移中查一个键,要查哪张表?
🙋♂️ 我:新旧表都要兼顾。
🧑💻 面试官:新键放哪里?如果一个桶里有很多键,单次迁移能保证毫无延迟吗?
渐进式的重点是把迁移工作拆开,不是把工作变没,也不是自动搬到另一条线程。
面试速答(60 秒版)
Redis 字典进行渐进式 rehash 时,会暂时维护新旧两张哈希表,把旧表的键分批迁移到新表。
迁移期间,新插入通常进入新表,查找和删除则需要按迁移状态兼顾两张表,保证尚未搬走的键仍然可用。
字典操作和受时间预算约束的周期工作可以推动迁移,不必一次处理全部数据。迁移完成后,释放旧表并让新表接替。
这样能分摊长时间阻塞,但不是零成本:迁移期多一张表,占用内存和 CPU,单个桶的工作量也可能不同。具体触发条件与实现细节应按 Redis 版本核对。

知识点详解:一边使用字典,一边把旧表搬空
为什么扩容不能只增加几个空桶?
键所在的桶与表大小有关。表的桶数量改变后,很多键应当位于新的位置,不是把新桶追加上去就结束。
假设旧表有四个桶,新表有八个桶。某个键原来落在旧桶 1,重新计算后可能进入新桶 5。新桶位置要按新的规则计算,不能简单照搬旧桶编号。
这里讨论 Redis 的字典哈希表,不是在说每一种 Redis 数据结构都采用同样的扩容过程。
新旧两张表怎样共同工作?
迁移开始时,旧表仍保存原来的键,新表准备接收迁移数据。Redis 记录迁移进度,逐步搬空旧表。
假设键 A 已经迁移,键 B 还在旧表,新键 C 刚插入。A 在新表,B 在旧表,C 通常进入新表。
查询 B 时,不能只看新表;修改或删除也必须找到键实际所在的位置。因为这些操作与迁移状态配合,用户不需要等所有键搬完才能继续访问。
当旧表已经没有需要迁移的键,新表接替旧表的位置,原旧表资源才能释放。
谁在推动迁移?
按本次核对的 Redis 8.2 字典源码,常见字典操作会帮助执行迁移步骤,源码也提供受时间预算约束的迁移入口。Redis 8.2 dict.c
这说明“渐进式”描述的是工作如何分摊,而不是单凭名字就能推断它发生在后台线程。
迁移的具体策略、暂停条件与周期调用还涉及更外层实现。不应把某个版本中的固定步数或触发阈值,当作所有版本永远不变的规则。
一步迁移,为什么不一定只搬一个键?
桶中可能保存一串冲突键。按桶推进迁移时,一次步骤可能处理该桶中的多个键;空桶扫描同样需要限制工作量。
因此可以说避免一次搬完整个表,但不能承诺“每条请求都只增加一个固定纳秒成本”,也不能保证扩容期间完全没有尾延迟变化。
新旧表并存还会增加一段时间的内存占用。渐进式换来了较平滑的工作安排,却没有免除扩容本身的资源成本。
如何观察,而不是只背搬家过程?
在可控环境里准备逐渐增长的数据,记录内存、CPU、吞吐与延迟分布。关注高分位延迟,而不是只看平均值。
同时区分字典扩容、淘汰、持久化和大键操作等其他因素。一次延迟升高恰好出现在内存增长期间,不足以证明原因只有 rehash。
面试官继续追问
渐进式 rehash 和一致性哈希是一回事吗?
不是。这里是在单个字典的新旧表之间迁移键,一致性哈希通常讨论键如何映射到不同节点或分片。
新旧表各保存一份完整数据吗?
不是。迁移中的键分布在两表中,已经搬走的旧位置会被清理。不要理解成整份数据长期双写备份。
为什么不直接一次搬完?
小表一次迁移可能成本不高,但大表集中迁移会占用较长执行时间。分批的价值主要在于控制单次工作对响应的影响。
面试速记卡
- 渐进式:分批迁移,不等于独立线程或零成本。
- 迁移状态:新旧表并存,操作必须兼顾键所在位置。
- 新键:通常进入新表,完成后由新表接替。
- 工作粒度:一个桶可能包含多个键。
- 工程观察:内存、CPU 与尾延迟,具体细节按版本核对。
公司面试真题
真题根据求职者公开面经整理,题意经过概括,非逐字原话或公司官方题库;本文为 Sunday 的独立解析。
美团 · Java后端 · 社招
Redis 字典怎样处理哈希冲突与 rehash?(题意整理)
社招一年半面经分享 · 美团部分 ↗
历史面经,面试年份未明确;页面编辑于 2024-07-19