土法炼钢兴趣小组的算法知识备份

【WiredTiger 内核】Cache 与 WT_REF:clean/dirty 计量与按需读页

文章导航

分类入口
databasestorage
标签入口
#wiredtiger#cache#wt-ref#wt-page#dirty-page#update-chain#eviction#mongodb

目录

第 2 篇 把操作收进 Session / Cursor;真正占内存配额、决定能否继续写的,是 Cache。Architecture Guide Cache 开篇三句已经给出本篇坐标系:

  1. Cache 保存最近访问或修改的数据副本;B-Tree 页按需读入。
  2. 空间不足时由 Eviction 移出不需要的页。
  3. 更新改的是 cache 中的页,经 Checkpoint 或 Eviction 异步刷到存储。

本文钉住:WT_CACHE / WT_REF / WT_PAGE 结构、clean 与 dirty 计量、页内修改如何挂链——不展开 eviction 线程调度(第 4 篇)与 reconcile 写镜像(第 6 篇)。

本文是「WiredTiger 内核」系列第 3 篇(共 17 篇)。→ 系列目录

先修:第 1–2 篇。续读:第 4 篇 Eviction;第 5 篇 update chain。

版本锚定:Architecture Guide Version 12.0.0 / develop Cache;源码 mongodb-8.0src/include/btmem.hcache.hsrc/cache/。Guide 声明不与发版锁步。


一、为什么有两套页布局

Guide 写明:cache 中的页布局为多线程并发访问优化;磁盘上的页布局为节省存储优化。因此读页、写页都要在 in-memory 与 on-storage 表示之间转换——写路径上的转换就是 reconciliation(第 6 篇)。

这与 PostgreSQL shared buffers「页在池中大体保持磁盘页格式再加少量元数据」的直觉不同:WT 明确把「内存形态」和「磁盘镜像」拆开。代价是 dirty 页不能「直接丢掉」或「原样 DMA 出去」,必须先收成 on-disk image。


二、WT_CACHE、WT_REF、WT_PAGE

2.1 全局 cache

2.2 按需读页

打开文件时加载 B-Tree 根页以及第一层 internal 页。查找从根下行;遇到不在内存的页,把 address cookie 交给 Block Manager,取回块,必要时解密/解压,再分配索引结构以支持键的二分查找。

flowchart LR
  root["Root WT_REF"] --> intl["Internal WT_PAGE"]
  intl --> ref2["Child WT_REF"]
  ref2 -->|"not in cache"| load["Block Manager read"]
  load --> leaf["Leaf WT_PAGE"]
  ref2 -->|"in cache"| leaf

2.3 WT_REF 与 WT_PAGE

结构 角色
WT_REF 指向「cache 中的页」或「尚未加载的页」;B-Tree 打开时根 WT_REF 挂在 WT_BTREE
WT_PAGE 已加载页:含解压后的 on-disk image 缓冲,以及访问/更新用的附属结构
Internal 页 子页数组为 WT_REF
Row-store leaf WT_ROW 数组(页上 KV);支持二分查找
VL column leaf WT_COL + WT_COL_RLE

几乎所有在这些结构上的操作设计为 lock-free,以支撑高并发 cache 访问(Guide)。

2.4 什么计入 cache_size

计入:B-Tree 数据及相关内存结构(索引、insert list、update chain)。

不计入:dhandle、cursor、session;以及读写磁盘镜像时临时分配、转换用完即释放的缓冲区。

运维含义:cacheSizeGB 不是进程内存上限;连接数、cursor 泄漏仍可推高 RSS(第 15 篇)。


三、clean / dirty 与第一次修改

Guide 定义:

用量上同时跟踪总量、clean、dirty。总量过高或 dirty 过多时触发 Eviction(第 4 篇)。

驱逐差异(本篇只记结论):

页状态 离开 cache 前要做什么
Clean 直接释放内存;磁盘副本不变
Dirty 先 reconcile(转 on-disk 格式)再写存储,然后才能释放

3.1 WT_PAGE_MODIFY

叶页上某条目首次插入或修改时,为该 WT_PAGE 分配 WT_PAGE_MODIFY

Row-store leaf:

Column-store leaf:用一对 skiplist 分别跟踪 append 与 update(本系列默认 MongoDB row-store 路径,列存仅作对照)。

flowchart TB
  page["WT_PAGE leaf"] --> mod["WT_PAGE_MODIFY"]
  mod --> upd["WT_UPDATE chains<br/>per on-page key"]
  mod --> ins["WT_INSERT skiplists<br/>gaps between keys"]

未提交更新挂在链上、不进磁盘镜像——与第 08 篇「提交后才进入可持久化历史」一致;把链收成磁盘最新值 + HS 旧值的步骤是 reconciliation。


四、Shared cache(边界)

Guide 描述:同一进程多个数据库 connection 时可启用 shared cache,由 cache pool server 按 pressure(miss 与 eviction 等待的加权)在 connection 间动态划分配额。MongoDB 单实例单 dbPath 的常见部署不依赖该模式;本系列正文默认单 connection 固定 cache_size。需要多租户同进程时再回查 Shared caches 节。


五、与 PG 缓冲池对照(机制,非排名)

维度 PostgreSQL Buffer Pool(站内口径) WiredTiger Cache
页格式 大体保持磁盘页 + 描述符 内存布局与磁盘布局分离
脏页写出 检查点 / 后台写等 Eviction 或 Checkpoint 路径上 reconcile
版本 堆元组 / 提示位等 update chain + 事后 HS
计量 shared_buffers 等 clean/dirty/updates 分触发(第 4 篇)

对照目的:读过 PG 的人不要假设「pin 住 buffer 再改字节」等于 WT 的修改模型。深度对照见第 16 篇。


六、争论、间隙与开放问题

谱系(短): 页式缓存是数据库经典组件;WT 的分叉点是 in-memory 页 + update chainon-disk image 显式分离,并与 Durable History 联动(第 08 篇)。

工程间隙: Guide 中的默认 cache_size 与 MongoDB 默认「一半 RAM」不同——排障时以 MongoDB 实际配置为准。本篇无本地 db.serverStatus() 实测。

开放问题: HS 页与用户热页是否公平争用同一 WT_CACHE——第 4、15 篇给机制入口;可复现阈值需实测,本篇不下性能结论。


七、收束

  1. Cache 按需加载 B-Tree 页;WT_REF 表示「在或不在内存」,WT_PAGE 是已加载形态。
  2. 计量分 clean / dirty;dirty 不能直接扔,必须 reconcile。
  3. 修改挂在 WT_UPDATE / WT_INSERT 上;session/cursor 不计 cache_size

下一篇:Eviction——谁在什么阈值下把页赶出 cache,以及 dirty eviction 如何强制走 reconcile 与 History Store。


参考资料

规范 / 官方文档

源码

站内


上一篇Connection / Session / Cursor
下一篇Eviction

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-07-22 · database / storage

【WiredTiger 内核】Eviction:脏页必须先 reconcile

拆解 WiredTiger Eviction 的 server/worker/队列、target/trigger 阈值,以及 dirty eviction 经 reconciliation 把最新值写入用户表、旧版本写入 History Store;说明应用线程被迫协助驱逐的条件。


By .