PostgreSQL WAL机制与重放流程详解
PostgreSQL的WAL(Write-Ahead Logging)机制是数据库可靠性和容灾能力的核心。本文将详细介绍 WAL 的基本原理、重放机制(Replay)、归档策略(Archiving)以及相关使用场景。
一、什么是 WAL?
WAL 是 PostgreSQL 的事务日志机制,它确保数据的持久性和一致性。其核心思想是:在修改数据之前,先将操作记录到日志文件中,这样即使系统崩溃,也可以通过日志恢复数据。
- 作用:防止数据丢失,支持崩溃恢复、主备复制、时间点恢复(PITR)等。
- 位置:默认存储在
pg_wal/
目录下。
每个 WAL 文件默认大小为 16MB,顺序编号并写入磁盘,下图就是运行中pg_wal日志目录:
总之:WAL在实际数据被修改前,变更信息已经被记录到了一个顺序写入的日志中;即使服务器崩溃,也能通过 WAL 重放日志 来恢复数据。
二、WAL 的核心作用
- 崩溃恢复: 数据写入后即使还没落盘,只要写入了 WAL就可以恢复(WAL重放机制)
- 高性能: 顺序写入 WAL 比频繁写磁盘更高效
- 流复制: 主库通过发送 WAL 给备库实现同步
- PITR(时间点恢复): 可以恢复到任意时间点(类似 Git 的时光机)
三、WAL 的工作流程
以一个简单的 INSERT 为例:
-
事务开始(begin):
PostgreSQL 分配一个事务 XID,SQL层面插入一条数据:
INSERT INTO test(id, name) VALUES(1,'test-1');
-
生成 WAL 记录:
数据页的修改被生成一条 WAL 记录(还未写入磁盘)。
-
先写 WAL:
WAL 记录被写入磁盘(WAL 文件),这叫 fsync()。关于
fsync
动作,在配置文件postgresql.conf
中有一个配置:fsync = on
,该配置默认是on,表示会自动刷新数据到磁盘,如果手动关闭以后可能带来的问题数据丢失。
-
提交事务(commit):
只有当 WAL 写入成功后,才允许事务 COMMIT。
-
后台慢慢刷数据页:
真正的表或索引的数据页落盘可能延迟发生,由后台 bgwriter 或 checkpointer 完成,可以通过pg的日志看到类似的记录:
2025-04-24 08:21:23.251 GMT,,,29,,6805ac75.1d,1868,,2025-04-21 02:24:53 GMT,,0,LOG,00000,"checkpoint complete: wrote 284 buffers (1.7%); 0 WAL file(s) added, 0 removed, 0 recycled; write=28.357 s, sync=0.003 s, total=28.361 s; sync files=25, longest=0.002 s, average=0.001 s; distance=822 kB, estimate=12165 kB",,,,,,,,"LogCheckpointEnd, xlog.c:8853","","checkpointer",,0
大致流程:
数据页变更 ——> 生成 WAL ——> 写入 pg_wal ——> 数据异步落盘
|
└──> 同步到备库
这样做的核心优势就是:
- 保证一致性(即使崩溃)
- 只需顺序写 WAL 文件,避免频繁磁盘写入数据页
四、WAL 重放机制(WAL Replay)
1. 什么是重放(Replay)
WAL Replay 指的是数据库在恢复模式下,从 WAL 文件中重放记录的所有变更操作,以恢复一致性状态。
2. 重放的触发时机
- 数据库宕机自动恢复(crash recovery)
- 基于备份和归档 WAL 的恢复(PITR)
- 主备流复制时,备库同步主库 WAL(物理复制)
3. 重放流程
- 数据库启动时读取 pg_control,发现处于恢复模式
- 读取 pg_wal/ 或归档目录中的 WAL 文件
- 按 LSN 顺序重放每一条日志记录
- 执行 REDO 操作(如 INSERT、UPDATE、DDL 等)
- 到达最后一个一致状态后退出恢复模式
- 数据库恢复正常,允许客户端连接
可以使用以下 SQL 查询当前重放状态,当然如果没有在重放是不会查询到数据的:
SELECT pg_last_wal_replay_lsn();
SELECT pg_is_in_recovery();
#输出:
test=# SELECT pg_last_wal_replay_lsn();
pg_last_wal_replay_lsn
------------------------
(1 row)
test=# SELECT pg_is_in_recovery();
pg_is_in_recovery
-------------------
f
(1 row)
五、WAL 归档策略(WAL Archiving)
WAL 文件默认会循环使用,一旦被覆盖就无法用于恢复。因此,若要支持时间点恢复和异地备份,就必须开启归档策略,将 WAL 文件拷贝到安全位置。
1. 配置方式
在 postgresql.conf
中添加以下内容:
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /var/lib/postgresql/archive/archive%f && cp %p /var/lib/postgresql/archive/archive%f'
archive_timeout = 60 # 每隔多长时间强制切换 WAL 文件
archive_cleanup_command = 'pg_archivecleanup /var/lib/postgresql/archive/archive %r' #清除archive数据
该配置会将 WAL 文件保存到 /var/lib/postgresql/archive/
目录,下图就是最新的archive数据,这里的格式跟WAL事一样的,毕竟是WAL的备份。
2. WAL 文件命名规则
格式为:
<timelineID><logSegNoHigh><logSegNoLow>
例:00000001000000000000000A
六、WAL 归档恢复(PITR)
1. 准备恢复
- 使用
pg_basebackup
获取基础数据快照,注意:谨慎操作,请在做数据恢复之前做好数据备份,可以使用pg_dump
或者pg_dumpall
工具来实现。
pg_basebackup -D /data/restore -X fetch --write-recovery-conf
- 将 WAL 文件放入归档目录(如 /archive)
2. 设置恢复参数
编辑 postgresql.conf
或创建 postgresql.auto.conf
:
restore_command = 'cp /archive/%f %p'
recovery_target_time = '2025-04-10 12:00:00'
recovery_target_action = 'pause'
并在数据目录创建 recovery.signal
文件:
touch $PGDATA/recovery.signal
3. 启动 PostgreSQL
PostgreSQL 将自动读取归档 WAL 并重放,恢复到指定时间点后进入 pause
,此时可检查状态并执行:
pg_ctl promote
七、WAL 总结
场景 | 说明 |
---|---|
崩溃恢复 | 数据库宕机后自动从 WAL 中恢复未提交事务 |
主备复制 | 主库将 WAL 实时发送至备库,备库重放 WAL |
时间点恢复 | 配合 basebackup + WAL 实现精确恢复 |
异地容灾 | 将 WAL 归档到异地,确保数据安全 |
八、常用相关函数
-- 查看当前 WAL 写入点
SELECT pg_current_wal_lsn();
-- 输出--
test=# SELECT pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
1/3B1660C0
(1 row)
-- 查看归档设置状态
SHOW archive_mode;
-- 输出--
test=# SHOW archive_mode;
archive_mode
--------------
on
(1 row)
SHOW archive_command;
-- 输出--
test=# SHOW archive_command;
archive_command
---------------------------------------------
cp %p /var/lib/postgresql/archive/archive%f
(1 row)
九、小结
WAL是PostgreSQL的核心功能,牵涉到数据的安全、容灾恢复以及高可用,类似于Mysql的 redo log、undo log、binlog
等日志,但其功能比Mysql的更强大并且更加简洁。