下面把 检查点进程(checkpointer)工作流程 按一条完整逻辑链推一遍。


1. 起点:前台事务先把页改脏,不立刻写数据文件

PostgreSQL 采用 WAL 先行。事务修改数据时,先生成并持久化相应的 WAL,再允许数据页稍后落盘;数据页不会因为一次 UPDATE/INSERT 就立刻写回表文件。官方文档明确说,数据文件的更改只能在对应 WAL 已经 flush 到持久存储之后再写。(PostgreSQL)

所以运行时会自然形成两个状态:

  • WAL 持续顺序追加;
  • shared buffers 里的页不断被改脏,但还停留在内存里。

这就埋下了 checkpoint 的必要性:如果一直只写 WAL,不系统地把脏页推进到数据文件里,崩溃后就得从更久以前开始重放 WAL,恢复时间会越来越长。(PostgreSQL)


2. 平时谁在写页:不是只有 checkpointer

在 checkpoint 发生前,脏页并不是完全没人管:

  • backend 在某些情况下会自己写页;
  • background writer 会平时预写一部分脏页,尽量减少前台进程自己去写的概率;
  • checkpointer 则在 checkpoint 事件 上负责最终收口。PostgreSQL 官方也明确区分了单独的 checkpointer 统计和 bgwriter 统计。(PostgreSQL)

所以要先建立一个边界:

bgwriter 更像平时保洁,checkpointer 更像阶段性正式收口。 (PostgreSQL)


3. 什么时候轮到 checkpointer 出场

checkpointer 不是一直不停地“全量刷盘”,它主要在 checkpoint 被触发时主导流程。常见触发条件有三类:

3.1 时间到了

达到 checkpoint_timeout。官方文档说明,减小 checkpoint_timeout 会让 checkpoint 更频繁。(PostgreSQL)

3.2 WAL 增长压力到了

WAL 逼近 max_wal_size。官方文档明确说,减小 max_wal_size 也会让 checkpoint 更频繁。(PostgreSQL)

3.3 人工强制

执行 CHECKPOINT 命令,会立刻触发一次 checkpoint。(PostgreSQL)

所以这里的逻辑是:

事务持续改页
→ 脏页和 WAL 都在累积
→ 到了时间阈值、WAL 阈值,或管理员强制要求
→ 系统发起一次 checkpoint
→ checkpointer 开始接管这次流程

4. checkpointer 接管后,第一件事不是猛刷盘,而是“定义这次 checkpoint 的责任边界”

checkpoint 本质上是 WAL 序列上的一个恢复边界。官方定义是:在 checkpoint 点上,所有数据文件都已经更新以反映日志中的信息,并且会被 flush 到磁盘。(PostgreSQL)

所以 checkpointer 接到一次 checkpoint 后,逻辑上先要做的不是“把内存都刷掉”,而是:

确定本轮 checkpoint 要负责把哪些此前已经 WAL 化的脏页推进到数据文件。 (PostgreSQL)

你可以把它理解成:给本轮 checkpoint 先划一个责任圈。


5. 然后它开始推进脏页写回,但会尽量摊平,而不是一次性爆刷

官方 WAL 配置文档明确说明,checkpoint 期间的写入会被 spread over a period of time,目的就是避免把 I/O 集中打爆;checkpoint_completion_target 就是控制把这次 checkpoint 的写盘尽量摊到整个周期多大比例时间里的参数。(PostgreSQL)

所以 checkpointer 的实际工作方式更像这样:

触发 checkpoint
→ 识别本轮需要收口的脏页
→ 在一个目标时间窗口内持续写出
→ 尽量把 I/O 摊平
→ 争取在下一次 checkpoint 之前完成本轮

这一步的核心目的不是“慢”,而是“平滑”。(PostgreSQL)


6. 为什么它不能只 write,还必须 flush

因为 checkpoint 要成为真正的恢复锚点,光把页发给操作系统缓存还不够。
官方 CHECKPOINT 文档明确说,checkpoint 点要求 all data files will be flushed to disk。(PostgreSQL)

所以 checkpointer 的工作分两层理解:

  • 把脏页写回数据文件;
  • 再确保这些文件真正 flush 到稳定存储。

