PostgreSQL:数据还在内存里,为什么断电也不怕?

前两篇文章所讲,都是 PostgreSQL 帮你做了什么,这篇所说,则是要搞懂 它是怎么做到 断电不丢数据、崩溃能恢复

前面讲Redis中的 AOF 和 RDB,一个 每次操作都记日志,一个 定期拍快照, PostgreSQL 的持久化策略和Redis有相似的影子

先看核心矛盾:读取速度

数据库的全部数据最终存在磁盘上,但如果每次查询都去读磁盘,慢的一批,所以 PostgreSQL在内存中维护了一块缓冲区, Shared Buffers,负责把磁盘数据搬一份到内存里操作

但是磁盘和内存之间搬运数据,并非一行一行搬,这样太慢了

知道一下基础概念:

  • 页:数据库读写数据的基本单位,默认是 8KB。从磁盘读取时,通常按整个页加载
  • 脏页(dirty page):指的是被改动过的页面,如 update

关键机制:

  1. 读操作:先在 Shared Buffers 里找目标页面,找到了(缓存命中)->直接读,没找着 -> 从磁盘加载整个 8KB 的页面到缓冲区
  2. 写操作:修改的不是磁盘文件,而是内存中的页面副本。被改过的页面标记为 脏页,内容和磁盘上的不一致了
  3. 驱逐:缓冲区满了,需要腾位置。干净页直接丢,脏页必须先写回磁盘再丢

那都知道,断电了咋办, 脏页 还没有回到数据库里,那不直接丢失了吗,那就轮到 WAL机制 登场了

核心作用就是:预写日志

Write-Ahead 的关键不是每次在内存中改页之前都要刷日志,而是数据页准备写回磁盘之前,相关的 WAL 必须已经先落盘

那为什么不直接写数据文件里面?

因为顺序

那么在 commit 的时候到底发生了什么?

注意:默认 synchronous_commit = on 时,commit 只保证 WAL 落盘,不保证脏页落盘。因为即使脏页丢失了,也可以从 WAL 日志重放所有修改,恢复出脏页的内容

铁律:正常的同步提交下,只要 WAL 落盘了,已经提交的数据就不会因为脏页还没刷盘而丢失。脏页没刷盘 ≠ 数据丢失,WAL 才是持久化的最后防线

Checkpoint(检查点)

脏页不随 commit 落盘,那什么时候写回磁盘?答案是 Checkpoint

Checkpoint 做两件事:

  1. 把 Checkpoint 开始时已经脏的页面逐步刷回磁盘
  2. 记录一个检查点位置------从恢复角度说,这个点之前的 WAL 不再需要;如果没有归档、复制等其他需求,之后就可以回收

链条:Checkpoint 触发 → 脏页刷盘 → 旧 WAL 可回收 → 磁盘空间释放

Checkpoint 间隔的权衡

核心矛盾:Checkpoint 间隔 = IO 平稳性 vs 恢复速度的跷跷板

如果间隔太短,Checkpoint 还没多久就来一次。脏页需要频繁刷盘,IO 容易争用;但断电时要重放的 WAL 比较少,恢复会快一点。

如果间隔太长,平时刷盘可以拉得更开,不那么容易突然顶满 IO。但一旦崩溃,需要重放更多 WAL,恢复就慢;旧 WAL 也得留得更久,占用的空间自然更大。

Spread Checkpoint(分散检查点)

解决 Checkpoint 集中刷盘的 IO 风暴问题

通过参数 checkpoint_completion_target(默认 0.9)控制:用 90% 的 Checkpoint 间隔时间来慢慢刷脏页

崩溃恢复

断电后,PostgreSQL 不是从头重放所有 WAL,而是从最近一次 Checkpoint 位置开始重放

原因:Checkpoint 之前的数据已经安全落盘了,不需要重放

Torn Page(页撕裂)与 full_page_writes

问题:断电恰好发生在数据页写了一半(8KB 只写了 4KB),页的前半是新数据,后半是旧数据,整个页自相矛盾。WAL 重放在这个残页上做增量修改,结果也是垃圾数据

解决方案:full_page_writes = on(默认开启)

在每次 Checkpoint 之后,某个数据页第一次被修改时,把完整 8KB 页面内容塞进 WAL 日志

text 复制代码
Checkpoint 后第一次修改某页:
  WAL 记录:完整 8KB 页面快照 + 本次修改操作

同一页后续修改:
  WAL 记录:只记修改操作(几十字节)

恢复时遇到残页:用 WAL 中保存的完整 8KB 镜像直接覆盖,再继续重放后续 WAL

代价:WAL 体积膨胀(WAL 写放大),尤其 Checkpoint 刚结束时大量页面都是"第一次修改"

最后

现在可以把这件事串起来了:数据先在 Shared Buffers 里改,先把修改记进 WAL;提交时,WAL 先落盘;脏页则不用着急,等 Checkpoint 慢慢刷回数据文件。

所以 PostgreSQL 不是靠"每改一次就立刻写磁盘"保证安全的。它把速度和安全拆开了:内存负责快,WAL 负责兜底,Checkpoint 负责把之前欠下的磁盘写入慢慢还掉。

断电、崩溃、甚至刚好写到一半,都还有 WAL、Checkpoint 和 full_page_writes 一层层接住。这也是数据库能在"数据很多、还总有人同时改"的情况下,仍然敢把数据放在磁盘里的原因。

相关推荐
周杰伦的稻香18 小时前
PostgreSQL中的METHOD(认证方法)
数据库·postgresql
Zhu75819 小时前
对docker环境的postgresql数据库做快速初始化
数据库·docker·postgresql
l1t2 天前
测试用rust重写的postgresql: pgrust
开发语言·postgresql·rust
IvorySQL3 天前
云环境下PostgreSQL的Cgroup内存管理实践
java·数据库·postgresql
IvorySQL3 天前
PG 日报|SQL/PGQ 图查询基于联接重写机制实现
数据库·人工智能·sql·postgresql·区块链·ivorysql
IvorySQL3 天前
深度拆解 IvorySQL 去 O 核心解决方案
数据库·人工智能·postgresql
大猫会长4 天前
mac安装PostgreSQL18笔记
postgresql
IvorySQL4 天前
PG 日报|新增 VACUUM 全维度统计信息补丁迭代
数据库·人工智能·postgresql·ivorysql
运维老郭4 天前
PostgreSQL编译安装实战
运维·postgresql