当前位置: 首页 > postgresql > 正文

第一层:LSN 的源码级真身(它到底是个什么变量?)

在 C 语言结构体层面,LSN 没有任何花哨的设计,它是极度纯粹的暴力美学。

1. 核心定义 (XLogRecPtr)

C

/* src/include/access/xlogdefs.h */
typedef uint64 XLogRecPtr;
  • 物理本质:它就是一个 64 位的无符号整型
  • 架构定性:它代表的是自数据库初始化(initdb)以来,写入 WAL 日志的全局绝对字节偏移量(Byte Offset)。因为是 64 位,它的理论物理极限是 16 EB(Exabytes),在人类目前的硬件寿命内绝对不会发生事务号(XID)那种回卷(Wraparound)灾难

2. 绝对零度宏 (InvalidXLogRecPtr)

C

#define InvalidXLogRecPtr   ((XLogRecPtr) 0)
  • 物理本质:值为 0,在终端里显示为 0/0
  • 架构定性:这是系统的“免死金牌”。如果一个 8KB 页面头部的 LSN 是这个值,说明该页面刚刚被初始化,或者属于 UNLOGGED(无日志)表。刷盘进程看到它,会直接放行,不校验任何 WAL 先行落盘的铁律。

第二层:底层宏运算与寻址制导(LSN 是如何变成物理文件的?)

这是面试场上的绝杀考点。当我们在数据库里看到 LSN 显示为 16/B374D840 时,它是如何被内核转化为底层 pg_wal 目录下的 16MB 物理文件的?

在 C 语言层面,内核通过一系列按位运算的宏(Macros)来进行极其高效的时空切割。

1. 逻辑拆解(高 32 位与低 32 位) 16/B374D840 被内核斜杠劈开:

  • 高 32 位 (16):逻辑日志文件号(Logical Log ID)。
  • 低 32 位 (B374D840):逻辑文件内的绝对偏移量。

2. 物理文件名的 C 语言拼接公式 (XLogFileName 宏) 一个真实的 WAL 文件名长达 24 个十六进制字符(例如:0000000100000016000000B3)。内核是这样拼出来的:

  • 第一段(前 8 位)TimelineID(时间线 ID,如 00000001)。注意:LSN 本身不包含时间线,时间线是内核上下文提供的另一个维度,两者结合才能锁定多维宇宙的绝对位置。
  • 第二段(中 8 位):就是 LSN 的高 32 位,即逻辑日志号 00000016
  • 第三段(后 8 位):物理段号。这是用低 32 位算出来的。
    • 算法推演:默认一个 WAL 文件是 16MB(0x1000000 字节)。内核将低 32 位 B374D840 除以 16MB 取整,得到的高位 B3 就是物理段号。补齐为 000000B3

一句话定性:LSN 不是乱码,它是经过底层除法与位移运算后,能够极其精准地映射到 Linux ext4/xfs 文件系统具体扇区的 GPS 寻址指针


第三层:LSN 统治全库的“三大结构体锚点”(网状记忆法)

单纯的字节游标没有意义,LSN 必须被死死地嵌入到 PostgreSQL 最核心的 C 语言结构体中,才能发挥其绝对的统治力。请把这三个锚点刻在脑子里:

锚点一:嵌在数据块头部 —— 生死契约 (PageHeaderData)

C

/* src/include/storage/bufpage.h */
typedef struct PageHeaderData
{
    /* 导致该页面最后一次修改的 WAL 记录的 LSN */
    XLogRecPtr    pd_lsn;    
    uint16        pd_checksum;
    /* ... 其他字段 ... */
} PageHeaderData;
  • 物理推演:这是 Crash-Safe 的绝对底线。bgwritercheckpointer 在调用底层 fsync() 将 8KB 脏页刷入磁盘前,会拦截并比对:如果该页面的 pd_lsn 大于硬盘上已固化的 WAL LSN,该脏页被物理锁死,绝对禁止落盘

锚点二:嵌在日志包头部 —— 物理回溯链表 (XLogRecord)

C

/* src/include/access/xlogrecord.h */
typedef struct XLogRecord
{
    uint32        xl_tot_len;
    TransactionId xl_xid;
    /* 指向本事务上一条 WAL 记录的 LSN 物理指针 */
    XLogRecPtr    xl_prev;    
    /* ... 其他字段 ... */
} XLogRecord;
  • 物理推演xl_prev 是一个向后指的物理链表。在崩溃恢复时,Redo 引擎不仅能顺着文件往未来重放,如果发生诡异的事务错误,它甚至可以顺着 xl_prev 在 16MB 的物理文件中进行微秒级的逆向时空回溯。

锚点三:嵌在全局控制文件 —— 时空界限 (ControlFileData)

C

