【WiredTiger 内核】Cache 与 WT_REF:clean/dirty 计量与按需读页
内容提要
本文介绍WiredTiger存储引擎的缓存机制。缓存按需加载B-Tree页,WT_REF表示页是否在内存,WT_PAGE是已加载形态。缓存计量区分clean和dirty页,dirty页必须经reconcile转换后才能写盘。修改通过WT_UPDATE链和WT_INSERT跳表记录,session和cursor不计入缓存配额。与PostgreSQL不同,WT将内存布局与磁盘格式分离,并支持共享缓存模式。
延伸解读
内存与磁盘布局分离的代价
WiredTiger 将内存页布局与磁盘镜像显式分离,这与 PostgreSQL 的 shared buffers 不同。这种设计意味着脏页不能直接写盘,必须先经过 reconcile 转换为磁盘格式。因此,脏页的驱逐成本高于干净页,且内存中的修改链(WT_UPDATE、WT_INSERT)需要额外维护。理解这一点有助于解释为何脏页过多会触发强制驱逐,以及为何缓存压力可能影响写入性能。
cache_size 并非进程内存上限
WiredTiger 的 cache_size 只统计 B-Tree 数据及相关结构,不包含 session、cursor 以及临时缓冲区。因此,即使 cache_size 设置合理,连接数过多或 cursor 泄漏仍可能导致 RSS 增长。运维时需注意,cacheSizeGB 不是进程内存的硬性限制,监控内存使用时应结合连接数和游标数量,避免因资源耗尽导致性能问题。
与 PostgreSQL 的机制差异
WiredTiger 的修改模型与 PostgreSQL 不同:PG 在缓冲池中直接修改磁盘页格式的副本,而 WT 通过 update chain 和 insert skiplist 记录修改,并延迟到 reconcile 时合并。这意味着 WT 的读路径可能涉及链的遍历,而写路径需要额外的转换步骤。对于从 PG 迁移的开发者,不应假设“pin 住 buffer 再改字节”的模型适用于 WT,否则可能误解其并发控制和持久化行为。
Q&A
WiredTiger的缓存机制中,WT_REF和WT_PAGE分别代表什么?
WT_REF表示一个B-Tree页是否在内存中,而WT_PAGE是页已加载后的内存形态,包含解压后的磁盘镜像和访问/更新用的附属结构。
WiredTiger如何区分clean页和dirty页?它们在驱逐时有何不同?
Clean页与磁盘版本一致,可直接释放内存;Dirty页已修改,必须先经过reconcile转换为磁盘格式并写入存储,然后才能释放。
WiredTiger中页内修改是如何记录的?
修改通过WT_UPDATE链(针对已有键的更新)和WT_INSERT跳表(针对新键插入)记录在WT_PAGE_MODIFY结构中。删除用特殊的tombstone更新表示。
WiredTiger的缓存计量中,哪些内容计入cache_size?
计入cache_size的是B-Tree数据及相关内存结构,如索引、insert list、update chain。不计入dhandle、cursor、session以及读写磁盘镜像时临时分配的缓冲区。
WiredTiger与PostgreSQL的缓冲池在页格式上有何主要区别?
PostgreSQL的缓冲池大体保持磁盘页格式并添加少量元数据;而WiredTiger明确将内存布局与磁盘布局分离,内存页为并发访问优化,磁盘页为节省存储优化,两者转换通过reconcile完成。
WiredTiger的shared cache模式是如何工作的?
Shared cache模式允许多个数据库connection共享一个缓存池,由cache pool server根据压力(miss和eviction等待的加权)动态分配配额。MongoDB单实例单dbPath的部署通常不使用此模式。
WiredTiger中dirty页为什么不能直接写盘?
因为WiredTiger将内存布局与磁盘布局分离,dirty页在内存中可能包含未提交的更新链和跳表,必须先通过reconcile转换为磁盘格式(on-disk image),才能写入存储。