wiredtiger缓存占满并非因gridfs chunk数据进入,而是元数据、索引页堆积及脏页积压所致;其cachesizegb不缓存bindata流,真正瓶颈是fs.files文档锁竞争、journal高频刷盘和事务槽位耗尽。

WiredTiger缓存占满不是因为GridFS数据进来了
WiredTiger的cacheSizeGB根本不会缓存GridFS的chunk数据——fs.chunks里的BinData是流式读写,绕过page cache。你看到“bytes currently in the cache”飙升,其实是元数据和索引页在疯狂堆积,不是文件内容被缓存了。
真正被塞满的是事务槽位和脏页缓冲区:每次上传都触发批量insertMany到fs.chunks + 单条insertOne到fs.files,每批操作都生成journal、标记脏页、占用一个事务slot。默认concurrentTransactions.write: 128在几百QPS下几秒就耗尽,后续请求排队,脏页持续积压,tracked dirty bytes in the cache很快冲过1GB。
fs.files文档锁和journal刷盘才是缓存假高真堵的根源
并发写同名文件时,fs.files集合的文档级锁会争抢——尤其当业务用filename做去重,反复覆盖同一文件,所有写都在等同一个_id的锁释放。这导致事务无法及时提交,脏页不能刷盘,缓存里堆着大量“待落盘但卡住”的修改。
- 每批chunks插入都会强制
fsyncjournal,SSD也扛不住毫秒级高频刷盘 -
storage.wiredTiger.engineConfig.journalCompressor: none(默认)会让journal体积膨胀,进一步拖慢刷盘速度 - OS层journal缓存不足时,WiredTiger会把压力转嫁到WT cache,表现为“pages evicted by age”异常高——不是缓存不够,是它被迫提前淘汰还没来得及刷的页
监控时盯错字段会误判问题本质
别只看"bytes currently in the cache",这个值高≠缓存有效。GridFS场景下必须查这三个字段:
-
"tracked dirty bytes in the cache"> 1GB → 脏页积压,journal或事务配置没跟上 -
"pages evicted by age"远高于"pages evicted by the application"→ 页面老化太快,chunk刚写入就被踢出,说明缓存分配策略错配 -
"unmodified pages evicted"≈"modified pages evicted"→ fs.chunks数据基本不被读,缓存该优先留给fs.files索引或业务集合
执行命令:db.runCommand({serverStatus: 1}).wiredTiger.cache
调大cacheSizeGB反而可能恶化写延迟
把cacheSizeGB从8GB拉到24GB,若不同时调整其他参数,只会让脏页堆积更隐蔽、事务排队更长、journal压力更大。实测中,单改这一项后netOut下降但opLatencies.commands.p99翻倍——缓存变大了,但写不出去。
- 必须同步设
storage.wiredTiger.collectionConfig.blockCompressor: zstd(MongoDB 6.0+),降低chunk写入字节数 - 必须改
storage.wiredTiger.engineConfig.journalCompressor: zlib,journal不支持zstd - OS要留足16GB+给文件系统缓存,journal和mmap页靠它,不是靠WT cache
最易忽略的一点:fs.files索引膨胀本身就会吃掉大量cache空间,而它对GridFS写入毫无加速作用——删掉冗余索引比调cacheSizeGB见效更快。











