reshardCollection会触发单次最长2秒的写锁,导致相关写请求阻塞并引发延迟突刺;查询须同时包含新旧分片键字段,否则报错;需预留至少1.2倍集合storageSize的磁盘空间,并在完成后手动清理旧索引及更新应用逻辑。

reshardCollection 会触发两秒写锁,业务延迟敏感需提前应对
reshardCollection 不是完全无感操作。它会在每个 chunk 迁移前对目标 chunk 加一个短暂的写锁,单次最长约 2 秒。这不是全局锁,但对落在该 chunk 上的写请求(insertOne、updateOne、deleteOne、findAndModify 等)会造成阻塞,表现为 P99 延迟突刺。
常见错误现象包括:
- 应用日志中出现大量
WriteConflict或超时(如MaxTimeMSExpired) - 监控图表上 write latency 出现周期性尖峰,与
resharding日志时间戳对齐 - 部分事务失败,尤其依赖
session.startTransaction()的场景可能因锁等待超时中断
实操建议:
- 确认业务能容忍单次 ≤2 秒的写入暂停;若不能,必须在低峰期执行,并配合应用层重试逻辑
- 避免在 resharding 期间执行大事务或长耗时更新,否则容易被锁住并拖慢整个迁移进度
- 不要依赖
maxTimeMS强制中断——锁是 MongoDB 内部机制,超时参数无法绕过
resharding 期间查询必须同时命中新旧分片键,否则报错
MongoDB 要求你在 resharding 过程中,所有涉及该集合的读写操作都必须包含当前分片键和新分片键的字段。否则会直接报错:Cannot target a shard key that is not present in the query。
典型使用场景:
-
find({ name: "zhangsan" })→ ❌ 报错,只含旧分片键name -
find({ name: "zhangsan", _id: ObjectId("...") })→ ✅ 同时含旧键name和新键_id(假设新键为_id) -
updateOne({ name: "zhangsan" }, { $set: { status: "active" } })→ ❌ 报错,不满足双键条件
注意点:
- 即使你没显式用到新键字段,也必须传进去(可设为
{$exists: true}或合理范围值) - 索引必须已存在且覆盖新旧键组合,否则查询性能会断崖式下跌
- 聚合管道中若用
$match过滤,同样要满足双键约束,否则阶段会被拒绝执行
磁盘空间不足会导致 resharding 中途失败并回滚
reshardCollection 不是原地修改,而是先在新分片上构建副本数据,再逐步切换路由。因此需要额外磁盘空间:至少为原集合大小的 1.2 倍。例如集合占 1TB,空闲空间必须 ≥1.2TB。
容易踩的坑:
- 误以为“只是改路由”,忽略临时副本占用;实际每个参与迁移的 shard 都要写一份新格式数据
- 监控只看
db.stats().dataSize,但没算storageSize(含索引、padding、WiredTiger cache 占用) - 使用 WiredTiger 引擎时,压缩率影响预估,建议按
storageSize × 1.5保守预留
验证方式:
- 执行前运行
db.runCommand({collStats: "newusers"})查看storageSize - 检查各 shard 的
df -h /var/lib/mongodb,确保可用空间 >storageSize × 1.2 - 留意
mongod.log中是否频繁出现WT_ERROR: No space left on device
resharding 完成后仍需手动清理旧索引和更新应用逻辑
reshardCollection 只改分片键和数据分布,不会自动删旧索引、不会自动关掉旧路由规则、也不会帮你改代码。很多团队卡在这一步导致长期双写负担或查询不准。
关键收尾动作:
- 确认
sh.status()中该集合的shardKey已更新为目标键(如从{name: "hashed"}变为{_id: 1}) - 手动 drop 掉旧分片键相关索引(如
db.newusers.dropIndex({name: "hashed"})),否则持续占用内存和写开销 - 应用端必须删除所有针对旧分片键的
.hint()、.min()/.max()、sh.splitAt()类硬编码逻辑 - 检查慢查询日志,确认不再有
SHARDING_FILTER阶段——这是旧路由残留的典型信号
最容易被忽略的是:resharding 后首次 full collection scan 仍可能走旧路由路径,除非你显式重建了 _id 索引或刷新了 mongos 的 config cache。











