1. 简介
缓存管理器是数据库管理系统(DBMS)中负责管理内存中page并处理文件和索引管理器的page请求的组件。由于内存空间有限,我们不能将所有page存储在缓存池中。因此,缓存管理器需要制定替换策略,当空间填满时选择哪些page进行替换。缓存管理器与磁盘空间管理器进行通信,执行所需的磁盘操作。
2. 缓存池
缓存池是通过将内存划分为可以放置page的frame来创建的。一个缓存frame可以容纳与一个page相同的数据量。为了有效地跟踪frame,缓存管理器会在内存中分配额外空间用于元数据表。
元数据表追踪以下信息:
- frame ID:唯一关联到内存地址
- page ID:确定frame当前包含哪个page
- 脏位:验证page是否被修改
- 固定计数:跟踪当前使用page的请求者数量
3. 处理page请求
当请求的page存在于内存中时,增加page的固定计数,并返回page的内存地址。如果page不在缓存池中且还有空间,找到下一个空frame并将page读入该frame,将page的固定计数设置为1并返回page的内存地址。如果没有空frame,则使用替换策略决定替换哪个page。
替换策略依赖于page访问模式,选择最优策略以减少I/O开销。如果被替换的page设置了脏位,需要将page写回磁盘以确保更新持久化。一旦请求者完成工作,需要通知缓存管理器减少使用page的固定计数。
4. LRU替换策略
LRU(最近最少使用)是一种常用的替换策略。当需要在已满的缓存池中读入新page时,替换最近最少使用且未固定的page。为了跟踪page使用情况,元数据表中添加了“最后使用”列,记录page固定计数减少的最新时间。实现LRU通常成本较高,Clock策略提供了一种高效近似LRU的实现。
5. Clock替换策略
Clock策略通过使用元数据表中的引用位列和时钟指针变量来高效地近似LRU。算法将元数据表视为一个循环列表,并设置时钟指针为当前考虑的frame。策略如下:
- 迭代表中的frame,跳过固定的page,找到第一个引用位为0的未固定frame。
- 每次迭代,如果当前frame的引用位为1,则将其设为0并移动时钟指针到下一个frame。
- 到达引用位为0的frame时,替换现有page(如果脏位为1,则写回磁盘并设为0),读入新page,设置frame的引用位为1,并移动时钟指针到下一个frame。
6. MRU替换策略
MRU(最近最常使用)策略在替换page时,选择最近最常使用的未固定page。尽管看似反直觉,但在某些访问模式下,如重复的顺序扫描,MRU的性能优于LRU。
7. page请求与缓存命中
在处理page请求时,如果请求的page不在缓存池中,需要选择一个未固定的frame进行替换。如果frame为脏,则需要将当前page写回磁盘并标记为干净,读入请求的page,并返回其地址。如果可以预测请求(如顺序扫描),可以提前读取多个page以优化性能。
8. 脏页处理
缓存管理器通过page上的脏位标记发现脏页。当page被修改时,设置脏位。当需要替换脏页时,缓存管理器会将其写回磁盘。
9. 缓存池状态
缓存池是DBMS服务器启动时分配的大范围内存(MB到GB)。缓存管理器的元数据是DBMS服务器启动时分配的小数组。在内存中保持一个基于PageId的哈希表索引,以便快速查找page。
10. 替换策略的选择
替换策略的选择对I/O次数影响很大,取决于访问模式。LRU适用于随机访问,而MRU适用于重复顺序访问。现代DBMS通常使用混合策略以适应不同的工作负载。例如,PostgreSQL使用Clock策略。
总结
缓存管理器提供了一种间接映射,将磁盘page ID映射到内存地址,确保每个请求的page在内存中被固定以进行操作,并在使用完成后解除固定。通过替换不太可能被引用的page和预取可能被引用的page,缓存管理器试图最小化缓存未命中次数。
要点回顾
- 固定计数和脏位:何时设置/取消设置?由系统的哪一层处理?
- LRU、MRU和Clock:能够手动运行每个策略。
- 顺序扫描和缓存未命中的行为。