本文包含以下内容:
WAL的基本介绍 WAL的写入机制和相关参数介绍 组提交和相关参数介绍 其他WAL相关参数介绍 pg_stat_wal视图介绍 PG的WAL和Oracle的REDO的对比
WAL的基本介绍
在PostgreSQL中,WAL是Write Ahead Log(预写式日志)的缩写,当执行INSERT、UPDATE、DELETE等修改操作时,并不会马上把数据持久化到数据文件中,而是先将操作记录写入WAL日志,待WAL日志成功落盘后,才更新shared buffers中的数据,后续再异步将shared buffers中的脏数据写入实际的数据文件中。
通过这样的机制,WAL具有如下作用:
WAL是事务持久性的核心保障机制,同时也是事务原子性和一致性的关键支撑。
确保数据库在崩溃或断电等异常情况下,能够通过日志恢复未持久化的数据,从而保证数据的完整性和一致性。
实现流复制和逻辑复制,满足各类数据的同步需求。
结合基础备份,实现任意时间点恢复(PITR)。
WAL的写入机制与相关参数
WAL的写入机制如下:
当数据发生变化时,对应的后端进程(每个客户端连接都会关联一个后端进程)会产生对应的WAL记录,这些WAL记录会被后端进程直接写入wal buffer中,才能继续后续的操作。
当后端进程在向wal buffer中写WAL记录时,发现wal buffer即将满了或者当前的WAL日志文件需要切换,就会自己先将wal buffer中的WAL记录写入WAL文件并刷盘,再继续向wal buffer中写WAL记录。
当事务同步提交时,后端进程会将wal buffer中的WAL记录写入WAL文件并刷盘,再将事务状态设置为成功。
当事务异步提交时,后端进程将wal buffer中的WAL记录写入操作系统缓存中,不等待刷盘就将事务状态设置为成功,刷盘的操作会等待WalWriter进程或者检查点进程来完成。
WalWriter进程负责定期将wal buffer中的WAL记录刷写到WAL日志文件中,他的工作主要由wal_writer_delay和wal_writer_flush_after两个参数共同控制(后面介绍参数的时候再详细说明)。
当数据库进行检查点操作时,会触发一次WAL日志文件的强制写入和刷盘来保证数据的一致性。
写WAL日志的基本流程图如下:

