当前位置: 首页 > news >正文

PostgreSQL WAL 幂等性详解

1. WAL简介

WAL(Write-Ahead Logging)是PostgreSQL的核心机制之一。其基本理念是:在修改数据库数据页之前,必须先将这次修改操作写入到WAL日志中。
这确保了即使发生崩溃,数据库也可以根据WAL日志进行恢复。

恢复的核心流程就是不断**Replay(重做)**WAL记录,把数据库恢复到一致状态。

2. 为什么需要幂等性?

故障恢复可能发生在任意时刻,且可能反复进行。为了确保数据一致性,必须保证:

  • WAL重放(replay)是幂等的:即多次重做同样的WAL记录,最终结果与只做一次是一样的。
  • 防止重复执行逻辑修改,避免出现脏数据(如重复插入、更新错误等)。

没有幂等性,PostgreSQL的可靠性和数据完整性就无从谈起。

3. PostgreSQL 如何实现 WAL 的幂等性?

分情况讨论:

3.1 物理日志(Full Page Write, FPW)

  • 当首次修改某个page时,如果遇到检查点之后第一次修改,或者出现了部分写(partial write)风险时,PostgreSQL会将整个page的物理镜像写入WAL。
  • 恢复时直接拷贝覆盖,无需考虑page上的状态。
  • 因为是整页覆盖,所以天然是幂等的。重复应用多次,结果不会变。

注:FPW是恢复的"大杀器",确保即使发生中间页损坏,也能恢复。

3.2 逻辑日志(Insert/Update/Delete操作记录)

  • 日志并不是直接记录"修改了page的哪一块物理位置",而是记录在什么page上执行了什么逻辑操作。
  • 例如:在某个page上插入一条tuple "X",而不是"在page偏移offset=32的地方插入"。
  • 如果简单地无脑重做,恢复两遍就会出现重复记录。

为避免此问题,PostgreSQL每个page在物理结构中维护了一个字段:pd_lsn,即Page LSN。

  • 当一个page被修改时,它的pd_lsn被更新为当前WAL record的LSN。
  • 恢复时,每次要重放一个WAL record之前,PostgreSQL会先检查:
    只有当 WAL record 的 LSN > page 的 pd_lsn 时,才进行重做,否则跳过

3.3 流程示意图

恢复 -> 读取WAL record -> 找到要修改的Page ->

比较 (WAL record LSN vs Page pd_lsn)

   -> 如果 WAL LSN <= Page LSN, 说明已经做过,跳过

   -> 如果 WAL LSN > Page LSN, 执行修改,更新Page LSN

这种机制可以保证:即使崩溃后在同一个地方反复重做日志,也不会对数据造成破坏。

4. 实验验证 PostgreSQL WAL 幂等性

下面用一个小实验验证pg的WAL幂等性:

4.1 实验环境准备

initdb -D /tmp/pgwaltest

pg_ctl -D /tmp/pgwaltest -l logfile start

psql

4.2 创建表并插入数据

CREATE TABLE test_wal (id serial PRIMARY KEY, val text);

INSERT INTO test_wal(val) VALUES('first insert');

CHECKPOINT;  -- 强制生成检查点,防止混入无关WAL

这时test_wal表中有一条数据,系统生成了新的检查点。

4.3 查看Page LSN

PostgreSQL提供了扩展 pageinspect 来查看物理Page的信息。

安装并使用:

CREATE EXTENSION pageinspect;

-- 找出表的物理文件

SELECT relfilenode FROM pg_class WHERE relname = 'test_wal';

-- 假设relfilenode是 16384

-- 查看page的LSN(第一页)

SELECT * FROM page_header(get_raw_page('test_wal', 0));

会返回:

lsn  | checksum | flags | lower | upper | special | pagesize | version | prune_xid

------+----------+-------+-------+-------+---------+----------+---------+-----------

0/1500720 |    ...   |       |   ... |   ... |    ...   |    8192  |    4    |    ...

