背景

XLog是PostgreSQL中的WAL,像MySQL中的RedoLog一样,提供预写日志的能力。
预写日志的主要目的包括事务中的两个概念:

  1. 原子性
  2. 持久性

同时,预写日志的写入方式大多又是追加写(顺序写),这在传统存储介质中性能远远优于随机写,同时又可以批量持久化(fsync),也间接提升了数据库的性能。
预写日志一般包含两种内容,redo 和 undo,redo一般是写入日志,undo是回滚日志;因为PostgreSQL的MVCC并没有利用回滚段,所以XLog中实际上只有redo的信息。

在PostgreSQL中关于XLog相关的变量



同时PG也支持使用pg_walinspect插件来进行DEBUG

XLog文件管理

PG的XLog一般存放在pg_xlog目录下,默认按照16MB切分为若干个文件
命名方式为24个字符,每个字符是十六进制数

前8位为时间线id,中8位为逻辑id,后8位为逻辑id下的第N个
在WAL文件写满后会切文件

双写策略

PG的数据页写入是按照page粒度的,对于WAL文件的page默认大小为8K,而操作系统的page大小默认为4K,也就是说操作系统层面只能做到4K的原子写,在异常容灾场景,很可能对于WAL文件来说,8K的page数据被截断了
为了应对这种情况,MySQL使用了innodb的double_write方案,即在bufferpool中开辟出一段空间专门用来双写的空间,我们称之为double write buffer,这段buffer有自己的刷脏策略,它在刷脏时会将数据写到ibdata中的共享表空间。于是如果crash发生在double write时,那么可以通过共享表空间中完整的page进行覆盖;如果数据页本身就是完整的,那么直接apply redolog即可恢复到一致性位点

PostgreSQL使用的是full_page_write策略,这个策略是checkpoint后的一个块第一次变脏后就要「整块」写到WAL日志中,后续修改此块则只把修改的信息写入WAL中,类似于对每个块都在初始变脏的时间点打了一个「快照」。在故障恢复时,如果有块被截断,那么则以「快照」点为基础进行恢复。
full_page_write开启是会对性能有损耗的

后来,PostgreSQL也支持了MySQL类似的方案进行双写,即开辟单独的一块buffer用于双写,它有独立的刷脏策略,同时在磁盘上也生成一个double write file充当MySQL中的共享表空间,在crash recovery时,因为double write file一定是完整的,如果有page截断则使用double write file中的数据进行覆盖补全后,apply XLog即可。

XLog的文件结构


WAL文件默认情况下16MB,其中按照8KB切分为一个Page,每个Page由 Header + Record组成
第一个Page包含XLogLongPageHeaderData,记录整个XLog文件的元信息
其他Page则以XLogPageHeaderData为头进行组织,主要记录事务日志对应的版本,时间线等信息

对于每条XLogRecord来说,也是由 Header + Data组成
XLogRecordHeader 一般记录日志的元信息,其中的xl_info每一位都代表不同信息
XLogRecordData 一般由XLogRecordBlockHeader和XLogRecordDataHeader组成
后面排列为块数据和主数据

XLogRecord的构造

XLogRecord = XLogRecordHeader + N 个 XLogRecordBlockHeader + XLogRecordDataHeader + block data + main data

其中block data通过XLogRegisBuffer方法注册到registered_buffer中
其中main data通过XLogRegisterData方法追加在XLogRecDatas链表中

同时,我们可以利用pg_xlogdump来解析一个XLog文件

其中比较重要的一些信息:

  1. desc : Heap 表示这是对堆表进行的操作,除了Heap之外还有Btree,Transaction等
  2. len : XLogRecord的长度
  3. xid : 事务号
  4. lsn : 本条记录的lsn
  5. pre : 上条记录的lsn
  6. blkref : 引用的第一个page所属堆表文件的信息,blk为块号

XLog的切换和回收

XLog的切文件时机:

  1. 主动发起pg_switch_xlog
  2. xlog写满时
  3. 使用archive_mode且超过archive_timeout设置值

XLog的回收时机:
理论上切换到新文件后,老的文件即可被重用或者回收,实际上还是有些不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 自动的WAL检查点之间的日志文件段的最大数量
checkpoint_segments
# 在自动WAL检查点之间的最长时间
checkpoint_timeout
# 缓解io压力
checkpoint_completion_target
# 日志文件段的保存最小数量,为了备库保留更多段
wal_keep_segments
# 已完成的WAL段通过archive_command发送到归档存储
archive_mode
# 强制timeout切换到新的wal段文件
archive_timeout


max_wal_size
min_wal_size

于是在正常不开启WAL归档时
通常不超过
(2 + checkpoint_completion_target) * checkpoint_segments + 1

checkpoint_segments + wal_keep_segments + 1

如果一个旧文件不再需要了会重命名然后继续覆盖使用,如果由于短期的日志输出高峰导致超过了
3 * checkpoint_segments + 1,直接删除文件

在开启归档时
只有在这个文件被归档成功后才会被删除