前面几篇讲了片键设计、范围/哈希分片、chunk 和 balancer。这一篇把分片上线后最常踩的三个坑收束起来:jumbo chunk、孤儿文档、写入热点。它们表现形式不同,但根源大多指向片键设计或运维操作。
理解这三个陷阱,能在分片集群出问题时快速定位:数据为什么倾斜?计数为什么重复?单 shard 为什么写满?这些都是分片运维的高频问题。
陷阱一:jumbo chunk 无法迁移
成因:片键基数太低。一个片键范围(chunk)内的文档太多,超过了 chunk 迁移上限,balancer 无法迁移它。
比如片键是 status(只有几个值),所有 active 文档挤在一个片键范围。这个 chunk 不断长大,却无法分裂(片键值太少,没有中间点可拆),最终超出迁移能力,被标记为 jumbo。
现象:balancer 一直在尝试均衡却均衡不了,数据永久倾斜在某个 shard。监控会看到某个 shard 的数据量远超其他,且 balancer 迁移日志里这个 chunk 反复失败。
解法 :根本解法是改片键------选一个基数足够高的片键,让 chunk 能正常分裂和迁移。临时手段是手动清除 jumbo 标记(clearJumboFlag),让 balancer 尝试迁移,但如果片键不改,jumbo 会再次出现。
陷阱二:孤儿文档
成因:chunk 迁移中断或残留。正常迁移时,源 shard 把 chunk 拷到目标后删除源数据。但如果迁移中途失败、或异常重启,源 shard 可能残留已迁移的文档副本------这些文档在 config server 元数据里已经不属于这个 shard,但物理上还在它的磁盘上,成为「孤儿」。
现象 :孤儿文档会导致数据被重复计算。比如 count() 可能比实际多(孤儿被算了两次),聚合结果异常。更隐蔽的是,某些查询可能从孤儿 shard 返回本该不返回的文档。
解法 :孤儿本质是迁移残留问题,现代 MongoDB(6.0+)已用改进的 range deleter 自动处理迁移后清理,cleanupOrphaned 命令在 6.0 已被移除,正常运维不需要任何手动操作 。只有当确实怀疑存在孤儿(count() 偏多、聚合结果异常、版本较老)时,再排查:6.0+ 确认 range deleter 是否正常工作,老版本可用 cleanupOrphaned(仅限 6.0 之前)扫描并删除残留。
陷阱三:写入热点
成因:片键单调递增 + 范围分片。时间戳、自增 ID、ObjectId 这类单调递增的片键,配合范围分片,会让所有新数据都落在「最大值」那个 chunk(通常在最后一个 shard),形成写入热点。
现象:单 shard 承受全部写入压力,CPU/IO 打满,其他 shard 闲置。分片集群的负载分散能力完全失效。监控会看到某个 shard 的 opcounters 远高于其他。
解法:改用哈希分片(把单调递增的值打散),或用复合片键(加一个分散前缀)。这又回到第 27 篇的范围/哈希选择------单调递增键不适合单独范围分片。
三个陷阱的共同根源
把三个陷阱放一起看,共同点是:它们都让「分了等于没分」。jumbo 让数据倾斜、孤儿让数据重复、热点让负载集中。分片集群失去了负载分散的意义,还增加了复杂度。
更关键的是,jumbo 和热点都根植于片键设计,而片键极难更改。所以这三个陷阱的预防,比治疗重要得多:
- 上线前压测片键分布:用真实数据量测试片键的数据分布是否均匀、会不会形成热点。
- 监控各 shard 数据量:数据量持续失衡说明片键有问题。
- 监控 balancer 迁移频率:频繁迁移或迁移失败,指向 jumbo 或倾斜。
- 孤儿靠 range deleter 自动清理 (6.0+ 无需手动;老版本才需要
cleanupOrphaned),重点是把片键选对,从源头减少迁移异常。
一个真实的教训
很多分片事故的剧本是这样的:业务上线时选了个「看起来合理」的片键(比如时间戳,因为想按时间查),用了范围分片。初期数据少,一切正常。随着数据增长,写入热点出现(时间戳递增,全堆一个 shard),但因为是递增的,jumbo 还没形成。等到发现单 shard 写满,想改片键已经很难(数据量大)。最后要么忍着倾斜,要么走艰难的 reshardCollection。
这个教训的核心是:片键设计要在上线前用未来的数据规模验证,不能只看当下。第 26 篇强调的「片键极难改」,在这一篇体现为具体的运维事故。
判断框架
- jumbo chunk:片键基数低 → 改高基数片键。
- 孤儿文档:迁移残留 → 6.0+ 由 range deleter 自动清理,无需手动;老版本才用
cleanupOrphaned。 - 写入热点:单调递增 + 范围分片 → 哈希分片或复合片键。
- 三个陷阱都让分片失效,预防比治疗重要。
- 上线前压测片键分布,监控 shard 数据量和 balancer 迁移。
- 片键是命运决定点,设计阶段多花时间。
下一篇是分片阶段的收尾,讲查询路由和广播查询。
关于十三Tech
All in AI Agent 方向的架构师,专注 AI 工程实践。
相信 AI 是程序员的最佳搭档,帮助每一位开发者驾驭 AI。
公众号搜索「十三Tech」