bigywater.tech
MongoDB · 19

WiredTiger 缓存:cacheSizeGB 压低时 eviction 怎么表现

把 cacheSizeGB 压到最低,数据比缓存大几倍时,WiredTiger 的 eviction 到底怎么表现?这篇装入 586 MB 数据,分别测工作集小于缓存、大于缓存、缓存加大到 2 GB,再用 8 线程写入压力看脏数据和应用线程淘汰。

256 MB
缓存最小值(0.25 GB),淘汰目标 80%、触发 95%,脏数据目标 5%、触发 20%
17 → 12,453
读 2 万次时读入缓存的页:工作集 60 MB 与 600 MB(缓存 256 MB)
514 ms
8 线程写入压力下 256 MB 缓存里应用线程淘汰的总耗时(2 GB 缓存只有 2 ms)

阈值

实测 · 本机执行输出
  maximum bytes configured = 268,435,456(256.0 MB)
  eviction threshold cache full target                  80.0%
  eviction threshold cache full trigger                 95.0%
  eviction threshold dirty target                        5.0%
  eviction threshold dirty trigger                      20.0%
  eviction threshold updates target                      2.5%
  eviction threshold updates trigger                    10.0%

serverStatus().wiredTiger.cache 直接给出阈值:占用超过 80% 后台线程开始淘汰,95% 时应用线程被迫帮忙;脏数据 5% / 20%,含更新的页 2.5% / 10%。

装入超过缓存的数据

实测 · 本机执行输出
  耗时 3.9 s(127,890 个/秒);每批 1000 个的延迟: 中位数 3.44 ms,p99 11.13 ms,最大 23.3 ms
  采样到的缓存占用峰值 205.6 MB(上限 256.0 MB),结束时 159.4 MB,脏数据 12.7 MB
  装入期间:读入缓存的页 15,写出的页 7,416,被淘汰的页(未修改 599 / 已修改 361),应用线程自己做的淘汰 0
  集合 dataSize 586.0 MB,storageSize 595.8 MB

586 MB 的数据装入 256 MB 的缓存,占用峰值 205.6 MB,没有越界;期间淘汰了未修改与已修改页,但全由后台线程完成。

工作集小于缓存

实测 · 本机执行输出
  工作集 60 MB / 缓存 256 MB: 20,000 次随机读,9,551 次/秒;中位数 0.10 ms,p99 0.18 ms,最大 0.6 ms;期间读入缓存的页 17(每次读 0.00 页)

只读 60 MB 的热数据时,预热后 2 万次随机读只读入 17 个页,中位数 0.10 ms。

工作集大于缓存

实测 · 本机执行输出
  工作集 600 MB / 缓存 256 MB: 20,000 次随机读,7,247 次/秒;中位数 0.14 ms,p99 0.24 ms,最大 1.8 ms;期间读入缓存的页 12,453(每次读 0.62 页)
  再来一轮: 20,000 次随机读,7,411 次/秒;中位数 0.14 ms,p99 0.23 ms,最大 5.3 ms;期间读入缓存的页 12,659(每次读 0.63 页)
  这一轮的淘汰:未修改页 14,151,已修改页 0

在全部 586 MB 里随机读,平均每次读要读入约 0.62 个页,同时淘汰别的页(读场景下淘汰的全是未修改页)。

加大缓存

实测 · 本机执行输出
  预热后缓存占用 652.3 MB(上限 2,048.0 MB)
  工作集 600 MB / 缓存 2 GB: 20,000 次随机读,8,994 次/秒;中位数 0.10 ms,p99 0.34 ms,最大 7.4 ms;期间读入缓存的页 0(每次读 0.00 页)

缓存 2 GB 时 600 MB 的工作集全部装得下,读入的页为 0,吞吐比 256 MB 时高约 24%——差距不大,因为操作系统缓存了文件。

单线程更新

实测 · 本机执行输出
  缓存 2 GB: 73,128 次更新(9,141 次/秒);中位数 0.10 ms,p99 0.24 ms,最大 6.4 ms;应用线程做的淘汰 0,应用线程淘汰耗时 0 ms,读入缓存的页 11,094
  缓存 256 MB: 67,702 次更新(8,463 次/秒);中位数 0.11 ms,p99 0.26 ms,最大 16.1 ms;应用线程做的淘汰 0,应用线程淘汰耗时 0 ms,读入缓存的页 50,196

单线程持续更新时,256 MB 与 2 GB 缓存的吞吐和延迟接近,应用线程都没有被迫淘汰,只是小缓存读入的页多得多。

写入压力

实测 · 本机执行输出
  cache2GB: 1,465,000 个文档(214 MB/s);每批延迟 中位数 36.66 ms,p99 93.98 ms,最大 151.5 ms;脏数据峰值 197.5 MB(上限 2,048.0 MB 的 10%);应用线程淘汰页 0,应用线程淘汰耗时 2 ms
  cache256MB: 1,363,000 个文档(200 MB/s);每批延迟 中位数 37.61 ms,p99 107.93 ms,最大 247.2 ms;脏数据峰值 26.1 MB(上限 256.0 MB 的 10%);应用线程淘汰页 0,应用线程淘汰耗时 514 ms

脏数据始终被封顶在缓存的约 10%,小缓存下脏数据峰值只有 26.1 MB。应用线程被迫淘汰的耗时从 2 ms 增加到 514 ms,但吞吐只低约 7%。本机是快速 SSD、操作系统文件缓存充裕,小缓存的代价主要体现在读入的页数和后台淘汰上,延迟变化不大;慢盘上结论可能不同。

分步演示

WiredTiger 缓存与淘汰
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / MongoDB 9.0.2(Homebrew 的 mongod、mongos)/ mongosh 2.12.0 / pymongo 4.18.3;每个实验在临时目录里起全新的 mongod(独立 dbpath,端口 9900 以上,只监听 127.0.0.1,WiredTiger 缓存 0.25 GB),需要副本集或分片集群时在本机起多个进程;命令输出来自真实的 mongosh,批量写入、计时与多线程用 pymongo;计时用 time.perf_counter,数字每次运行会有小幅抖动,ObjectId、时间戳等每次运行都不同。缓存用 --wiredTigerCacheSizeGB 0.25 和 2 控制(重启切换);数据是不可压缩的随机字节,避免压缩影响;缓存指标都来自 serverStatus().wiredTiger.cache
    • 没有验证:慢磁盘或冷操作系统缓存下的读延迟;缓存占用到 95% 以上、应用线程大量淘汰的极端情况(本实验中没有触发);cacheSizeGB 之外的内存占用(连接、排序、聚合);Linux 上的内存压力;checkpoint 对脏数据的影响;history store(WiredTigerHS.wt)对缓存的占用
    京ICP备2026067502号-1京公网安备11010502063487号