只有这样,checkpoint 才真正完成。(PostgreSQL)


7. checkpoint 完成后,系统得到了什么

得到的是一个 新的恢复起点
官方 WAL 配置文档说明,更频繁的 checkpoint 会让崩溃后的恢复更快,因为要 redo 的工作更少。(PostgreSQL)

所以 checkpoint 完成后的系统收益是:

  • 崩溃恢复不必从更久以前开始;
  • 只需要从更近的 checkpoint 开始重放 WAL;
  • 恢复时间通常更可控。(PostgreSQL)

这就是 checkpointer 最核心的价值:

把一部分本来会在崩溃恢复阶段做的工作,提前分摊到运行期。 (PostgreSQL)


8. 为什么 checkpoint 既重要又昂贵

因为它会同时带来两类成本。

8.1 刷脏页本身很贵

官方文档明确说,更频繁的 checkpoint 会增加 flushing dirty data pages more often 的成本。(PostgreSQL)

8.2 还可能放大 WAL

如果 full_page_writes 开启,每次 checkpoint 之后,某个页面第一次再被修改时,会把整页内容写进 WAL。官方文档明确指出,这是 checkpoint 频繁时另一项要考虑的成本。(PostgreSQL)

所以这里有个平衡:

  • checkpoint 太频繁:运行期 I/O 更重、WAL 更大,但崩溃恢复更快;
  • checkpoint 太稀疏:运行期更轻一些,但恢复更慢。(PostgreSQL)

9. 这条流程里,和其他进程怎么分工

把它们放在一条链里最好理解:

前台事务修改页
→ 页在 shared buffers 中变脏,WAL 先行
→ 平时 bgwriter 预写一部分脏页,backend 必要时也会自己写页
→ 达到 checkpoint 条件
→ checkpointer 接管这次正式收口
→ 把相关脏页平滑写回并 flush
→ 建立新的恢复边界

所以:

  • backend:为当前业务服务;
  • bgwriter:平时减少 backend 自写压力;
  • checkpointer:负责 checkpoint 这件事真正成立。(PostgreSQL)

10. 从监控角度,怎么判断它现在干得怎么样

当前 PostgreSQL 有独立的 pg_stat_checkpointer 视图,官方监控文档已明确列出。(PostgreSQL)

排查时通常重点看:

  • checkpoint 是按时间触发多,还是被请求/WAL 压力触发多;
  • write / sync 时间高不高;
  • checkpoint 是否过于频繁;
  • 日志里 checkpoint 信息是否显示刷盘量大、耗时高。(PostgreSQL)

11. 整条工作流程压缩版

直接记这条:

事务修改数据页
→ 页在 shared buffers 中变脏
→ WAL 先记录并满足持久化要求
→ 数据页暂时不必立即落盘
→ 到了 checkpoint_timeout、WAL 逼近 max_wal_size,或人工执行 CHECKPOINT
→ 系统触发 checkpoint
→ checkpointer 接管,确定本轮要收口的脏页范围
→ 在 checkpoint_completion_target 约束下尽量平滑地写出这些脏页
→ 再把相关数据文件 flush 到磁盘
→ checkpoint 完成,新的恢复边界建立
→ 以后如果崩溃,只需从这个更近的 checkpoint 开始恢复

这就是 检查点进程的完整工作流程。(PostgreSQL)


12. 面试版标准答案

checkpointer 的工作流程是:前台事务先通过 WAL 记录数据修改,数据页在 shared buffers 中变脏但不急着立刻写回;当达到 checkpoint_timeout、WAL 增长逼近 max_wal_size,或人工执行 CHECKPOINT 时,系统触发一次 checkpoint,由 checkpointer 接管这次正式收口。它会在一个时间窗口内尽量平滑地把本轮需要处理的脏页写回并 flush 到数据文件,最终建立新的恢复边界。这样 PostgreSQL 把一部分本来会留到崩溃恢复阶段处理的工作,提前分摊到运行期完成,从而在运行期开销、WAL 放大和恢复时间之间做平衡。(PostgreSQL)


了解 www.876873.xyz 的更多信息

订阅后即可通过电子邮件收到最新文章。

检查点进程的完整工作流程:等您坐沙发呢!

发表评论