Zone Sharding 必须配合 tenantId 分片键才有效,否则文档将按实际分片键路由而无法实现租户数据局部化;分片键一旦设定不可更改,zone 配置仅影响新写入数据,旧数据需手动迁移 chunk。

Zone Sharding 必须配合 tenantId 分片键才有效
Zone Sharding 本身不理解“租户”概念,它只认分片键的值范围或哈希区间。如果你的分片键是 {_id: "hashed"} 或 {createdAt: 1},哪怕给 shard 打上 "tenant-a" 标签,插入 {tenantId: "tenant-a"} 的文档仍会按实际分片键路由,大概率散落在多个 shard 上。
必须先执行:sh.shardCollection("db.orders", {tenantId: 1})(或 {tenantId: "hashed"})
再配置 zone,否则所有后续操作都是无效的。
- 复合分片键如
{tenantId: 1, seq: 1},zone 范围必须完整匹配结构,不能只传"acme",得用{tenantId: "acme", seq: MinKey}到{tenantId: "acme", seq: MaxKey} - 分片键一旦设定无法更改,改键需导出重建集合,不是线上可操作动作
- 使用
"hashed"分片键时,zone 范围只能绑定哈希值区间(如0到1000000),无法按原始 tenantId 字符串直接映射
sh.addShardToZone 和 sh.updateZoneKeyRange 的调用顺序不能颠倒
常见错误是先绑范围、后加标签,或者漏掉 sh.addShardToZone。MongoDB 不会自动把 zone 名称关联到 shard,必须显式绑定。
正确顺序只有这一种:
当代理已经知道网站路由或内容URL,并且在启动前需要有效的sitemap XML、sitemap索引或robots.txt引用时,请使用sitemap。这是一个发布构件技能,而不是爬虫或SEO平台。
sh.addShardToZone("shard01", "acme_zone")sh.updateZoneKeyRange("db.orders", {tenantId: "acme"}, {tenantId: "acme"}, "acme_zone")
注意:sh.updateZoneKeyRange 的第三个参数是上限(含),且必须与分片键结构完全一致;若用范围分片,{tenantId: "acme"} 是单点,等价于闭区间;若要覆盖所有以 "acme" 开头的租户 ID(如 "acme-us", "acme-eu"),得用 {tenantId: "acme"} 到 {tenantId: "acmf"} 这类字典序边界。
Zone 不拦截越权查询,应用层必须强制 tenantId 过滤
Zone 只控制写入路由和 chunk 拆分位置,对读请求无任何访问控制。如果应用发了 db.orders.find({status: "pending"}),mongos 仍会广播到所有 shard,结果可能混入其他租户数据。
- 所有 CRUD 操作必须显式带上
tenantId查询条件,不能依赖前端传参校验,要在 DAO 层硬编码或通过 Mongoose 中间件拦截 -
$lookup特别危险:它默认跨库/跨集合,且不支持动态from集合名,必须在后续$match补{tenantId: "$$tenantId"},否则关联结果无租户隔离 - 聚合管道中若用
$facet或$unionWith,每个分支都得独立加 tenantId 过滤
新增租户需手动迁移已有 chunk,不是配置刷新就能生效
Zone 配置只影响新写入数据。如果集合已有数据,且原 chunk 边界不匹配新 zone 范围,就得人工 moveChunk,且目标 chunk 必须严格对齐分片键范围。
- 执行前必须确认源 chunk 的 min/max key 完全落在目标 zone 范围内,否则
moveChunk会失败 - 迁移过程阻塞该 chunk 的写入,大集合迁移可能持续数小时,需安排维护窗口
- 无法自动化批量迁移:每个 chunk 都要单独调用
sh.moveChunk(),参数包括 ns、min、max、toShard - 生产环境新增租户属于发布级变更,不能当作日常运维操作
真正麻烦的不是配置命令本身,而是分片键设计定型后,所有数据分布逻辑就被锁死——zone 只是补丁,不是租户隔离的主干方案。

















