理解 LSN 和 WAL 文件名

本页目录

img

如何读取 LSN 值

LSN — 日志序列号,是指向预写日志 (WAL) 中某个位置的指针。理解并学会如何使用它,对于处理流复制、逻辑复制、备份和恢复非常重要。Postgres 相关文档:

LSN 是一个 8 字节(64 位)的值(源码)。它表示为 A/B(更具体地说,A/BBbbbbbb,详见下文),斜杠两侧分别表示高 32 位和低 32 位。例如:

PSQL
nik=# select pg_current_wal_lsn();
 pg_current_wal_lsn
--------------------
 5D/257E19B0
(1 row)

有趣的是,LSN 值可以进行比较,甚至可以进行减法运算 — 前提是我们使用 pg_lsn 数据类型。结果将以字节为单位表示:

PSQL
nik=# select pg_lsn '5D/257D6780' - '5D/251A8118';
 ?column?
----------
  6481512
(1 row)

nik=# select pg_size_pretty(pg_lsn '5D/257D6780' - '5D/251A8118');
 pg_size_pretty
----------------
 6330 kB
(1 row)

这也意味着我们可以通过查看从"点 0" (值 0/0) 出发的距离来获得 LSN 的整数值:

PSQL
nik=# select pg_lsn '5D/257D6780' - '0/0';
   ?column?
--------------
 400060934016
(1 row)

如何读取 WAL 文件名

现在,让我们看看 LSN 值如何与 WAL 文件名对应 (文件位于 $PGDATA/pg_wal 中)。我们可以使用 pg_walfile_name() 函数获取任何给定 LSN 相对应的 WAL 文件名:

PSQL
nik=# select pg_current_wal_lsn(), pg_walfile_name(pg_current_wal_lsn());
 pg_current_wal_lsn |     pg_walfile_name
--------------------+--------------------------
 5D/257E19B0        | 000000010000005D00000025
(1 row)

这里的 000000010000005D00000025 是 WAL 文件名,它由三个 4 字节 (32 位) 组成:

  1. 00000001 — 时间线 ID (TimeLineID),这是一个从 1 开始的连续 "历史号",当 Postgres 集群初始化时就会启动。它用于 "标识不同的数据库历史记录,以防止在恢复数据库的先前状态之后出现混乱" (源码)。
  2. 0000005D — LSN 序列号的高 4 字节部分。
  3. 00000025 — 可以分为两部分:
    • 000000 — 6 个前导零
    • 25 — 序列号低位部分的最高字节

需要记住的重要一点是,WAL 文件名的第三个 4 字节内容里有 6 个前导零 — 通常,这会导致在比较两个 WAL 文件名以理解其中包含的 LSN 时产生混淆和错误。

以下示例很有用 (文章来源):

Shell
LSN:                5D/      25   7E19B0
WAL: 00000001 0000005D 00000025

这在需要处理 LSN 值或 WAL 文件名,或者同时处理它们时非常有帮助。快速导航或比较这些值以了解它们之间的"距离"可能很有用。例如,在以下情况下:

此外,除了官方文档外,还有一些优秀的博客文章值得阅读:


谢谢阅读!和往常一样,请与从事 #PostgreSQL 的同事和朋友分享这篇文章。

我见

Postgres WAL Files and Sequence Numbers | Crunchy Data Blog

对于给定一个 LSN,我们可以迅速算出对应的 WAL 文件名,假设 pg_current_wal_insert_lsn() 返回 0/9A80D10 ,如果当前时间线为 1,那么 logid 就是 0/9A80D10 中的第一个数字 — 0,logseg 就是 0/9A80D10 中的第二个数字除以 16MB 的大小,9A80D10 左移 6 位,也就是 09。那么根据 WAL 文件的格式timelineID + logid + logseg,则相当于:"00000001"+"00000000"+"00000009",即为:"000000010000000000000009" ,而写位置的偏移量则是第二个数字 "9A80D10" 后六位 "A80D10"。

讨论与补充

在 GitHub 查看讨论

支持作者

感谢支持 PostgreSQL Howto 的长期维护。

微信赞赏码

读者交流群

二维码若已过期,可通过公众号 PostgreSQL 学徒或邮件联系。

PostgreSQL 学徒读者交流群二维码