分布式数据库在进行数据重分布(也叫数据再平衡、resharding)时,最核心的难题不是数据怎么搬,而是搬的过程中业务流量怎么切。简单说,就是你不能停服务去搬数据,必须在线完成迁移,同时保证业务不中断、不丢数据、不出现大面积超时。目前业界主流的方案有三种:双写过渡方案、代理层流量切换方案、以及基于时间窗口的分阶段切换方案。每种方案都有明确的适用场景和操作步骤,下面我逐一拆解。

一、为什么数据重分布必须伴随流量切换

分布式数据库的数据重分布,本质上是因为集群节点扩容、缩容、故障替换或者分片策略调整,导致原有的数据分布不再均衡,需要把一部分数据从旧节点迁移到新节点。这个过程中,数据的物理位置发生了变化,但业务层的路由规则还指向旧位置。如果不做流量切换,业务请求就会打到空节点或者数据不完整的节点上,直接导致查询失败或数据错乱。所以流量切换不是可选项,而是数据重分布的必要配套动作。

二、双写过渡方案:最稳妥但最慢的方式

双写方案的核心思路是:在重分布期间,业务同时往旧分片和新分片写数据,等新分片数据追平之后,再把读流量切过去,最后停掉旧分片的写。具体操作分四步:

第一步,开启双写模式。在应用层或者中间件层,把写请求同时发送到旧分片和新分片。这时候需要处理写冲突的问题,通常以新分片的数据为准,旧分片的数据做幂等覆盖。

第二步,后台异步迁移存量数据。双写开启后,用后台任务把旧分片的历史数据逐步拷贝到新分片,这个过程不影响线上流量。

第三步,数据校验。通过checksum或者行数对比,确认新旧分片数据完全一致后,把读流量逐步切到新分片。

第四步,下线旧分片。确认新分片稳定运行一段时间后,关闭旧分片的双写通道,完成切换。

这个方案的优点是数据一致性有保障,缺点是双写期间写性能会下降大约30%-50%,而且整体切换周期长,适合对数据一致性要求极高的金融、支付类业务。

三、代理层流量切换方案:最灵活的中间路线

代理层方案是在数据库前面加一层智能代理(比如基于MySQL Protocol的中间件或者自研的SQL代理),由代理来控制流量的路由。重分布时,代理根据配置规则,把特定范围的请求转发到新节点,其他请求仍然走旧节点。

具体实现上,代理维护一张路由表,记录每个数据范围(比如按ID区间或者哈希值区间)对应的目标节点。重分布开始后,逐步修改路由表,把部分区间指向新节点。修改是渐进式的,每次只切一小段流量,观察一段时间没问题再切下一段。

这种方案的关键代码逻辑大致如下:

// 路由规则更新示例(伪代码)
func updateRoutingTable(oldShard, newShard, idRange) {
    // 1. 将指定ID范围的路由从旧分片改为新分片
    routingTable.remove(oldShard, idRange)
    routingTable.add(newShard, idRange)
    
    // 2. 灰度发布:先切10%流量观察
    grayRatio := 0.1
    trafficSwitch.set(idRange, newShard, grayRatio)
    
    // 3. 监控指标正常后全量切换
    if metrics.check(latency, errorRate) {
        trafficSwitch.set(idRange, newShard, 1.0)
    }
}

代理层方案的优势是对业务代码无侵入,切换粒度细,可以做到按SQL级别甚至按事务级别控制。劣势是代理本身成为单点,需要做高可用,而且代理的性能开销需要重点关注,一般要求代理层延迟增加不超过1毫秒。

四、分阶段时间窗口方案:适合可容忍短暂不一致的场景

这种方案适用于业务有明显低峰期的场景,比如电商的凌晨时段、游戏的维护窗口。核心做法是把重分布拆成多个阶段,每个阶段只处理一部分数据的迁移和流量切换,利用低峰期完成高风险操作。

第一阶段:在低峰期暂停部分业务或者降级服务,把第一批数据迁移到新节点,完成后恢复服务。

第二阶段:在下一个低峰期,继续迁移下一批数据,同时更新路由配置。重复这个过程直到所有数据迁移完毕。

这种方案的好处是每个阶段的风险可控,出了问题影响范围小。坏处是总切换周期可能长达数天甚至数周,而且需要业务方配合做降级预案。

五、流量切换过程中必须关注的五个核心指标

不管用哪种方案,切换过程中都要盯紧以下五个指标,任何一个异常都要立即回滚:

第一,请求延迟P99。切换期间延迟上升超过基线的20%就要暂停,排查是否有热点数据没迁完或者路由配置有误。

第二,错误率。特别是写冲突导致的唯一键冲突、死锁等问题,错误率超过0.1%必须回滚。

第三,数据一致性。通过定时校验任务确认新旧节点数据一致,尤其是双写方案中,要防止旧数据覆盖新数据的情况。

第四,连接数和吞吐量。切换可能导致连接重建风暴,需要提前做好连接池预热和限流策略。

第五,主从同步延迟。如果重分布涉及主从切换,要确保新主的同步延迟在可接受范围内,否则切过去会有数据丢失风险。

六、回滚机制:没有回滚方案的切换都是赌博

任何流量切换方案都必须配套回滚机制。回滚的核心是保证在发现问题后,能在5分钟内把流量切回旧节点,并且数据不丢失。具体做法包括:保留旧节点至少运行24小时不下线、切换前做全量数据快照、路由表支持一键回退配置。在代理层方案中,回滚就是把路由表恢复到切换前的状态;在双写方案中,回滚就是重新把读写切回旧分片。

七、实际案例中的常见坑和避坑建议

根据行业实践,数据重分布流量切换最容易踩的坑有三个:一是忽略了跨分片事务的处理,迁移过程中事务被拆到两个分片上导致数据不一致;二是没做好灰度验证,一次性切全量流量导致系统雪崩;三是低估了数据迁移对磁盘IO和网络带宽的占用,导致正常业务被挤占资源。

避坑建议:迁移前做全链路压测,确认资源够用;切换时严格按5%-10%-50%-100%的梯度放量;事务类业务尽量在迁移期间走旧分片,等迁移完再统一切。

八、总结:选方案的决策逻辑

选哪种方案,取决于三个因素:业务对停机的容忍度、数据一致性要求、以及团队的技术储备。零停机要求高且数据敏感,选双写;需要灵活控制且有中间件能力,选代理层;业务有低峰窗口且能接受长周期,选分阶段方案。不管选哪种,回滚机制和监控体系都是底线,缺一不可。数据重分布不是一次性工程,而是分布式数据库运维的常态能力,把这套流程跑通、跑熟,才是真正的技术壁垒。