本页目录

动态再平衡

集群扩容或节点故障时,数据需要从旧分布迁移到新分布。好的再平衡只迁移必要的最少数据,且不中断读写——一致性哈希的虚拟节点让这件事变得去中心化。

概述

一致性哈希分片策略解决了"数据怎么分布到节点"。但当集群扩容(加节点)或缩容(节点故障/下线)时,数据需要从旧分布迁移到新分布⁠——这是再平衡(rebalancing)。一个糟糕的再平衡方案可能迁移远超必要的数据量,甚至在迁移期间拒绝服务。好的再平衡只迁移必要的最少数据⁠,且不中断读写⁠。

三种再平衡策略

固定分区:简单但粗糙

预创建远超节点数的分区(如每个节点 100 个分区,10 个节点共 1000 个分区),分区数不变。加节点时,从每个旧节点匀一些分区给新节点。

加节点前: Node-1: [p1..p100],  Node-2: [p101..p200], ... Node-10: [p901..p1000]
加节点后: Node-1: [p1..p90],   Node-2: [p91..p180],  ... Node-11(新): [p901..p990]
  • 迁移粒度:整个分区(N 个 key 一起搬),不是逐 key。
  • 缺点:⁠分区大小和节点数强绑定⁠——如果初始估算偏差(分区太大或太小),后续难调;分区数是静态的,不能随着数据量增长而分裂。

Redis Cluster 使用这个方案:16384 个固定 slot。

动态分区:分区随数据增长分裂

每个分区设一个大小上限(如 10GB),超过就分裂成两个新分区。节点数变化时,分区整体迁移到新节点——和固定分区类似,但分区数本身是动态的。

分区 p3 到达 10GB → 分裂成 p3a 和 p3b (各 ~5GB)
需要迁移时 → 整体搬 p3a 到新节点
  • 优势:分区数和总数据量成正比,不会出现固定分区那种"预创建太多或太少"的问题。
  • 代价:需要集中式协调者追踪分区元数据(哪个分区在哪个节点、哪个正在分裂)。HBase 用这个方案。

一致性哈希 + 虚拟节点:去中心化再平衡

一致性哈希里每个物理节点对应多个虚拟节点(vnode,如每节点 ~100 个),分布在整个哈希环上。加节点时,新节点插入环,带走它 vnode 覆盖的 token range(从相邻节点的 vnode 匀过来);删节点时,它的 vnode 对应的 range 被邻近 vnode 接管。

加节点 N11(带 vnode v1101..v1200) 到环:
  原来 token [5000, 5200] 在 N3 的 v303 上
  现在 v1101 覆盖 [5000, 5200] → 这个 range 从 N3 迁移到 N11
  只迁移 token 在新节点范围内的 key, 其他 key 不动
  • 最小迁移量⁠:只迁移落在新 vnode 范围内的 key,不是整个分区。
  • 去中心化⁠:每个节点通过 Gossip 协议 知道全局路由表,自己算自己该接管哪些 range,不依赖协调者。
  • 代价:迁移粒度是单个 vnode 覆盖的 range,可能造成热点(某个 vnode 的 range 数据特别多)——解决办法是动态调整 vnode 数按负载分裂 vnode(Cassandra 的 virtual node splitting)。

迁移工程:怎么在不中断的情况下搬数据

无论哪种策略,实际搬数据的过程有一些通用实践:

双写过渡

迁移期间,新旧两个节点同时接受对该 range 的写⁠:

1. 元数据更新: range [5000, 5200] 从 N3 迁移到 N11
2. 过渡期: 读写仍然发给 N3, N3 把新写的数据流式传(N11)
3. N11 收到完成信号 → 元数据更新: [5000, 5200] 现归 N11
4. 后续读写直接发 N11

双写保证迁移期间不丢写:即使新节点还没完全就绪,旧节点仍在服务。Cassandra 的 nodetool move / decommission 就是这套。

限流与优先级

数据迁移吃网络和 IO——在业务高峰期全速迁移会拖垮正常请求:

  • 限流⁠:限制迁移带宽(如 Cassandra 的 stream_throughput_outbound_megabits_per_sec)。
  • 低谷迁移⁠:手动触发,在低峰执行。
  • 增量再同步⁠:先传全量 snapshot,再传迁移期间的新写(类似 MySQL 的 binlog 追赶)。

一致性的坑

迁移期间,一条数据的 N 个副本可能跨越新旧分布⁠——部分副本按旧路由,部分已按新路由。解法是先迁移数据,再切换路由元数据⁠(原子切换,不是逐个副本切换)。或像 Cassandra 那样,读的时候去新旧两个位置都查一下(过渡期间有短暂的双路由)。

权衡与失败模式

  • 全量重分布⁠:加一个节点就把所有 key 重新 hash → 迁移量 = 全量数据/N,大量无谓迁移。永远用一致性哈希或固定分区 + 搬分区。
  • 热点 vnode:某个 vnode 覆盖的 range 数据特别多,迁移时成为瓶颈 → 监控 vnode 大小,动态分裂。
  • 迁移期间数据丢失⁠:先切路由再搬数据 → 始终先搬数据,再原子切路由。
  • 迁移风暴⁠:一次性加大量节点,同时迁移可能压垮集群 → 逐个节点加,每次等再平衡完成再加下一个(Cassandra 推荐)。

参考

  • 论文⁠: "Dynamo"(一致性哈希再平衡, 2007)

Keywords: rebalancing, 固定分区, 动态分区, hash ring, virtual node, vnode, 数据迁移, 最小迁移量, 双写过渡, 渐进迁移, 限流, atomic routing switch, hotspot vnode