/* src/include/catalog/pg_control.h */
typedef struct ControlFileData
{
    /* ... */
    /* Checkpoint 发生时的 REDO 点坐标 */
    XLogRecPtr    checkPointCopy_redo; 
    /* ... */
} ControlFileData;
  • 物理推演:每次 Checkpoint 成功,内核会将当前的 LSN 刻入 pg_control 文件。数据库断电重启时,Redo 引擎像一台无情的推土机,直接从 pg_control 里摸出这个坐标,直接跨过几百 GB 的旧日志,只从这个 LSN 坐标开始向未来推进。

架构师的终极复盘(记忆提炼)

为了便于你在面试和排障时瞬间调取,将 LSN 体系浓缩为以下四句“内核心法”:

  1. 数据类型:它是 64 位整型(uint64),是全局绝对字节坐标,永不回卷。
  2. 文件映射:它通过极其冷酷的位移与求余运算,拼接上 Timeline ID,动态生成了底层 16MB 物理文件的绝对路径。
  3. 防线基石:它打在 PageHeader.pd_lsn 上,形成了日志必须先于数据落盘的生死契约;它打在 pg_control 上,画出了崩溃恢复的物理起跑线。
  4. 算力指标:由于它是绝对字节,两个 LSN 的简单减法(pg_wal_lsn_diff),就能直接算出真实的磁盘 I/O 写入量和主从流复制的精确字节延迟。



极客碎屑一:DBA 的内置算力原语 —— pg_lsn 数据类型

  • 常规认知的死角:很多人以为在 SQL 里查询 LSN,它返回的只是一串带有斜杠的文本字符串(比如 '16/B374D840'),想要计算还得写脚本去转换。
  • 内核的贴心设计:PostgreSQL 极其罕见地在 SQL 层面原生提供了一个专门的数据类型,就叫 pg_lsn
  • 物理碾压: 它不是字符串!内核为 pg_lsn 类型重载了底层的数学运算符。你可以直接在 SQL 里像做小学减法一样对物理字节进行运算: SELECT '16/B374D850'::pg_lsn - '16/B374D840'::pg_lsn; 系统会直接返回 16(代表相差 16 个物理字节)。你甚至可以直接用 <> 来比较两个时空坐标的先后顺序。这是内核研发团队为了方便 DBA 写高级监控脚本而专门开的“底层后门”。

极客碎屑二:时空坐标的人为伪造 —— pg_resetwal -l(上帝之手)

  • 灾难绝境:假设极其罕见的情况发生了——你的 pg_wal 目录被不知情的实习生用 rm -rf 彻底删光了!此时数据库宕机,重启时 Redo 引擎去 pg_control 里摸到了一个 REDO LSN,结果去目录里一找,日志全没了!系统报 PANIC,彻底死亡。
  • 强行篡改宇宙坐标: 此时,高级法医(原厂 DBA)会拔出最后的核按钮工具 pg_resetwal,并且带上一个极其恐怖的参数:-l(强行指定下一个 LSN 物理段号)
    • 底层物理篡改:DBA 会随便编一个比过去所有 LSN 都大得多的新坐标(比如直接把卷尺拉到一百年后),然后用这条命令,极其暴力地将这个虚假的、未来的 LSN 强行注入覆盖到 pg_control 文件里
    • 定性:这叫“强行蒙蔽 Redo 引擎的双眼”。系统重启时,看到这个未来的 LSN,会以为历史日志都已经安全重放完毕,从而放弃寻找丢失的日志,强行拉起数据库。代价是:丢失部分数据的一致性,但保住了全库的命。

极客碎屑三(绝杀过渡):MVCC 幽灵的物理足迹 —— wal_log_hints

这是整个 LSN 体系中最隐秘的角落,也是我们接下来要手撕 MVCC 的完美物理桥梁

  • 物理悖论:我们马上要学到,PostgreSQL 的 MVCC 为了判断一行数据是死是活,会在 8KB 数据页的元组头部打上“Hint Bits(提示位)”。打提示位,意味着 8KB 的内存数据被修改了(变成了脏页)。
  • 冲突爆发:既然页面被修改了,根据 LSN 生死契约,是不是必须生成一条 WAL 日志,并更新页面的 pd_lsn
    • 如果每次打 Hint Bit 都生成日志,WAL 日志量会再次暴涨十倍!
    • 如果不生成日志,这个脏页悄悄落盘了,但它的 pd_lsn 没更新,以后做增量备份或物理校验就会彻底错乱!
  • 架构师的终极开关(wal_log_hints: 在 postgresql.conf 里,这个参数决定了 LSN 与 MVCC 之间的物理妥协。
    • 如果开启(或者开启了数据页 Checksum),内核咬咬牙,只要你因为打 MVCC 提示位修改了页面,哪怕只是改了一个比特,且跨越了 Checkpoint,它就老老实实地生成全页镜像(FPW),并更新 LSN
    • 如果关闭,内核则极其诡异地允许这个页面被修改,但不更新其 LSN,也不生成日志(这叫“静默修改”)。

了解 www.876873.xyz 的更多信息

订阅后即可通过电子邮件收到最新文章。

lsn的结构::等您坐沙发呢!

发表评论