MongoDB从3.2版本开始默认使用WiredTiger作为存储引擎,它的内存管理机制和早期MMAPv1完全不同。WiredTiger在内部维护一块固定大小的cache,所有数据页从磁盘读入后都会先缓存在这块区域里,当空间不足时由eviction(驱逐)机制决定哪些页可以被淘汰出去。理解这套机制,是排查MongoDB内存相关性能问题的基础。

WiredTiger缓存的基本工作原理
WiredTiger的cache可以理解为一个页缓存池,默认大小为物理内存的50%减去1GB(在Linux容器环境中需要注意,MongoDB以容器可见内存计算,而不是宿主机内存)。每个数据页默认约32KB,包含B树的一个分支。读取数据时,引擎先查缓存,未命中则从磁盘加载;写入数据时,修改发生在内存中的页上,产生所谓的脏页(dirty page),脏页需要被刷写到磁盘才能释放。
缓存中的页大致分为两类:干净的页(内容和磁盘一致)和脏页(内容比磁盘新)。干净页的驱逐成本很低,直接丢弃即可,下次读取时重新加载;脏页驱逐前必须先执行刷写,把修改持久化到磁盘,这也是脏页驱逐往往成为性能瓶颈的原因。
WiredTiger内部维护了缓存的压力模型,用几个关键指标来衡量当前状态:bytes currently in the cache表示当前缓存占用量,tracked dirty bytes in the cache表示脏页占用量。当这些数值逼近上限时,驱逐机制就会介入。
eviction server和eviction worker的协作机制
WiredTiger驱逐工作由后台线程完成。核心是一个名为eviction server的线程,它定期评估缓存压力,当超过设定的阈值时,从B树中选择合适的页放入驱逐队列,多个eviction worker线程从队列中取出页并真正执行淘汰动作。理解这套分工有助于解读监控指标,比如pages evicted by application threads这一项,它统计的是应用线程亲自参与驱逐的次数,正常情况下应该接近于零。
当应用线程请求新页而缓存已满,且后台驱逐速度跟不上时,应用线程会被迫自己执行eviction,这就是所谓的application-level eviction。此时读写请求的延迟会明显上升,表现为P99延迟毛刺甚至整体卡顿。这在db.serverStatus()的wiredTiger.cache段中有对应指标可以观察:
// 查看缓存压力相关指标
db.serverStatus().wiredTiger.cache
{
"bytes currently in the cache" : 4123456789,
"maximum bytes configured" : 8589934592,
"tracked dirty bytes in the cache" : 623456789,
"pages evicted by application threads" : 1024,
"pages queued for eviction" : 8,
"pages requested from the cache" : 9876543,
"pages read into cache" : 123456,
"pages written from cache" : 234567
}
如果pages requested from the cache远大于pages read into cache,说明缓存命中率不错;反之则说明工作集超过了缓存容量,大量请求落到磁盘IO上。而pages evicted by application threads持续增长,就是驱逐压力传导到业务线程的直接证据。
三个关键阈值参数详解
WiredTiger通过几个参数控制驱逐的触发时机,它们之间存在递进关系。第一个是target size,即缓存的目标使用水位,默认为缓存总大小的80%。达到这个水位后,eviction server开始把页放入驱逐队列,属于温和的常态清理。
第二个是eviction trigger,也叫eviction_dirty_trigger层面的概念,当缓存使用超过约95%时,驱逐工作会变得激进,后台线程会占用更多CPU来尽快释放空间。第三个是eviction target,默认约78%,表示驱逐过程要把缓存用量降到的目标水位。
脏页还有独立的一套阈值。脏页占比默认超过5%时开始驱逐脏页,超过20%时进入激进模式。脏页驱逐需要刷盘,代价高,所以WiredTiger倾向于通过checkpoint批量处理脏页。checkpoint默认每60秒执行一次,会把所有脏页统一持久化。如果写入流量极大,脏页在两次checkpoint之间快速累积,驱逐压力就会显现。这些参数可以通过启动参数或命令动态调整:
# 启动时设置缓存大小为6GB
mongod --wiredTigerCacheSizeGB 6
# 通过命令调整驱逐触发阈值(百分比单位,需谨慎操作)
db.adminCommand({
setParameter: 1,
"wiredTigerEngineRuntimeConfig":
"eviction_dirty_trigger=8,eviction_trigger=92"
})
生产环境调优与常见问题排查
缓存大小的设置是第一要务。默认值在很多场景下并不合适:如果MongoDB与业务应用同机部署,默认占用一半内存会挤占应用;如果机器上还有其他缓存型服务,也需要手动下调。一个常见的经验法则是把wiredTigerCacheSizeGB设置为可用内存的40%到60%,并保证文件系统缓存至少有2GB以上,因为磁盘读操作依赖操作系统页缓存来提升性能。
缓存并不是越大越好。如果设置得过大,WiredTiger内部的一些计算会基于缓存大小伸缩,极端情况下反而会增加内存碎片压力;同时留给操作系统的页缓存不足,会导致读放大问题。判断缓存是否够用的核心方法是对比工作集大小,可以用db.stats()中的data size与缓存命中指标交叉验证。
遇到驱逐引发的延迟抖动时,建议按以下顺序排查:先看pages evicted by application threads是否增长,确认压力来源;再看tracked dirty bytes in the cache是否长期高于5%上限,判断是写驱动还是读驱动的问题。如果是写驱动,考虑降低单次写入量、批量合并写入,或者适当提高脏页触发阈值;如果是读驱动且工作集确实大于缓存,则应该扩容内存或利用索引缩小扫描范围,减少不必要的页加载。
另外要注意MongoDB 4.4之后版本对默认配置做了不少优化,包括更快的历史准确性处理和更积极的表级统计,许多旧文章推荐的激进调参在新版本上未必适用。升级或调参前,务必先在测试环境观察serverStatus指标变化,用数据说话,而不是盲目套用参数。
MongoDBMongoDB缓存驱逐cache eviction修改时间:2026-09-15 03:24:32