Redis 渐进式 rehash(重哈希)的核心是 分批次、穿插在日常操作中迁移数据,通过维护关键状态变量和双表并行机制,实现非阻塞的数据迁移,具体实现逻辑如下:
1. 核心状态变量
Redis 字典(dict 结构)中维护 3 个关键变量,用于控制 rehash 过程:
-
rehashidx:标记当前 rehash 进度的索引,初始值为 -1(表示未进行 rehash);开始 rehash 后设为 0,每迁移完一个哈希桶就加 1,直到等于原表容量(迁移完成)。
-
ht[0]:原哈希表(旧表),rehash 前存储所有数据,迁移过程中逐步清空。
-
ht[1]:新哈希表(新表),rehash 开始时创建,容量根据扩容/缩容规则确定,用于接收迁移的数据。
2. 具体实现步骤
步骤 1:初始化新表与 rehash 状态
当负载因子触发扩容(>1)或缩容(<0.1)时:
1.根据规则计算 ht[1] 的容量(扩容为 ht[0] 的 2 倍,缩容为大于当前 size 的最小 2 的幂)。
2.为 ht[1] 分配内存,初始化所有哈希桶为空。
3.将字典的 rehashidx 设为 0,标志 rehash 正式开始。
步骤 2:分批次迁移数据(核心逻辑)
rehash 不一次性迁移所有数据,而是 穿插在字典的每一次增删改查操作中,每次迁移 1 个哈希桶(ht[0] 中 rehashidx 指向的桶),具体逻辑:
1.当客户端对字典执行 查询、删除、更新 操作时:
先处理当前操作(如查询时,先查 ht[1] 再查 ht[0],确保数据不遗漏)。
检查 rehashidx 是否不为 -1(即正在 rehash),若是则执行 一次迁移:将 ht[0] 中 rehashidx 指向的哈希桶内所有键值对,重新计算哈希值后迁移到 ht[1] 对应的桶中。
迁移完成后,rehashidx 加 1,推进迁移进度。
2.额外「主动迁移」:为避免 rehash 长期停滞(如字典长时间无操作),Redis 还会在 定时任务(serverCron) 中,批量迁移多个哈希桶(默认一次迁移 100 个桶),加速进度。
步骤 3:读写操作适配双表
rehash 期间,ht[0] 和 ht[1] 同时存在,所有操作需适配双表,确保数据正确性:
写入操作(新增键值对):只写入 ht[1],不写入 ht[0],避免 ht[0] 持续产生新数据,导致迁移无法结束。
查询/删除/更新操作:先在 ht[1] 中查找,若未找到再去 ht[0] 中查找,确保能访问到所有数据。
步骤 4:rehash 完成收尾
当 rehashidx 的值等于 ht[0] 的容量时,说明 ht[0] 所有哈希桶已迁移完毕:
1.释放 ht[0] 的内存空间。
2.将 ht[1] 赋值给 ht[0](新表成为主表)。
3.清空 ht[1](重置为空表,供下次 rehash 使用)。
4.将 rehashidx 设回 -1,标志 rehash 结束。
核心优势
-
非阻塞:避免一次性迁移大量数据导致主线程阻塞,保障 Redis 高并发响应能力。
-
低开销:迁移操作分散在日常请求中,单次迁移成本极低,对服务性能影响可忽略。