动态 Sharding 迁移机制:数据分片在运行中分裂与搬迁的零停机实践
发布时间:2026/9/4 22:07:18
分类:文化教育
浏览:1234

动态 Sharding 迁移机制数据分片在运行中分裂与搬迁的零停机实践在现代分布式存储架构如 TiKV、CockroachDB、ScyllaDB 或各类自研分布式 KV 引擎中弹性水平扩展Elastic Horizontal Scalability是其战胜传统单机数据库的最核心武器。当业务数据从几百 GB 持续膨胀到数百 TB或者某个业务分片因为大促秒杀突然涌入天量写入流量时系统必须具备在不停机、不阻塞业务读写的前提下自动将过载的数据分片Region / Tablet / Shard进行在线物理分裂Split并将其平滑迁移搬迁Rebalance / Migration到低负载节点的能力。这个看似简单的“分片搬迁”操作在底层涉及 Raft 共识日志的原子截断、跨节点物理数据搬移、两阶段配置变更以及全局路由元数据的毫秒级收敛。package sharding import ( context fmt sync ) // Region 分布式连续键值分片区间 type Region struct { ID uint64 StartKey []byte EndKey []byte RegionEpoch RegionEpoch // 纪元版本包含 ConfVer 与 Version Peers []*Peer LeaderID uint64 } type RegionEpoch struct { ConfVer uint64 // 副本成员变更版本号 Version uint64 // 分片分裂/合并版本号 } // SplitRegion 在 Raft 状态机中原子执行的分裂指令 func (r *Region) ApplySplit(splitKey []byte, newRegionID uint64, newPeerIDs []uint64) (*Region, *Region, error) { if bytesCompare(splitKey, r.StartKey) 0 || bytesCompare(splitKey, r.EndKey) 0 { return nil, nil, fmt.Errorf(非法的 SplitKey超出当前分片物理区间) } // 1. 产生左分片 (保留原 RegionID更新 EndKey 与 Version) leftRegion : Region{ ID: r.ID, StartKey: r.StartKey, EndKey: splitKey, RegionEpoch: RegionEpoch{ ConfVer: r.RegionEpoch.ConfVer, Version: r.RegionEpoch.Version 1, }, Peers: r.Peers, } // 2. 产生右分片 (分配全局全新 RegionID继承 StartKey 与原 EndKey) rightRegion : Region{ ID: newRegionID, StartKey: splitKey, EndKey: r.EndKey, RegionEpoch: RegionEpoch{ ConfVer: 1, Version: 1, }, Peers: createPeers(newPeerIDs), } return leftRegion, rightRegion, nil }第一步在线分片分裂Split的原子性保障当一个 Region 的物理体积突破阈值通常为 96MB 或 512MB时调度器会触发分裂流程寻找最佳分割点Split Key存储引擎在底层 LSM-Tree 中扫描数据分布选取位于正中间的 Key或者基于写入流量统计选取读写频次最高的分割点在 Raft 复制组中下发AdminSplit指令分裂绝对不是外部进程强行切文件AdminSplit命令本身被封装为一条标准的 Raft Log Entry走多数派共识提交状态机原子生效当所有 Follower 在本地 Apply 这条日志时在同一个微秒内旧 Region 的键值区间被截断为 $[StartKey, SplitKey)$新 Region $[SplitKey, EndKey)$ 在本地无缝诞生。由于整个过程是在内存元数据中完成指针修改无需任何磁盘物理数据拷贝业务读写耗时抖动小于 1 毫秒[Region 在线分裂与跨节点调度迁移流程] 阶段 1: 单一 Region 膨胀并本地原子分裂 Node A: [ Region 1: Key A ~ Z (200MB) ] │ (Raft Apply AdminSplit Key M) ▼ Node A: [ Region 1: Key A ~ M (100MB) ] [ Region 2: Key M ~ Z (100MB) ] 阶段 2: 跨节点异步调度搬迁 (Region 2 迁移至 Node B) Node A (Leader) ──(1. 添加 Node B 为 Learner)──▶ Node B (异步接收 SST 快照) ──(2. Promote Node B 为 Follower)──▶ Node B (追平 Raft Log) ──(3. 将 Leader 转移至 Node B)──▶ Node B (成为新 Leader) ──(4. 移除 Node A 副本)────────▶ Node A (释放本地存储空间)第二步跨节点物理搬迁Replica Relocation分裂完成后Node A 上同时承载了两个高负载分片。全局调度调度器如 Placement Driver, PD感知到 Node A 的 IO 水位偏高随即下发调度指令将Region 2搬迁至空闲的 Node B1. 异步快照追赶Snapshot Catch-upNode A 并行向 Node B 发送全量 LSM-Tree SST 物理文件快照。在此期间业务写入依然正常砸向 Node ANode B 仅作为后台接收端完全不影响核心交易读写。2. 单步成员变更升级数据快照传输完毕后Node A 通过 Raft 单步成员变更将 Node B 提升为正式的 Follower 节点并开始增量重放这段时间积压的 Raft Log。3. 秒级 Leader 转移Leader Transfer当 Node B 的复制延迟降为 0 时Node A 主动向 Node B 发送TransferLeader指令。Node B 在数毫秒内接管 Leader 角色随后将 Node A 上的冗余副本物理剔除并回收磁盘空间。路由一致性收敛RegionEpoch防御过期请求在分片搬迁期间客户端缓存的路由表Client Routing Table可能尚未及时刷新依然将属于Region 2的请求发送给旧节点 Node A。系统如何防止读写发生“张冠李戴”答案在于每个分片自带的RegionEpoch纪元版本号只要发生分裂Version自动加 1只要发生节点变更ConfVer自动加 1。客户端发起任何请求时必须在请求头中携带本地缓存的RegionEpoch。如果 Node A 收到请求后发现自己的纪元版本已更新EpochNotMatch会立即拒绝请求并返回最新的路由信息StaleCommand响应。客户端捕获后自动刷新本地缓存并重定向至 Node B整个容错重试过程在 2~5 毫秒内透明完成上层业务代码完全感知不到任何异常。动态 Sharding 机制是分布式存储对抗数据洪峰与容量瓶颈的终极护城河。看清这套无锁分裂与异步搬迁的内核实现才能真正驾驭现代云原生分布式数据库的弹性力量。