CMU 15-445 -- Buffer Pool - 03
- 引言
- Buffer Pools
- Buffer Pool Manager
- Lock 和 Latch 的区别
- PAGE TABLE 和 PAGE DIRECTORY
- Multiple Buffer Pools
- Prefetching
- Scan Sharing
- Buffer Pool Bypass
- OS Page Cache
- Buffer Replacement Policies
- LRU
- Clock
- LRU 与 Clock 的问题
- LRU-K
- Localization
- Priority Hints
- Dirty Pages
- Allocation Policies
- Other Memory Pools
- 小结
引言
本系列为 CMU 15-445 Fall 2022 Database Systems 数据库系统 [卡内基梅隆] 课程重点知识点摘录,附加个人拙见,同样借助CMU 15-445课程内容来完成MIT 6.830 lab内容。
Buffer Pools
上节中提到,DBMS 的磁盘管理模块主要解决两个问题:
-
如何使用磁盘文件来表示数据库的数据(元数据、索引、数据表等)
-
(本节)如何管理数据在内存与磁盘之间的移动
本节将讨论第二个问题。管理数据在内存与磁盘之间的移动又分为两个方面:空间控制(Spatial Control)和时间控制(Temporal Control)
Spatial Control
- 空间控制策略通过决定将 pages 写到磁盘的哪个位置,使得常常一起使用的 pages 能离得更近,从而提高 I/O 效率。
Temporal Control
- 时间控制策略通过决定何时将 pages 读入内存,写回磁盘,使得读写的次数最小,从而提高 I/O 效率。
整个 big picture 如下图所示:
本节的提纲如下:
- Buffer Pool Manager
- Replacement Policies
- Allocation Policies
- Other Memory Pools
Buffer Pool Manager
DBMS 启动时会从 OS 申请一片内存区域,即 Buffer Pool,并将这块区域划分成大小相同的 pages,为了与 disk pages 区别,通常称为 frames,当 DBMS 请求一个 disk page 时,它首先需要被复制到 Buffer Pool 的一个 frame 中,如下图所示:
同时 DBMS 会维护一个 page table,负责记录每个 page 在内存中的位置,以及是否被写过(Dirty Flag),是否被引用或引用计数(Pin/Reference Counter)等元信息,如下图所示:
当 page table 中的某 page 被引用时,会记录引用数(pin/reference),表示该 page 正在被使用,空间不够时不应该被移除;当被请求的 page 不在 page table 中时,DBMS 会先申请一个 latch(lock 的别名),表示该 entry 被占用,然后从 disk 中读取相关 page 到 buffer pool,释放 latch。以上两种过程如下图所示:
Lock 和 Latch 的区别
Locks:
- Locks通常是在DBMS的事务管理系统层面实现的,用于保护事务对数据库对象(如表、行、页)的独占访问。
- 在事务期间被持有
- 需要支持回滚加锁期间改变的内容
- 可以实现多种粒度的锁,例如行级锁、页级锁、表级锁等,以提供更细粒度的并发控制
- Locks的作用范围可以跨越多个事务,可以实现在事务之间的共享和排他访问控制
Latches:
- Latch通常是在DBMS的存储管理系统层面实现的,用于保护内部数据结构(如缓冲池、索引结构)的一致性。
- 在对内部数据结构操作期间持有
- 不需要支持回滚特性
- 简单的互斥锁实现
- Latch的作用范围通常限于单个线程或单个事务,用于保护数据结构在内部操作期间的一致性
总体而言,Locks和Latch在数据库管理系统中扮演着不同的角色。Locks用于提供更细粒度的并发控制和隔离,用于事务对数据库对象的独占访问。而Latch则用于保护内部数据结构的一致性,提供基本的互斥访问。
PAGE TABLE 和 PAGE DIRECTORY
page directory:
- 维护 page id 和 page 在磁盘上位置的映射关系
- 映射关系的改变必须记录到磁盘,这样DBMS才能再重启时不丢失映射关系。
page table:
- 维护 page id 和 page 在 buffer pool 中 frame 的映射关系
- 这是一个存在于内存中的数据结构,无需同步到磁盘上。
Multiple Buffer Pools
为了减少并发控制的开销以及利用数据访问的局部性,DBMS 可能在不同维度上维护多个 Buffer Pools:
- 多个 Buffer Pools 实例,相同的 page hash 到相同的实例上
- 每个 Database 分配一个 Buffer Pool
- 每种 Page 类型一个 Buffer Pool
Prefetching
DBMS 可以通过查询计划来预取 pages,如:
- Sequential Scans
- Index Scans
Scan Sharing
Scan Sharing 技术主要用在多个查询存在数据共用的情况。当两个查询 A, B 先后发生,B 发现自己有一部分数据与 A 共用,于是先共用 A 的 cursor,等 A 扫完后,再扫描自己还需要的其它数据。
Buffer Pool Bypass
当遇到大数据量的 Sequential Scan 时,如果将所需 pages 顺序存入 Buffer Pool,将造成后者的污染,因为这些 pages 通常只使用一次,而它们的进入将导致一些可能在未来更需要的 pages 被移除。因此一些 DBMS 做了相应的优化,在这种查询出现时,为它单独分配一块局部内存,将其对 Buffer Pool 的影响隔离。
OS Page Cache
大部分 disk operations 都是通过系统调用完成,通常系统会维护自身的数据缓存,这会导致一份数据分别在操作系统和 DMBS 中被缓存两次。大多数 DBMS 都会使用 (O_DIRECT) 来告诉 OS 不要缓存这些数据,除了 Postgres。
Buffer Replacement Policies
当 Buffer Pool 空间不足时,读入新的 pages 必然需要 DBMS 从已经在 Buffer Pool 中的 pages 选择一些移除,这个选择就由 Buffer Replacement Policies 负责完成。它的主要目标是:
- Correctness:操作过程中要保证脏数据同步到 disk
- Accuracy:尽量选择不常用的 pages 移除
- Speed:决策要迅速,每次移除 pages 都需要申请 latch,使用太久将使得并发度下降
- Meta-data overhead:决策所使用的元信息占用的量不能太大
LRU
维护每个 page 上一次被访问的时间戳,每次移除时间戳最早的 page。
Clock
Clock 是 LRU 的近似策略,它不需要每个 page 上次被访问的时间戳,而是为每个 page 保存一个 reference bit :
- 每当 page 被访问时,reference bit 设置为 1
- 每当需要移除 page 时,从上次访问的位置开始,按顺序轮询每个 page 的 reference bit,若该 bit 为 1,则重置为 0;若该 bit 为 0,则移除该 page
LRU 与 Clock 的问题
二者都容易被 sequential flooding 现象影响,从而导致最近被访问的 page 实际上却是最不可能需要的 page。为了解决这个问题,又提出了 LRU-K 策略。
sequential flooding:
- 一个执行顺序扫描的SQL查询,并且会顺序读取每一页(会顺序读取很多页)
- 这些批量顺序读取出来的页会污染buffer pool ,因为这些page大多只读取一次,然后再也不会被访问了
LRU-K
LRU-K 保存每个 page 的最后 K 次访问时间戳,利用这些时间戳来估计它们下次被访问的时间,通常 K 取 1 就能获得很好的效果。
Localization
DBMS 针对每个查询做出移除 pages 的限制,使得这种影响被控制在较小的范围内,类似 API 的 rate limit。
PostgreSQL(通常称为Postgres)维护着一个小的环形缓冲区,该缓冲区是每个查询私有的:
- 在PostgreSQL中,当执行一个查询时,通常涉及多个步骤,如解析、规划和执行查询。在执行阶段,PostgreSQL为每个查询分配一个小的环形缓冲区,也称为私有临时缓冲区。
- 这个私有的环形缓冲区是特定于查询的临时存储区域,用于保存查询执行过程中的中间结果、临时数据或其他所需的信息。它作为查询处理过程的工作区,允许高效地访问内存,而不会干扰其他并发查询。
- 这个私有环形缓冲区的存在,确保了每个查询都有自己的私有工作区,避免了不同查询之间的干扰或冲突。这种机制有助于提高查询的性能和并发处理能力,并确保查询的正确执行。
Priority Hints
有时候 DBMS 知道每个 page 在查询执行过程中的上下文信息,因此它可以根据这些信息判断一个 page 是否重要。
Dirty Pages
移除一个 dirty page 的成本要高于移除一般 page,因为前者需要写 disk,后者可以直接 drop,因此 DBMS 在移除 page 的时候也需要考虑到这部分的影响。除了直接在 Replacement Policies 中考虑,有的 DBMS 使用 Background Writing 的方式来处理。它们定期扫描 page table,发现 dirty page 就写入 disk,在 Replacement 发生时就无需考虑脏数据带来的问题。
Allocation Policies
Allocation Policies 指 DBMS 如何为不同的查询分配内存,可以分为 Global Policies 和 Local Policies。前者指同时考虑所有查询来分配内存,后者指为单个查询分配内存时不考虑其它查询的情况。
Other Memory Pools
除了存储 tuples 和 indexes,DBMS 还需要 Memory Pools 来存储其它数据,如:
- Sorting + Join Buffers
- Query Caches
- Maintenance Buffers
- Log Buffers
- Dictionary Caches
Maintenance Buffers(维护缓冲区)是PostgreSQL中的一个概念,用于处理后台写入和维护操作:
在PostgreSQL中,当进行后台写入操作(如自动保存点、检查点和日志刷新)或执行维护操作(如自动清理、VACUUM和索引维护)时,会使用维护缓冲区。
维护缓冲区是一部分内存空间,用于临时存储在后台写入和维护过程中需要的数据和元数据。它用于缓冲和管理这些操作中产生的数据页的更改,以便在适当的时机写入磁盘。
通过使用维护缓冲区,PostgreSQL可以将后台写入和维护操作与前台查询和更新操作隔离开来,以避免对数据库性能的直接影响。维护缓冲区的使用可以提高后台写入和维护操作的效率,并确保数据持久性和一致性。
维护缓冲区的大小可以通过配置参数进行调整,以平衡后台操作的性能需求和系统资源的利用。不同的工作负载和系统需求可能需要不同大小的维护缓冲区设置。
小结
数据库管理系统(DBMS)可以比操作系统更好地管理内存资源。
利用查询计划的语义可以做出更好的决策,包括页面驱逐、内存分配和预取操作。
本节内容对应教材的PDF