WAL即Write-Ahead Logging,预写式日志(WAL)是保证数据完整性的一种标准方法。WAL的中心概念是数据文件(存储着表和索引)的修改必须在这些动作被日志记录之后才被写入,即在描述这些改变的日志记录被刷到持久存储以后。如果我们遵循这种过程,我们不需要在每个事务提交时刷写数据页面到磁盘,因为我们知道在发生崩溃时可以使用日志来恢复数据库:任何还没有被应用到数据页面的改变可以根据其日志记录重做(这是前滚恢复,也被称为REDO)。WAL 的中心思想是先写日志,再写数据,数据文件的修改必须发生在这些修改已经记录在日志文件中之后。
实现WAL机制,需要保证脏页在刷新到磁盘前,该数据页相对应的日志记录已经刷新到磁盘中。为了实现WAL机制,当PostgreSQL进行事务提交(脏数据页需要刷新到磁盘)时,需要进行如下操作:
为了标记每个数据页最后修改它的日志记录号,在每个数据页的PageHeaderData结构中引入了一个LSN标记,如下:
typedef struct PageHeaderData
{
PageXLogRecPtr pd_lsn; //指向最后修改页面的日志记录
uint16 pd_checksum;
uint16 pd_flags;
LocationIndex pd_lower;
LocationIndex pd_upper;
LocationIndex pd_special;
uint16 pd_pagesize_version;
TransactionId pd_prune_xid;
ItemIdData pd_linp[1];
} PageHeaderData;
其中PageXLogRecPtr结构是一个无符号的64位整数,它的含义如下:
32位 | 8位 | 13位 | 11位 |
---|---|---|---|
逻辑日志文件号 | 段号 | 块号 | 块内偏移 |
PageXLogRecPtr结构和每个日志记录一一对应,同时LSN是全局统一管理,顺序增加的。
当缓冲区管理器(Bufmgr)写出脏数据页时,必须确保小于页面PageHeaderData中pd_lsn指向的Xlog日志已经刷写到磁盘上了。这里的LSN检查只用于共享缓冲区,用于临时表的local缓冲区不需要,因此临时表是没有WAL日志的,不受WAL机制的保护。
wal_level决定多少信息写入到 WAL 中。默认值是replica, 它写入足够的数据以支持WAL归档和复制,包括在备用服务器上运行只读查询。
但最少的 WAL 不会包括足够的信息来从基础备份和 WAL 日志中重建数据,因此,要启用 WAL 归档(archive_mode)和流复制,必须使用replica或更高级别。
在9.6之前的版本中,此参数还允许值archive和 hot_standby。这些仍然被接受,但映射到replica。
minimal则删除除了从崩溃或立即关闭中恢复所需的信息之外的所有日志记录。logical会增加支持逻辑解码所需的信息。
控制事务提交后返回客户端是否成功的策略,代表一个事务是否需要等待 WAL 记录被写入磁盘
on:默认值,为on且没有开启同步备库的时候,会当wal日志真正刷新到磁盘永久存储后才会返回客户端事务已提交成功,
当为on且开启了同步备库的时候(设置了synchronous_standby_names),必须要等事务日志刷新到本地磁盘,并且还要等远程备库也提交到磁盘才能返回客户端已经提交.
remote_apply:提交将等待, 直到来自当前同步备用数据库的回复表明它们已收到事务的提交记录并应用它, 以便它对备用数据库上的查询可见。
remote_write:提交将等待,直到来自当前同步的后备服务器的一个回复指示该服务器已经收到了该事务的提交记录并且已经把该记录写出到后备服务器的操作系统。
local:当事务提交时,仅写入本地磁盘即可返回客户端事务提交成功,而不管是否有同步备库。
off:写到缓存中就会向客户端返回提交成功,但也不是一直不刷到磁盘,延迟写入磁盘,延迟的时间为最大3倍的wal_writer_delay参数的(默认200ms)的时间,所有如果即使关闭synchronous_commit,也只会造成最多600ms的事务丢失。
可能会造成一些最近已提交的事务丢失,但数据库状态是一致的,就像这些事务已经被干净地中止。但对高并发的小事务系统来说,性能来说提升较大。
WAL writer进程的间歇时间。默认值是200ms。准确的配置应该根据自身系统的运行状况。
如果时间过长可能造成WAL buffer的内存不足;反之过小将会引起WAL的不断的写入,对磁盘的IO也是很大考验。
个已经提交的数据在WAL buffer中存放的时间,单位ms,默认值是0,不用延迟。非0值表示可能存在多个事务的WAL同时写入磁盘。
如果设置为非0,表明了某个事务执行commit后不会立即写入WAL中,而仍存放在WAL buffer中,这样对于后面的事务申请WAL buffer时非常不利,尤其是提交事务较多的高峰期,可能引起WAL buffer内存不足。
如果内存足够大,可以尽量延长该参数值,能够使数据集中写入这样降低了系统的IO,提高了性能。
同样如果此时崩溃数据面临着丢失的危险。个人建议采用默认值,同时将WAL文件存放在IO性能好的磁盘上。
检查点是在事务序列中的点,这种点保证被更新的堆和索引数据文件的所有信息在该检查点之前已被写入。
在检查点时刻,所有脏数据页被刷写到磁盘,并且一个特殊的检查点记录将被写入到日志文件(修改记录之前已经被刷写到WAL文件)。
在崩溃时,崩溃恢复过程检查最新的检查点记录用来决定从日志中的哪一点(称为重做记录)开始REDO操作。在这一点之前对数据文件所做的任何修改都已经被保证位于磁盘之上。
因此,完成一个检查点后位于包含重做记录的日志段之前的日志段就不再需要了,可以将其回收或删除(当WAL归档工作时,日志段在被回收或删除之前必须被归档)。
服务器的检查点进程常常自动地执行一个检查点。检查点在每checkpoint_timeout秒开始,或者在快要超过 max_wal_size时开始。 默认的设置分别是 5 分钟和 1 GB。如果从前一个检查点以来没有WAL被写入, 则即使过了checkpoint_timeout新的检查点也会被跳过。
自动 WAL 检查点之间的最长时间,以秒计。 有效值在30秒和1天之间。 默认是 5 分钟(5min)。 增加这个参数的值会增加崩溃恢复所需的时间。
在自动WAL检查点使得WAL增长到最大尺寸。缺省是1GB。