暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL写WAL机制解析

365

本文包含以下内容:

  • 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等各类技术知识。


文章转载自张家辉的DB演武场,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论