zone sharding 不能用于租户隔离,因其仅按分片键路由、不理解租户语义;必须以 tenantid 为分片键并配合应用层强校验与查询过滤,否则将导致数据错乱、越权访问及运维失控。

Zone Sharding 不能用于租户隔离 —— 它不是为这个目的设计的,强行用会路由失效、数据错落、运维失控。
分片键必须是 tenantId,否则 Zone 配置无效
Zone 本身不理解“租户”语义,它只按分片键的值范围或哈希值做物理落盘控制。
如果你的分片键是 {createdAt: 1} 或 {_id: "hashed"},那无论你执行多少次 sh.addShardToZone("shard01", "tenant-a"),插入 {tenantId: "tenant-a"} 的文档依然会散落在多个分片上。
- 必须先执行
sh.shardCollection("db.col", {tenantId: 1})(或{tenantId: "hashed"}) - 再用
sh.updateZoneKeyRange()绑定范围,例如:sh.updateZoneKeyRange("orders", {tenantId: "acme"}, {tenantId: "acme"}, "acme_zone") - 注意:范围必须严格匹配分片键结构;若分片键是复合键
{tenantId: 1, seq: 1},就不能只传"acme",得写成{tenantId: "acme", seq: MinKey}到{tenantId: "acme", seq: MaxKey}
Zone 不提供访问控制,越权查询照常执行
Zone 是纯路由策略,不是权限机制。
即使你把所有 tenantId: "acme" 的数据都强制落到 shard01,只要应用层查询漏写 tenantId 条件,比如 db.orders.find({status: "pending"}),mongos 仍会广播到所有分片,结果可能混入其他租户数据。
- 租户隔离的防线只能在应用层:所有读写操作必须显式带
tenantId过滤 - Schema 层需用 Mongoose 钩子或 JSON Schema 强制校验该字段存在且非空
- 聚合管道中
$lookup更容易漏 —— 它不支持动态集合名,必须手动在后续$match补tenantId
新增租户要重配分片逻辑,不适合动态业务
每加一个租户,就得走一遍完整链路:
-
sh.addShardToZone()加标签 -
sh.updateZoneKeyRange()绑范围 - 若已有数据,还得
moveChunk手动迁移,且 chunk 边界必须对齐分片键
这不是日常运维动作,而是发布级变更。
一旦租户量涨到几百个,这套流程就不可持续 —— 尤其当租户生命周期短(如试用期用户自动注销),配置漂移和 chunk 错位风险极高。
真正能撑住生产流量的租户隔离,靠的是分片键 + 应用层强约束,而不是把 Zone 当租户开关用。最容易被忽略的一点是:已有集合无法直接改分片键,sh.shardCollection() 只接受空集合或已建好对应索引的非空集合,而索引又必须以 tenantId 为前导字段。











