7.2 分片部署与迁移风暴事故


7.2 分片部署与迁移风暴事故

本节摘要:chunk 超过阈值会分裂,分片间 chunk 数不均触发 balancer 迁移。本节从一次业务高峰期的迁移风暴讲集群搭建流程与 balancer 的窗口化管理。

事故档案 16:晚高峰的迁移风暴

电商大促前夜扩容,新加两个分片。balancer 检测到 chunk 分布严重不均,连夜开始搬数据。恰好赶上晚高峰,迁移的复制流量与业务流量叠加,全集群延迟翻倍,被迫紧急停 balancer:

// 止血:立刻停平衡器 sh.stopBalancer(); // 观察窗口 sh.isBalancerRunning(); // 恢复但限制在凌晨窗口 db.settings.updateOne( { _id: "balancer" }, { $set: { activeWindow: { start: "02:00", stop: "06:00" } } }, { upsert: true } );

迁移本身是稳态操作(复制 + 追 oplog + 原子改路由),但大促扩容 = 大量 chunk 要搬,搬的时间点错了就是事故。

集群搭建流程

// 1. 初始化 config 复制集(三节点)与各分片复制集 // 2. 把分片加入集群 sh.addShard("rs0/mongo-s1a:27017,mongo-s1b:27017"); sh.addShard("rs1/mongo-s2a:27017,mongo-s2b:27017"); // 3. 启用分片并选键(键上必须有索引) sh.enableSharding("trade"); db.orders.createIndex({ orderId: "hashed" }); sh.shardCollection("trade.orders", { orderId: "hashed" }); // 4. 预分裂(哈希键可预分裂,避免初期集中分裂) sh.shardCollection("trade.orders", { orderId: "hashed" }, false, { numInitialChunks: 64 });

迁移生命周期

运维节奏

  • balancer 设时间窗,避开业务高峰是铁律;
  • 扩容选在低谷,一次加足,避免连续多次触发再平衡;
  • sh.status() 例行巡检:chunk 分布、zone 标签、balancer 状态;
  • jumbo chunk(无法分裂的超大 chunk)会卡住均衡,通常是分片键基数不足的信号。

💡 迁移风暴的本质是"欠账集中还"。平时让 balancer 小步慢走,别等分布严重失衡才扩容。

事故复盘:风暴是怎么滚起来的

时间线还原。大促前夜 22 点,两个新分片加入集群,balancer 立刻发现新旧分片 chunk 数量比是 10:0:0,开始搬迁。chunk 迁移的机制是:目标分片从源分片复制 chunk 数据、追平迁移期间的增量 oplog、config 原子改路由、源分片删除旧数据。每一步都是真实的网络与磁盘流量,且迁移期间该 chunk 的写要在两侧同时维护。23 点晚高峰到达,业务写入与新加的两个分片的全量搬迁叠加,mongos 的路由刷新也随 config 高频变更出现瞬时抖动,集群 P99 延迟从 15 毫秒翻到 34 毫秒。23 点 40 分执行 sh.stopBalancer(),已排队的迁移做完后不再新增,延迟回落。

复盘结论有三条。第一,扩容动作与均衡动作必须解耦:加分片前先停 balancer,业务低谷再打开窗口放行。第二,迁移速度有控制参数可调( balancer 的迁移并发与每轮 chunk 数,通过 config.settings 的 balancer 配置收紧),风暴时"一刀切停"之外还有"限速放行"这个中间选项,能让均衡在可控代价下继续。第三,监控里必须有 balancer 活动指标(config.changelog 的迁移频次),均衡状态不该是"看不见的黑盒"——风暴滚了 100 分钟才有告警,说明平时没人看这里。

// 均衡健康度巡检:最近的迁移记录 use config db.changelog.find({ what: "moveChunk.commitStart" }) .sort({ time: -1 }).limit(5) .pretty(); // { what: "moveChunk.commitStart", ns: "trade.orders", // details: { from: "rs0", to: "rs2", ... } }

chunk 与 zone 的进阶用法

chunk 除了自动均衡,还能用 zone 做受控放置:给分片打标签(如 zone: "south")、给键区间打同样标签,数据就只会落在指定分片。典型场景是数据合规(某地区数据必须留在当地机房)与冷热分层(老数据放低配分片)。zone 是把"数据住哪"从均衡器手里拿回一部分控制权的正路,很多团队用脚本干预 chunk 位置这种野路子,极易被 balancer 悄悄改回去。

sh.addShardTag("rs2", "south"); sh.addTagRange("logistics.orders", { province: "广东", orderId: MinKey }, { province: "广东", orderId: MaxKey }, "south"); // 广东数据全部落在 rs2,其余照常均衡

预分裂的价值再强调一次:哈希键 + numInitialChunks 让新集合从第一笔写入就分布均匀,省掉"先集中分裂、再均衡搬迁"的一整轮折腾;范围键的预分裂要手工给出切分点,适合已知分布规律的场景(如按地区前缀切)。这一步在建集合时只花一行参数,错过就要在 7.2 的迁移机制里还债。

本节要点回顾

  • chunk 分裂是体积驱动,迁移是均衡驱动
  • balancer 必须有窗口,高峰期先停再排期;
  • 扩容一次到位,选低谷执行;
  • jumbo chunk 是键选择问题的回声,回到 7.3 解决。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U