一.缓冲池

1.概念

缓冲池是数据库在内存中开辟的一块大型固定区域,本质是“内存缓存”,用于存储从磁盘读取的固定大小数据页(Page),避免查询时频繁访问磁盘,从而提升数据库运行效率。

通俗理解:缓冲池就像个放资料的架子,架子上有很多格子(帧),每个格子能放一份磁盘资料的副本。要找资料时,先看看架子上有没有,有就直接拿;没有就从仓库(磁盘)取一份放格子里。要是格子满了,就按规则清理一些旧资料,腾出空间放新的。

2.查询流程

想要读取某一页 → 查询页表

  • ✅ 命中:页已经在缓冲池的帧里,直接读内存,不用访问磁盘
  • ❌ 未命中:没有,发起磁盘 IO,把磁盘页读取到空闲帧

没有空闲帧:启用替换算法(LRU)淘汰不常用页面,腾出位置

3.缓冲池元数据管理

(1)页表

  • 页表 是缓冲池里的 “登记册”,记录内存中每个页的位置、状态,帮数据库快速找到缓冲池里的页。
  • 页目录 是单个数据页里的 “小目录”,记录页内每行数据的位置,方便在这一页里快速定位某行。一个管缓冲池里的页,一个管数据页里的行。

(2)脏标志

  • 页表脏标志: 如果缓冲池里的页被修改过,和磁盘上的原页不一样了,就会标记为 “脏”,之后需要把修改同步回磁盘,否则修改会丢。
    (通俗例子:比如我们借阅图书时,若在书上做了笔记(修改),归还时必须将笔记同步到图书馆的原版书,若没做笔记,直接归还即可。)

(3)固定引用计数器

  • 固定引用计数器: 记录这个页被多少地方在用,计数器不为 0 时,即使内存满了也不能被淘汰,避免数据被意外清理。
    (通俗例子:比如,一本图书正在被3个读者翻阅(计数器=3),图书馆管理员就不能把这本书收走;只有所有读者都看完(计数器=0),才能收走。)

二.锁与闩锁

1.锁(业务层面)

锁是数据库里保护数据的机制,比如你修改一行数据时,加锁防止别人同时改。

2.闩锁(数据库内部)

闩锁是更底层的 “门锁”,保护缓冲池里的页或数据结构,比如多个线程同时访问一个页,闩锁保证只有一个线程能操作,防止数据混乱。

(通俗理解:假设你去银行取钱,“锁” 就像你正在办理取款的那个账户,银行会给这个账户临时 “冻结”,防止别人同时从这个账户转账或取款,这是为了保护账户里的钱不冲突。“闩锁” 就像你正在用的那台 ATM 机,机器会临时 “占住”,确保同一时间只有你一个人能用这台机器操作,等你取完钱机器才会解锁给下个人用。锁管的是具体账户里的钱,闩锁管的是操作钱的机器或通道。)

三.为什么数据库不依赖操作系统的内存映射(mmap)?

1.先用大白话解释 mmap 是什么

mmap 相当于:你告诉操作系统 “把这个磁盘文件直接映射到我的内存里”。
程序只管读写内存地址;缺页、加载磁盘页、内存不够淘汰页面,全部由操作系统内核自动调度。
(通俗类比:你租了一间仓库(磁盘),雇了个管家(操作系统)。管家主动把常用货物搬到前厅(内存)。什么时候搬进来、什么时候把东西清走,全是管家说了算,你没法精准指挥。)

2、数据库为什么不愿意把权力交给操作系统(四大痛点)

① 脏页刷盘时机不受控(安全灾难)

数据库有严谨的日志机制(redo/undo),规定顺序:先写日志,再刷脏页到磁盘,保证崩溃恢复不出错。
如果用 mmap:操作系统随时可能偷偷把内存里的脏页刷回磁盘,顺序完全不可控。
一旦崩溃,磁盘上只写了修改后的数据,配套日志没写完 → 数据不一致,直接损坏。

② 页面淘汰策略数据库无法自定义(性能灾难)

mmap 下,内存紧张时操作系统想踢走哪个页面就踢哪个。
极端情况:操作系统把正在查询的热点页强行清出内存,频繁出现缺页中断,性能雪崩。
数据库自己的缓冲池:数据库自身决定淘汰谁;mmap:操作系统决定淘汰谁。

③ 无法精准控制 IO 类型

数据库能区分:预读、随机读、顺序读,针对性优化。
mmap 的 IO 由操作系统统一管理,数据库没法精细调控读写行为。

④ 无法感知页面状态