记录下当前Page的LSN,比如是0/1500720。

4.4 模拟崩溃后重复恢复

手动重放WAL当然很复杂,但可以模拟:

  1. 手动修改page LSN,使其低于WAL日志的LSN;
  2. 重启数据库,触发recovery。

不过这样太麻烦,我们可以简单理解为:

  • 如果page LSN已经大于WAL record的LSN,那么即使WAL被Replay,也不会改动页面。

4.5 直接实验重复写入一条相同数据:

-- 模拟意外重复插入

INSERT INTO test_wal(val) VALUES('first insert');

-- 应该报错,因为违反唯一约束(id列),而不是"无脑"插两遍

说明PostgreSQL在逻辑层也有幂等保护,比如:

  • 主键/唯一约束
  • page LSN校验
  • XID(事务ID)检测

5. 注意事项和补充

  • FPW(Full Page Writes)必须打开,否则遇到partial write会导致崩溃后无法恢复。
  • 极端情况下,日志丢失(比如WAL损坏)仍然会导致恢复失败,因此建议使用流复制+归档备份双保险。
  • WAL日志管理工具(如pg_waldump)可以辅助查看日志内容,帮助理解。

示例:

pg_waldump /tmp/pgwaltest/pg_wal/

可以看到诸如:

rmgr: Heap        len (rec/tot):  54/ 54, tx: 490, lsn: 0/01500280, desc: INSERT off 2

显示了一个Heap表上的insert动作及其LSN。

总结

  • PostgreSQL WAL重放过程是严格幂等的。
  • 物理日志天然幂等,逻辑日志依赖于Page LSN机制保证幂等。
  • 实现细节涵盖了page级别和事务级别的双重校验。
  • 实验验证了幂等机制的有效性。

PostgreSQL通过这些设计,确保即使在最坏情况下崩溃恢复,也能保证数据一致性和正确性。

http://www.xdnf.cn/news/173647.html

相关文章:

  • PH热榜 | 2025-04-26
  • 论文速报《ChatBEV:理解BEV地图的视觉语言模型新突破》
  • uniapp自定义一个选择年月日时分的组件。
  • Linux CentOS 安装Python 3.8.0
  • 8、HTTPD服务--CGI机制
  • BR_单时隙/多时隙灵敏度(Sensitivity-single/multi slot packets)
  • Apache Tomcat 漏洞(CVE-2025-24813)导致服务器面临 RCE 风险
  • 域名系统DNS
  • 03.04、化栈为队
  • PAT第七题素数对猜想
  • 手机充电进入“秒充“时代:泡面刚下锅,电量已满格
  • 贪心算法和动态规划
  • 【Flutter】Unity 三端封装方案:Android / iOS / Web
  • EN18031测试,EN18031认证,EN18031报告解读
  • MySQL 锁等待超时问题解析:Lock wait timeout exceeded;try restarting transaction
  • PLC在仪表控制系统中的应用
  • windows10系统:如何把文件夹里的图片直接显示出来?
  • vue3实现对自定义组件自由拖动效果
  • 如何有效防止 SQL 注入攻击?
  • [创业之路-341]:华为人力资源管理 - 华为技术专家体系详解
  • 论文导读 - 基于大规模测量与多任务深度学习的电子鼻系统实现目标识别、浓度预测与状态判断
  • 计算机网络全栈精讲:从 TCP/UDP 原理到 Socket 编程与 HTTP 协议实战(含代码实现)
  • 深入浅出JVM - Java架构师面试实战
  • 【网络原理】 网络编程套接字
  • Animate 中HTMLCanvas 画布下的鼠标事件列表(DOM 鼠标)
  • 关于IDEA的循环依赖问题
  • 如何在 iPhone 上恢复已删除的联系人:简短指南
  • Spring MVC 拦截器教程
  • 动手学深度学习11.11. 学习率调度器-笔记练习(PyTorch)
  • 助力产业升级 | BMC安全启动方案上新了!