预分片是秒杀场景下规避写入热点的唯一务实手段,需配合哈希分片键、显式sh.splitAt()预先切分chunk并均匀分布,且活动前停均衡器、验证chunk分布与数量。
秒杀场景下,MongoDB分片集群不能靠“等数据写入后再自动拆分”来扛住流量——那会直接压垮单个 shard,引发
和迁移延迟雪崩。预分片(pre-splitting)是唯一能提前把数据打散、规避写入
热点
的务实手段。
为什么自动分片在秒杀中完全不可靠
默认块大小 64MB、触发拆分阈值为 51.2MB(
),而秒杀请求常在几秒内涌入数十万文档。即使分片键合理,首个 chunk 仍会持续接收所有新写入,直到撑爆阈值才分裂——此时 mongos 路由压力、单 shard 的锁竞争、WiredTiger page latch 冲突已导致超时或拒绝服务。
自动分裂是被动响应,无法应对毫秒级突发写入
均衡器(balancer)迁移 chunk 是后台异步操作,迁移期间 chunk 不可写,加剧排队
秒杀 ID 若含时间前缀(如
),范围分片下所有请求路由到同一 chunk,哈希分片虽缓解但无法消除首写集中
预分片必须配合哈希分片键 + 显式
预分片不是“多建几个 shard”,而是预先将目标集合按预期写入规模切出足够多的初始 chunk,并均匀分布到各 shard。关键前提是:分片键必须支持哈希,且基数足够高(如用户 ID、订单号哈希值)。
jQuery点击热图效果
一款基于jQuery实现的点击热图效果,点击按钮整个网页变灰。适用浏览器:IE8+、FireFox、Chrome、Safari、Opera。
下载
先启用库和集合分片:
,再用哈希方式指定键:
计算预估总写入量(如 100 万订单),结合平均文档大小(如 512B),得出需覆盖的哈希空间范围(
到
)
用
手动切出 chunk,例如:
,重复执行直至覆盖全哈希空间
用
验证 chunk 数量与分布,确保各 shard 的 chunk 数差异 ≤ 2
预分片后仍要禁用均衡器并锁定 chunk 分布
秒杀活动期间,任何 chunk 迁移都是灾难——它会触发跨网络复制、元数据更新、短暂不可写。必须在活动开始前停掉均衡器,并防止自动平衡干扰。
停用均衡器:
(注意:该命令需在
上执行,非 config server)
确认已停:
返回
,且
返回
避免误启:检查
中
文档的
字段是否为
活动结束后再手动调用
,并观察
确认迁移完成
真实部署中容易被忽略的三个硬约束
预分片不是配置完就一劳永逸。实际压测时常见失败点都卡在底层限制上:
每个 shard 最多支持约 32,000 个 chunk(受 WiredTiger cache 和元数据内存占用限制),预分片总数不能超过
只接受精确的分片键值,不能传范围;若用复合哈希键(如
),则必须按哈希后值 + 范围组合构造 split 点,极易出错
预分片操作必须在集合为空时执行,一旦有数据写入,
会报错
或静默失败
hotspot0.8 × chunkSize"20260407_001"sh.splitAt()sh.enableSharding("seckill")sh.shardCollection("seckill.orders", { "user_id": "hashed" })Long.MIN_VALUELong.MAX_VALUEsh.splitAt()sh.splitAt("seckill.orders", { "user_id": NumberLong("-4611686018427387904") })sh.status()sh.stopBalancer()mongossh.getBalancerState()falsesh.isBalancerRunning()falseconfig.settingsbalancerstoppedtruesh.startBalancer()config.changelogshard_count × 32000sh.splitAt(){ "user_id": "hashed", "item_id": 1 }sh.splitAt()ChunkTooBig