WAL Structure(Logical-Structure)
从wal逻辑结构来看,WAL 可被描述为变长日志条目流。每个条目都包含有关特定操作的一些数据,并以标准标头作为前缀。该标头提供的信息包括但不限于:
- 与条目(entry)相关的事务 ID
- 解释条目的资源管理器
- 用于检测数据损坏的校验和
- 条目长度
- 对前一个 WAL 条目的引用
WAL 通常是按正向读取的,但某些工具(例如 pg_rewind)可能会反向扫描它
WAL 数据本身可以具有不同的格式和含义。例如,它可能是一段页片段(page fragment),需要替换某个页面中指定偏移处的一部分内容。相应的资源管理器(resource manager)必须知道如何解析并重放这一特定条目。针对表、各种索引类型、事务状态以及其他实体,PostgreSQL 都有独立的资源管理器来处理它们各自的 WAL。
WAL 文件会占用服务器共享内存中的特殊缓冲区。用于 WAL 的缓存大小由参数 wal_buffers 决定。默认情况下,这个大小会自动设为总缓冲区缓存(buffer cache)大小的 1/32。
WAL 缓存与缓冲区缓存(buffer cache)非常相似,但它通常以环形缓冲区(ring buffer)的方式运行:新的日志条目被添加到缓冲区的头部,而旧的条目则从尾部开始写入磁盘。如果 WAL 缓存太小,就会比必要的更频繁地进行磁盘同步操作。
在系统负载较低的情况下,插入位置(即缓冲区的头部)几乎总是与已经写入磁盘的条目位置(即缓冲区的尾部)保持一致。
1 | demo=# SELECT pg_current_wal_lsn(), pg_current_wal_insert_lsn(); |
在PostgreSQL 10之前,所有函数名称都包含XLOG首字母缩写词,而不是WAL。
为了引用某个特定的日志条目,PostgreSQL 使用一种特殊的数据类型:pg_lsn(日志序列号,Log Sequence Number,简称 LSN)。它表示从 WAL 起始位置开始,以字节为单位的 64 位偏移量。LSN 通常以两个十六进制数字表示,中间用斜杠(/)分隔。
我们创建一个表:
1 | demo=# CREATE TABLE wal(id integer); |
现在执行一个任意命令,例如,更新一行数据。
1 | demo=*# UPDATE wal SET id = id + 1; |
页面的修改是在 RAM 中的缓冲区缓存(buffer cache)中进行的。这个更改也会记录在位于 RAM 中的 WAL 页面中。因此,插入的 LSN 会向前推进。
1 | demo=*# SELECT pg_current_wal_insert_lsn(); |
为了确保修改后的数据页是在对应的 WAL 条目之后才被刷新到磁盘,数据页的页头会存储该页最新相关的 WAL 条目的 LSN。你可以使用 pageinspect 插件查看这个 LSN。
1 | demo=*# SELECT lsn FROM page_header(get_raw_page('wal',0)); |
整个数据库集群只有一个 WAL,并且新的条目会不断地追加到其中。因此,存储在数据页中的 LSN 可能会比 之前某个时刻 pg_current_wal_insert_lsn() 返回的 LSN 更小。但如果系统中没有发生任何操作,这两个数值将会相同。
现在提交这个事务
1 | demo=*# commit; |
为了确保某个 CLog 页在对应的 WAL 条目写入磁盘之前不会被刷新到磁盘,必须追踪该页所对应的最新 WAL 条目的 LSN。但这种 LSN 信息是保存在内存(RAM)中的,而不是存在 CLog 页本身
某个时刻,WAL 日志条目会被写入磁盘;此时,才能把对应的 CLOG 和数据页从缓存中淘汰(evict)。如果必须更早淘汰这些缓存页,那么系统会发现这一点,并会先强制将对应的 WAL 条目写入磁盘。
如果你知道两个 LSN(日志序列号)的位置,就可以通过简单地相减计算这两者之间的 WAL 日志大小(以字节为单位)。只需将它们转换为 pg_lsn 类型即可进行减法运算
1 | demo=# demo=# SELECT '1/EBDDC500'::pg_lsn - '1/EBDDA4F8'::pg_lsn; |
在这个具体案例中,更新(update)和提交(commit)操作相关的 WAL 条目大约占用了几千字节。可以用相同的方法,估算某个工作负载在单位时间内产生的 WAL 日志量。
这些信息对设置检查点(checkpoint)参数非常重要。
参考书目
- Egor Rogov, PostgreSQL 14 Internals, https://postgrespro.com/community/books/internals