06 缓存管理
缓冲区结构

- 缓冲区由一个个 frame 构成,每个 frame 刚好可以容纳一个页
- 为计算机程序隐藏了“并非所有数据都存在内存”这一事实
frame 参数
- dirty:frame 中的块是否已经被修改
- pin-count:frame 的块的已经被请求且还未释放的计数,即当前的用户数
*others- latch: 是否加锁
缓冲区管理过程
- 当请求块且块不在缓冲区时:
- 选择一个用于替换的 frame(空或非空)
- 如果 frame 是 dirty 的(部分块被修改过),frame 的内容写回磁盘
- 将请求块写入被选择的frame
- pin the block(将该 frame 的 pin-count 自增1),并返回其地址
- 当释放块时
- 请求者 unpin the block (将该 frame 的 pin-count 自减 1)
- 请求者必须声明该块是否被修改过(用frame->dirty)
缓冲区置换算法
opt
在实验中作为算法性能上界加以对比
LRU
- 维护一个链表,并按照最近访问时间排序
- 基于时间局部性 (temporal locality) 假设:越是最近访问的在未来被访问的概率越高
- 总是替换 lru 端的 frame
- 优点
- 适用于满足时间局部性的场景(多次重复请求同一页)
- 选取被替换 frame 的时间复杂度是 o (1)
- 缺点
- 缓存污染 (sequential flooding): lru + repeated sequential scans
- 缓存污染问题主要关注数据的访问频次
- lru链表维护代价昂贵:修改链表耗时
- 如果访问不满足时间局部性,则性能较差
- 只考虑最近一次访问,忽略了访问频率
- 缓存污染 (sequential flooding): lru + repeated sequential scans
lru-k

- 需要维护两个 lru 链表
- 一个存储访问小于 k 次的
- 一个存储访问大于 k 次的
- 需要替换 frame 时,按照 lru 策略优先置换小于 k 次的链表
- 实验表明,k并非越大越好,lru-2性能较好
- 缺点:需要额外记录访问次数
2q
和 lru-2 类似,但是访问 1 次的队列采用 fifo,而不是 lru
second-chance fifo
- 所有 frame 组成 fifo 链表,每个 frame 附加一个 bit 位,初始为0。
- fi, fo 分别为队尾和队头
- 每 frame 被使用时,将 bit 设置为 0
- 当 fo 页第一次被选中置换时置为 1,并移到 fi 端。只有 bit 位为 1 的 fo 端的页才被选中置换
- 相当于每个 frame 给了两次置换机会,避免高频访问但最近一轮没有被访问的 frame 被置换出 buffer
clock
- 把 second-chance fifo 组织成环形
- current 指针指向当前 frame
- 每个 frame 有一个 referenced 位,初始为1
- 当需要置换页时
- 若 pin-count>0,current 增加1
- 若 referenced=1 ,则关闭它(=0)并增加 current
- 若 pin-count=0 并且 referenced 关闭(=0),则替换该 frame,同时 current 加1
置换过程才会更新 current 指针,访问命中过程不改变 current 指针
ssd 上的置换算法

为什么不使用 os 的缓冲管理
- dbms 能预测访问模式 (access pattern)
- 可以使用更专门的缓冲区替换策略
- 有利于 pre-fetch 策略的有效使用
- dbms 需要强制写回磁盘能力
- os 的缓冲写回一般通过记录写请求来实现(来自不同应用),实际的磁盘修改推迟,因此不能保证写顺序
缓冲区管理的实现
block vs. disk file

- 文件存储在磁盘上的物理形式是 bits/bytes
- block 是由 os 或 dbms 软件对文件所做的抽象,这一抽象是通过控制数据在文件中的起止 offset 来实现的
buffer vs. disk file

- frame 是 buffer 中的单位;page/block 是内存/磁盘的单位
- 通常 frame 和 page 大小相同
buffer 存储结构
#define framesize 4096
struct bframe
{
char field[framesize];
}
#define bufsize 1024 // frame数目
bframe buf[bufsize]; //也可以是用户配置的值
- buffer 是一个存放 frame 的数组
在 buffer 中查找 frame
- 读 block 时,根据
page_id确定 buffer 中是否已经存在 frame - 写 block 时,根据
frame_id找到文件对应的page_id
维护 buffer 中所有 frame 的维护信息(buffer control blocks):
struct bcb
{
bcb();
int page_id;
int frame_id;
int count;
int time;
/* int latch; */
int dirty;
bcb * next;
};
用哈希表建立 frame-page 之间的索引:
bcb htable[buffersize] //page 2 frame
int htable[buffersize] //frame 2 page
buffer manager 的基本功能
fixpage(int page_id):将对应 page_id 的 page 读入到 buffer 中- 如果 buffer 已满,则需要执行替换算法
fixnewpage()- 在插入数据时申请一个新 page 并将其放在 buffer 中
selectvictim()- 选择换出的 frame_id
findframe(int page_id):查找给定的 page 是否已经在某个 frame 中了setdirty(int frame_id)
数据库文件组成
- 数据文件
insert_record-->fixnewpage创建首块
- 系统目录文件
create_table-->fixnewpage创建首块
存储管理器
从磁盘中存取物理数据,为 buffer manager 提供 page 抽象

浙公网安备 33010602011771号