在MongoDB分片集群的运维过程中,故障码1570是一个出现频率不低的信号。它对应的错误类型是SplitFailed,也就是块分裂操作失败。偶发一次影响不大,但如果日志里反复出现1570,并且伴随写入延迟上升、CPU和IO周期性抖动,基本可以判定集群的块分裂行为已经失控,需要尽快定位根因并做出调整。本文将从触发机制、性能影响、排查路径和优化方案四个层面,把这个问题完整讲清楚。

故障码1570的底层含义:SplitFailed是怎么触发的
要理解1570,先要理解分片集群中块的基本运作方式。数据按照分片键划分成一个个chunk,每个chunk对应分片键的一段区间。当某个chunk的数据量增长超过配置的chunkSize(新版本默认128MB,旧版本常见64MB)时,系统会尝试把这个chunk一分为二,让数据分布重新变得均匀。分裂之前,mongod需要在分片键索引上执行splitVector逻辑,扫描索引找出合适的分裂点,然后向配置服务器注册新的chunk元数据,整个流程才算完成。
故障码1570正是在这个分裂流程中抛出的。常见的触发场景有三类:第一类是找不到分裂点,最典型的情况是一个chunk内所有文档的分片键值完全相同或者高度集中,索引上不存在能把数据切开的点,splitVector返回空结果,分裂直接失败;第二类是元数据操作超时或版本冲突,config server响应慢、chunk版本号不一致,都会让分裂流程中途放弃;第三类是热点写入导致反复触发分裂,写入集中落在少数chunk上,刚分裂完很快又达到阈值,集群进入持续分裂的循环状态。日志中典型的报错形态如下:
[shard1] I SHARDING [conn12345] splitVector failed,
ns: orders.clusters, shard: shard1,
errorCode: 1570, errorMessage: cannot find available split points
[mongos] W SHARDING [conn678] splitting chunk
ns: orders.clusters, shard: shard1
failed with error 1570 SplitFailed
需要特别强调的是,1570本身只是一个结果,它背后往往藏着分片键设计缺陷或写入热点这类结构性问题。如果只是盯着报错做重试,而不去分析为什么分裂会失败、为什么分裂如此频繁,问题只会反复出现,甚至越拖越严重。
块分裂频繁为什么会拖垮性能
不少人对块分裂的开销存在误解,觉得它只是元数据操作,代价不大,这个认知需要修正。一次完整的分裂涉及三块成本:其一是splitVector的索引扫描,数据量大、索引层级深时,这个扫描会消耗可观的CPU和IO;其二是配置服务器的元数据写入,每次分裂都要在config.chunks中删除旧记录、插入新记录,并同步版本信息到所有路由节点;其三是分裂之后的连锁反应,chunk数量变化会触发均衡器的迁移判断,而迁移过程涉及数据拷贝、索引重建和源端的范围删除,这才是开销的真正大头。
当块分裂频繁发生时,这几块成本会叠加放大。假设集群每分钟触发十几次分裂,每次分裂又可能引发一次迁移,源分片要一边承接业务写入一边执行范围删除,IO队列迅速堆积,写入延迟从几毫秒恶化到几百毫秒都很常见。更麻烦的是,分裂失败且持续超过阈值的chunk会被标记为jumbo状态,均衡器不再尝试迁移它,数据倾斜随之加剧,热点分片的负载进一步恶化,形成恶性循环。
还有一个容易被忽视的影响是临界区的锁等待。分裂和迁移在执行的关键阶段会进入critical section,期间相关chunk上的写入会被短暂阻塞。偶发一次感知不到,但如果分裂和迁移持续不断,应用端的写入就会出现周期性卡顿,在监控上表现为P99延迟曲线的规律性尖刺。如果你们的延迟图正好长这样,不妨先去日志里搜一下分裂记录,往往能对上时间点。
排查路径:从日志到集群状态的诊断链路
定位1570问题,建议按照固定顺序走一遍诊断流程。第一步看mongos和mongod的日志,用关键字过滤分裂相关记录,确认报错的命名空间、所在分片以及具体错误信息:
grep -E "split.*failed|error 1570|jumbo" /var/log/mongodb/mongos.log | tail -50
第二步检查chunk的分布和状态,在mongos上执行sh.status(),重点观察目标集合的chunk总数、各分片上的chunk数量差异,以及是否出现带JUMBO标记的chunk。第三步直接查询配置库,拿到更精确的分布数据:
use config
// 查看某个集合的chunk区间分布
db.chunks.find({ns: "orders.clusters"},
{min: 1, max: 1, shard: 1}).sort({min: 1})
// 统计各分片上的chunk数量,判断倾斜程度
db.chunks.aggregate([
{$match: {ns: "orders.clusters"}},
{$group: {_id: "$shard", count: {$sum: 1}}},
{$sort: {count: -1}}
])
第四步评估热点chunk的实际数据量。chunk的元数据里没有实时大小字段,需要用dataSize命令估算,min和max的取值来自上一步的查询结果:
db.runCommand({
dataSize: "orders.clusters",
keyPattern: {userId: 1},
min: {userId: MinKey},
max: {userId: 100000},
estimate: true
})
第五步检查分片键的基数和分布,这是判断1570根因的关键一步。用聚合统计分片键值的重复程度:
use orders
// 统计分片键上出现次数最多的值
db.clusters.aggregate([
{$group: {_id: "$userId", cnt: {$sum: 1}}},
{$sort: {cnt: -1}},
{$limit: 20}
])
如果排在前面的几个分片键值各自对应了几十万甚至上千万条文档,那么根因基本可以锁定:分片键基数不足,单个键值的数据量远超chunkSize,chunk无法在键值内部切开,分裂必然失败,这类chunk最终都会走向jumbo。反过来,如果分片键基数没问题,就要把注意力放到写入热点和chunkSize配置过小这两个方向上。
优化方案:从分片键设计到参数调优
解决1570问题要分两个层面:一是消除当前的报错和数据倾斜,二是从设计上避免问题复发。最根本的方案是重新设计分片键。一个健康的分片键应当同时具备高基数、写入分布均匀、查询能命中三个特征。对于存在热点风险又无法保证写入均匀的场景,散列分片是首选,它会把相邻的键值打散到不同的chunk,从根本上避免热点写入导致的频繁分裂:
// 新集合直接使用散列分片
sh.shardCollection("orders.clusters_v2", {userId: "hashed"})
// 存量大集合建议新建散列分片集合后迁移数据
// 避免在线调整带来的长时间元数据锁竞争
如果业务暂时无法更换分片键,可以退而求其次做参数和运维层面的缓解。第一招是调大chunkSize,降低分裂触发频率:
// 将块大小调整为256MB,减少分裂次数
db.settings.update(
{_id: "chunksize"},
{$set: {value: 256}},
{upsert: true}
)
需要注意,调大chunkSize只是降低频率,治标不治本,而且会让单个chunk迁移的代价变得更高。一般建议控制在64MB到512MB之间,不要盲目调到GB级别,否则一次迁移就能让源分片的IO打满。第二招是对可预判的写入模式做预分裂,在写入高峰到来之前手动把目标区间切开,避免集群在业务最忙的时候自动分裂:
// 手动在指定分裂点切分chunk
sh.splitAt("orders.clusters", {userId: 500000})
// 对单调递增的分片键,可以按区间预分裂一批chunk
sh.splitAt("orders.clusters", {orderId: 1000000})
sh.splitAt("orders.clusters", {orderId: 2000000})
sh.splitAt("orders.clusters", {orderId: 3000000})
第三招是处理已经产生的jumbo chunk。如果chunk被标记为jumbo但实际数据已经可以切开,比如热点写入高峰已过,可以清除标记让均衡器重新接管;如果确实切不开,只能通过清理或归档部分数据来缩小chunk体积:
// 清除jumbo标记,find参数用于定位目标chunk
db.adminCommand({
clearJumbo: "orders.clusters",
find: {userId: 500000}
})
最后从监控层面建立防线。把分裂和迁移次数纳入日常监控指标,config.changelog中记录了所有分裂和迁移事件,可以按时间窗口统计频次:
use config
// 统计最近一小时的分裂事件数量
db.changelog.count({
time: {$gt: new Date(Date.now() - 3600 * 1000)},
what: "split"
})
当单小时的分裂次数持续超过个位数时,就应该警惕写入热点或分片键设计问题,提前介入的成本远低于事后救火。
总结
故障码1570本质上是分片集群在发出信号:块分裂这条路走不下去了。偶发的1570可以观察不动,但频繁的1570叠加性能抖动,基本指向分片键基数不足、写入热点或chunkSize配置不当这几个方向。排查时按照日志、chunk分布、数据量、分片键基数的顺序逐步收敛,处理时优先考虑散列分片或复合分片键这类结构性方案,参数调优和手动预分裂只作为过渡手段。把分片键设计这一课补扎实,1570自然就失去了生存的土壤,集群的写入吞吐和延迟曲线也会回归平稳。
MongoDB块分裂分片集群分片键修改时间:2026-10-03 07:30:37