嵌入数组超50项或文档超2MB会显著降低WiredTiger缓存命中率,因大文档被切散至多个非连续4KB页,查询/更新时强制加载/重写整文档,挤占缓存空间并激增脏页;此时应拆分至独立集合并建合适索引。

嵌入数组超过50项会显著拉低WiredTiger缓存命中率
WiredTiger按4KB页管理缓存,单个文档若含超长数组(如comments、history),实际存储时可能被切散到多个非连续页。查询时哪怕只读title字段,整个数组仍要从磁盘/缓存加载——因为投影无法跳过嵌入结构内部的未声明字段。这直接导致缓存页“装了大量冷数据”,挤占真正高频字段的空间。
更新操作触发整文档重写,脏页激增
对大数组末尾追加或修改一条记录(比如push或set数组某索引),WiredTiger通常需重写整个文档。这会把关联的多个缓存页标记为脏页(tracked dirty bytes in the cache升高),加速其被淘汰。尤其在高并发写场景下,这种开销会放大成明显的CPU与I/O压力。
2MB是嵌入数组的隐性分水岭
当数组平均长度 > 50 条,或文档估算大小 > 2MB(按公式 ∑(字段名长度 + 字段值大小 + 10) × 1.2 计算),就该考虑拆出独立集合。这不是硬限制,而是性能拐点:BSON单文档上限16MB,但远未到上限时,局部性已严重劣化。拆分后配合tenantId或orderId建索引,既能保持查询效率,又避免单点膨胀。
分片环境下shard key与嵌入结构冲突风险
若把含长数组的文档作为分片依据(比如用_id分片),数组增长会导致文档不断变大,引发频繁的chunk迁移;若用数组外字段做shard key(如userId),又可能让同一用户的多条长数组记录集中在少数分片,造成热点。这种结构性矛盾,在设计初期几乎无法靠后期调优解决。
真正难处理的不是“能不能塞”,而是“塞进去之后,哪次查询、哪个更新、哪台机器先扛不住”。数组长度只是表象,背后是缓存行为、IO模式和分片拓扑三者的耦合问题。


