数据库缓冲池能清晰标记:脏页、引用计数、页面是否正在被线程持有。
mmap 模式下,操作系统在内核偷偷管理页面,数据库不知道页面什么时候被逐出、有没有被刷盘,失去所有监控手段。

四.页面替换策略

1.LRU(Least-Recently-Used)

为每个页面维护一个最近访问时间戳,当需要驱逐页面时,选择时间戳最旧(最久未被访问)的页面。
(通俗理解:纯 LRU 像书架,新书直接放最前面,最旧的书被挤到最后淘汰,要是一次性搬来一堆书,会把常用的书挤走。)

2.Clock(时钟算法)

Clock 算法给每页加访问位,指针循环扫描,未访问过的淘汰,访问过的置 0 再给次机会,实现简单且接近 LRU 效果。
(通俗理解:Clock 算法像给每本书贴个便利贴,看过就贴一张,没看过的直接拿走,看过的撕掉便利贴再留一轮,简单又能少丢常用书。)

3.改进 LRU

纯 LRU 对顺序扫描大表不友好,会把大量冷页顶走热点页。改进 LRU 会将链表分成热区和冷区,新页先入冷区,被多次访问才进热区,避免大表扫描冲击缓存。
(改进 LRU 把书架分成前后两格,新书先放后格,被经常拿才移到前格,避免一次性搬书挤走常用书。)

4.ARC自适应替换缓存

通俗理解:ARC 就像个会 “聪明调整” 的两层书架。第一层放最近刚看的 “新热书”,第二层放以前常看但最近没看的 “老热书”。如果新热书区的书总被很快淘汰,就缩小新热区、扩大老热区;如果老热书区的书又被翻出来看,就扩大老热区。它能根据你看书的习惯,自动变两层书架的大小,比固定分区的改进 LRU 更灵活,尽量不丢你可能还会看的书

5.本地化替换策略

通俗理解:本地化替换策略就像每个部门有自己的小书架,部门内的书优先在自己书架淘汰,不影响其他部门。数据库里如果按表或索引分区,每个分区有独立的缓存空间,替换页面时只在分区内选,避免一个大表扫描把其他表的热点页挤走,适合多租户或多业务场景。

6.优先级提示替换策略

通俗理解:优先级提示替换策略就像给书架上的书贴不同颜色的标签,红色标签是 “重要必留”,黄色是 “一般关注”,蓝色是 “可优先淘汰”。数据库会根据页面的重要性给它们贴标签,内存满时优先淘汰蓝色标签的页面,红色标签的即使不常访问也尽量保留。比如索引页可能贴红色,临时查询的页贴蓝色,确保关键数据不被轻易挤走。

五.脏数据和刷盘策略

1.脏数据

脏数据是被修改过,但还没写回磁盘的内存数据。

2.对脏数据的处理

  • 快速路径是 “紧急情况” 下的处理,比如缓冲池没空间了,要淘汰一个脏页时,必须马上把它刷到磁盘,不然新页没地方放,这时候刷盘会阻塞前台操作。
  • 慢速路径是 “日常维护”,后台线程空闲时,慢慢找些脏页批量刷盘,就像趁没人的时候整理东西,不耽误别人用地方,前台操作完全没感觉。

3.脏页与干净页的驱逐逻辑

  • 干净页:未被修改过的页面,驱逐时可直接丢弃,无需写回磁盘(快速路径)。
    • 通俗例子: 就像借阅的图书没有做任何笔记,归还时直接交给管理员即可,无需额外操作。
  • 脏页:被修改过的页面,驱逐时必须先写回磁盘,确保修改被持久化(慢速路径)。
    • 通俗例子: 就像你借阅的图书做了笔记,归还时必须将笔记同步到图书馆的原版书,才能完成归还。

六.磁盘I/O调度与O_DIRECT机制

1.磁盘 IO 调度

操作系统汇集 IO 请求,进行合并、重排序,减少机械盘磁头寻道耗时,优化磁盘访问效率。磁盘 IO 调度是操作系统决定磁盘读写顺序的方式,比如把零散的小 IO 合并成连续大 IO,减少磁头移动。

2.O_DIRECT

打开文件的标记,绕过操作系统页缓存,数据直接在磁盘与程序内存传输。
数据库更懂自己的 IO 需求,所以常用 O_DIRECT 机制,绕过操作系统的页缓存,直接把数据从磁盘读到数据库自己的缓冲池,避免操作系统缓存和数据库缓冲池存两份数据浪费内存,也让数据库能完全掌控 IO 流程。
优点:避免【操作系统缓存 + 数据库缓冲池】双重占用内存,数据库自主管控 IO。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