以下是和WAL的写入机制相关的参数:
fsync
参数说明: 控制数据持久化的核心参数。 设为on时,当刷盘时,会强制调用操作系统的fsync()或等效函数,来将wal_buffers中的数据同步写入物理磁盘。 设为off时,仅会将wal_buffers中的数据写入操作系统缓存,这会带来性能上提升,但会存在数据丢失的风险。
设置参考: 生产环境下,必须设置为on。
synchronous_commit
参数说明:控制事务提交的持久性级别,默认值为on。
设置为off时,事务提交时,WAL写入缓存,而不需要持久化到磁盘存储中,就向客户端返回成功。若发生系统崩溃可能导致最近约3倍wal_writer_delay时间内提交的事务丢失。
在非复制环境下或复制环境并未指定synchronous standby_names时:设置为on、local、remote_write、remote_apply时,事务提交时,等待WAL持久化到磁盘存储中,才向客户端返回成功。
在复制环境下且指定了synchronous standby_names时:
设置为local时,事务提交时,WAL在本地被持久化到磁盘存储中,就向客户端返回成功,不需要考虑备库状态。
设置为remote_write,事务提交时,WAL传入备库缓存时,向客户端返回成功。
设置为on,事务提交时,WAL在备库被持久化到磁盘存储时,向客户端返回成功。
设置为remote_apply时,WAL在备库被持久化到磁盘存储且被apply,才向客户端返回成功。
设置参考: 通常建议设置为on;在要求金融级严格一致性的系统可以设置为remote_apply;当对性能的需求超过对事务持久性需求时,可以设置为off。
wal_sync_method
参数说明:fsync设置为on时,刷盘时调用的系统函数,可选值有:open_datasync、fdatasync、fsync、fsync_writethrough、open_sync。在linux中默认值为fdatasync。
设置参考: 通常可以保留默认值。借助pg_test_fsync工具测试后可以了解当前操作系统下设置为何值最优。
wal_buffers
参数说明: 设置WAL缓冲区的大小,默认值是-1,代表让PostgreSQL自动计算,取shared_buffers的1/32,但不超过一个WAL日志段的大小(默认16MB)。
设置参考: -1
wal_writer_delay
参数说明: 控制了WalWriter休眠的时间,并和wal_writer_flush_after参数共同控制WalWriter主动刷盘的阈值,默认值是200ms。
当没有发生后端进程和检查点主动刷盘时,walbuffer中没有落盘的WAL记录会等待WalWriter自动唤醒后才批量写入WAL日志。
当写入WAL操作的时间距离上次刷盘时间不足 wal_writer_delay且累计写入的WAL大小不足wal_writer_flush_after时,当次操作仅会将WAL记录写入操作系统缓存而不会触发强制刷盘操作。
当WalWriter完成了一次写WAL日志操作后,会进入休眠,休眠的时间为wal_writer_delay。默认情况下,如果连续50次WalWriter唤醒后都没有任何WAL记录需要被写入WAL日志中,WalWriter就会进入冬眠模式,休眠时间变为wal_writer_delay的25倍,当WalWriter在一次唤醒后向WAL日志中写入了WAL记录,那么冬眠结束并重置冬眠计数。
当synchronous_commit设置为off时,wal_writer_delay控制所有WAL写入(包括事务提交)的最大延迟间隔。
设置参考: 保持默认值即可,写入密集场景可以适当缩短。
wal_writer_flush_after
参数说明:和wal_writer_delay参数共同控制WalWriter主动刷盘的阈值。 该参数默认值为1MB。如果设置为0则代表总是立即刷盘。
设置参考: 在使用SSD磁盘、写入密集场景且synchronous_commit=on时,可以适当调大该参数。通常保持默认值1MB即可。
组提交
在PostgreSQL中,组提交用于减少事务提交时的WAL持久化开销。其核心思想是:将多个并发事务的 WAL 刷盘请求 “批量合并”,通过一次物理磁盘I/O操作完成多个事务的WAL持久化,从而降低频繁单事务刷盘带来的性能损耗(尤其在高并发写入场景下效果显著)。
换个形象的说法,就是一个人(后端进程)准备打车(同步提交),但是一个人打车的费用比较贵,他会先判断是否有足够多同样可能需要打车的人(当前运行并发事务是否达到commit_siblings个),如果有,他就会等待一定时间(commit_delay)看能不能拼到车,同时也不能耽搁自己太久的时间。
commit_delay
参数说明: 指定事务提交后等待多少微秒才将 WAL 记录写入磁盘,默认值是0。
设置参考: 10000
commit_siblings
参数说明: 指定触发延迟提交所需的最小并发活动事务数,只有当系统中至少有commit_siblings个活动事务时,commit_delay才会生效。默认值为5。
设置参考: 5
其他WAL相关的参数
wal_level
参数说明: 设置WAL记录的信息量,级别越高,产生的WAL越详细,对I/O的压力和所占用的磁盘空间也越大。 可选值如下: minimal:仅记录崩溃恢复所需要的信息。 replica:wal_level的默认值,可以支持流复制、在线热备、PITR恢复、WAL归档等场景。 logical:支持逻辑解码(将WAL记录转换为可读的SQL),支持表级和数据库级的数据复制和分发。
设置参考:replica可以覆盖90%的业务场景,需要逻辑复制的场景可以设置为logical。生产环境禁止将wal_level设置为minimal。
full_page_writes
参数说明: 数据库进行检查点后,对某个数据页第一次进行修改时,会将整个数据页都写入WAL中。 默认值为ON。 关闭后可以减少WAL日志量,降低磁盘空间的使用率,提高性能,但会增加数据损坏的风险。 另外,当fsync参数设置为off时,full_page_writes设置为on也没有意义。
设置参考: 生产环境中都应设置为on,除非底层存储系统本身就能保证页面写入的原子性(例如使用ZFS文件系统)。
wal_log_hints
参数说明: 开启该参数后,检查点后对某个数据页第一次进行修改时,即使只是修改了页面的提示位,也会将整个页面都写入WAL中。 默认值是off。 页面的提示位,包括了页面是否含死堆元、页面已满、堆元是否都已被冻结等。在vacuum、统计信息收集、索引维护等操作时,都可能会导致数据页的提示位发生改变。 打开此参数也会导致WAL日志量增加,可能会影响数据库性能。 关闭该参数后无法使用pg_rewind命令来恢复数据库,也可能导致数据库流复制主备不一致的情况。要实现PITR恢复(基于时间点的恢复)也需要开启此参数。
设置参考: 生产环境建议设置为on。
wal_compression
参数说明: 控制PostgreSQL数据库是否启用WAL日志压缩,默认值为off。 开启后,进行full page write的时候会对WAL日志进行压缩,不影响非全页写产生的WAL日志。 可以达到一定减少磁盘I/O,节省存储空间,降低网络带宽的作用,但会增加CPU的使用率。 PG15以前的版本只能设置为on,PG15开始可以设置为lz4、pglz或zstd。
pglz: 中等压缩率,中等速度,低CPU消耗。
lz4: 低压缩率,非常高的速度,极低的CPU消耗。
zstd: 高压缩率,较高的速度,中等的CPU消耗。
设置参考: PG15以前设置为on,PG15以后可以根据需求选择压缩方式。
wal_init_zero
参数说明: 启用该参数时,PostgreSQL会用“0”填充新创建的WAL文件,确保整个 WAL 文件在首次使用前被完全初始化。 关闭该参数时,PostgreSQL会创建稀疏文件(sparse file),只有实际写入的部分会占用物理存储空间,操作系统负责按需分配实际存储空间。 开启该参数的优点是:确保写入性能一致,避免首次写入时的延迟。 关闭该参数的优点是:可以降低WAL日志初始化时的I/O负载,但可能导致后续写入时出现“文件扩展”的额外I/O开销。
设置参考: 使用ext4文件系统时,建议设置为ON。支持 “稀疏文件” 与 “快速清零”的文件系统(例如xfs、zfs)中可以设置为on。
wal_recycle
参数说明: 控制WAL文件重用行为,默认值为on。 设置为on时:PostgreSQL会将过期的WAL段文件重命名,作为新的WAL段文件循环使用,避免频繁创建和删除文件的I/O 开销。
设置参考: on
wal_skip_threshold
参数说明: 仅在wal_level=minimal的时候生效。事务涉及“新建表”或“重写表”这类会产生新数据文件的操作时,如果新数据的大小超过wal_skip_threshold,那么不会写WAL日志,而是直接写数据文件并刷盘,来保证事务的持久性。默认值是2MB
设置参考:通常wal_level不会设置为minimal,所以该参数保留默认值即可。
pg_stat_wal视图
视图基本说明
通过PostgreSQL中的pg_stat_wal视图,可以监控到与WAL生成和活动相关的关键统计信息,这些统计信息可以用于诊断数据库性能、I/O 瓶颈以及优化配置。
视图字段说明

PostgreSQL的WAL和Oracle的Redo比较
PostgreSQL的WAL和Oracle的Redo在核心概念上一致,都是预写式日志机制的实现,这是保证数据库事务持久性和崩溃恢复的基石。 但它们存在以下差别:

最后,欢迎感兴趣的各位老铁加入DB演武场交流群,在这里可以交流PostgreSQL、Linux、Kingbase、openGauss等各类技术知识。





