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

PostgreSQL 基础知识:WAL 文件和序列号

译者:阎书利

Postgres预写日志(Write Ahead Log, WAL)是数据库的一个功能组件。WAL使许多关键功能成为可能,比如时间点恢复备份、从事件中恢复、流复制等等。有时候,深入数据库的人员需要直接使用WAL文件进行诊断或恢复。

最近在与Crunchy Data的一位客户合作时,我遇到了一种情况,在这种情况下,理解名称和序列号非常重要。在与几个致力于Postgres项目的同事一起工作时,我收集了一些关于WAL内部细节的笔记。今天的目标是研究WAL的LSN和命名约定,以帮助用户更好地理解WAL文件。

日志序列号

PostgreSQL中的事务创建WAL记录,这些记录最终被附加到WAL日志(文件)中。插入发生的位置称为日志序列号(LSN)。可以比较LSN(类型为 pg_lsn)的值,以确定在两个不同偏移量(以字节为单位)之间生成的WAL的数量。当以这种方式使用时,重要的是要知道,如果使用多个WAL日志,则计算假设使用了完整的WAL段(16MB)。和这里使用的计算类似的计算通常被用于确定replica的延迟。

LSN是一个64位整数,表示在预写日志流中的位置。这个64位整数被分成两个段(高32位和低32位)。格式为两个十六进制数,中间用斜杠(XXXXXXXX/YYZZZZZZ)隔开。“X”代表LSN的高32位,“Y”是低32位部分的高8位。“Z”表示文件中的偏移位置。每个元素是一个十六进制数。'X’和’Y’值在默认PostgreSQL部署的WAL文件的第二部分中使用。

WAL文件

WAL的文件名格式为 TTTTTTTTXXXXXXXXYYYYYYYY。这里’T’是时间线,'X’是LSN的高32位,'Y’是LSN的低32位。

首先查看当前的WAL LSN并插入LSN。pg_current_wal_lsn是最后一次写操作的位置。pg_current_wal_insert_lsn是逻辑位置,反映缓冲区中尚未写入磁盘的数据。还有一个刷新值,显示已写入持久存储的内容。

[postgres] # select pg_current_wal_lsn(), pg_current_wal_insert_lsn(); pg_current_wal_lsn | pg_current_wal_insert_lsn --------------------+--------------------------- 76/7D000000 | 76/7D000028 (1 row)

尽管您可以根据上面的输出猜出WAL文件的名称,但最好还是使用这个函数:pg_walfile_name

[postgres] # select pg_walfile_name('76/7D000028'); pg_walfile_name -------------------------- 00000001000000760000007D (1 row)

查看文件系统,我们可以看到段00000001000000760000007D确实是最近修改的文件。注意,如果数据库是空闲的,那么00000001000000760000007D可能是最老的文件(基于O/S最后修改日期)。这可能是因为PostgreSQL重用了旧的WAL段。在pg_wal_switch期间,旧的文件被重命名。修改的O/S日期/时间只会在文件写入时更改。在ls命令中使用-i将显示文件的inode(第一列)。当文件被重用时,这个数字不会改变,因为文件只是被重命名。

$ ls -larti 00* 169034323 -rw------- 1 bpace staff 376 Jan 30 09:13 0000000100000075000000A0.00000060.backup 169564733 -rw------- 1 bpace staff 16777216 Feb 13 15:49 00000001000000760000007E 167120667 -rw------- 1 bpace staff 16777216 Feb 13 15:50 00000001000000760000007F 167120673 -rw------- 1 bpace staff 16777216 Feb 13 16:00 00000001000000760000007B 167120686 -rw------- 1 bpace staff 16777216 Feb 13 16:18 00000001000000760000007C 169564722 -rw------- 1 bpace staff 16777216 Feb 13 16:18 00000001000000760000007D

让我们创建一个小表并执行一个WAL切换。

[postgres] # create table test (a char(1)); CREATE TABLE Time: 23.770 ms [postgres] # select pg_switch_wal(); pg_switch_wal -------------- 76/7D018FD8 (1 row)

再次查看文件,我们现在看到00000001000000760000007D文件已经更新(从O/S角度更改了日期/时间)。由于在后台发生了其他一些项目,下一个段00000001000000760000007E也在pg_switch_wal之后收到了一些写操作。

$ ls -larti 00* 169034323 -rw------- 1 bpace staff 376 Jan 30 09:13 0000000100000075000000A0.00000060.backup 167120667 -rw------- 1 bpace staff 16777216 Feb 13 15:50 00000001000000760000007F 167120673 -rw------- 1 bpace staff 16777216 Feb 13 16:00 000000010000007600000080 167120686 -rw------- 1 bpace staff 16777216 Feb 13 16:18 000000010000007600000081 169564722 -rw------- 1 bpace staff 16777216 Feb 13 16:24 00000001000000760000007D 169564733 -rw------- 1 bpace staff 16777216 Feb 13 16:24 00000001000000760000007E

在新创建的表中,插入一条记录。确保数据库是平稳的,并在更改之前和之后获取当前的WAL LSN。请注意,我们将1个字节(‘a’)插入到具有单列的单个表中。

[postgres] # select pg_current_wal_lsn(), pg_current_wal_insert_lsn(); pg_current_wal_lsn | pg_current_wal_insert_lsn --------------------+--------------------------- 76/7E000060 | 76/7E000060 (1 row) [postgres] # insert into test (a) values ('a'); INSERT 0 1 [postgres] # select pg_current_wal_lsn(), pg_current_wal_insert_lsn(); pg_current_wal_lsn | pg_current_wal_insert_lsn --------------------+--------------------------- 76/7E000108 | 76/7E000108 (1 row)

使用这两个LSN位置,我们可以计算INSERT生成的WAL的数量(在本例中为168字节)。

[postgres] # select '76/7E000108'::pg_lsn - '76/7E000060'::pg_lsn size_bytes; size_bytes ------------ 168 (1 row)

获取当前的WAL LSN,然后WAL插入LSN,再执行一次切换。然后列出文件。

[postgres] # select pg_switch_wal(); pg_switch_wal --------------- 76/7E0001D0 (1 row) $ ls -larti 00* 169034323 -rw------- 1 bpace staff 376 Jan 30 09:13 0000000100000075000000A0.00000060.backup 167120673 -rw------- 1 bpace staff 16777216 Feb 13 16:00 000000010000007600000080 167120686 -rw------- 1 bpace staff 16777216 Feb 13 16:18 000000010000007600000081 169564722 -rw------- 1 bpace staff 16777216 Feb 13 16:24 000000010000007600000082 169564733 -rw------- 1 bpace staff 16777216 Feb 13 16:26 00000001000000760000007E 167120667 -rw------- 1 bpace staff 16777216 Feb 13 16:26 00000001000000760000007F

使用我们在前面步骤中捕获的信息,使用pg_waldump获取人类可读的WAL段内容摘要。在下面的命令中,指定了起始位置(-s)和结束位置(-e)以及WAL文件名(00000001000000760000007E)。开始位置是INSERT之前的current_wal_lsn,结束位置是插入之后的current_wal_lsn。之前,仅使用lsn,我们确定有168个字节从事务写入到WAL。查看waldump可以发现INSERT占用103字节(INSERT占用57字节,COMMIT占用46字节)。

sh-4.4$ pg_waldump -s 76/7E000060 -e 76/7E000108 00000001000000760000007E rmgr: Heap len (rec/tot): 57/ 57, tx: 59555584, lsn: 76/7E000060, prev 76/7E000028, desc: INSERT+INIT off 1 flags 0x08, blkref #0: rel 1663/5/53434 blk 0 rmgr: Transaction len (rec/tot): 46/ 46, tx: 59555584, lsn: 76/7E0000A0, prev 76/7E000060, desc: COMMIT 2023-02-13 16:25:19.441483 EST rmgr: Standby len (rec/tot): 50/ 50, tx: 0, lsn: 76/7E0000D0, prev 76/7E0000A0, desc: RUNNING_XACTS nextXid 59555585 latestCompletedXid 59555584 oldestRunningXid 59555585

从INSERT之前的点查看整个WAL文件,可以看到INSERT本身,然后是COMMIT。最后,记录检查点和执行的pg_wal_switch()切换。如果wal_level设置为replica或更高,则添加RUNNING_XACTS项。RUNNING_XACTS条目捕获当前快照(活动事务)的详细信息。最后一个条目SWITCH是执行的pg_switch_wal。

sh-4.4$ pg_waldump 00000001000000760000007E rmgr: Standby len (rec/tot): 50/ 50, tx: 0, lsn: 76/7E000028, prev 76/7D018FC0, desc: RUNNING_XACTS nextXid 59555584 latestCompletedXid 59555583 oldestRunningXid 59555584 rmgr: Heap len (rec/tot): 57/ 57, tx: 59555584, lsn: 76/7E000060, prev 76/7E000028, desc: INSERT+INIT off 1 flags 0x08, blkref #0: rel 1663/5/53434 blk 0 rmgr: Transaction len (rec/tot): 46/ 46, tx: 59555584, lsn: 76/7E0000A0, prev 76/7E000060, desc: COMMIT 2023-02-13 16:25:19.441483 EST rmgr: Standby len (rec/tot): 50/ 50, tx: 0, lsn: 76/7E0000D0, prev 76/7E0000A0, desc: RUNNING_XACTS nextXid 59555585 latestCompletedXid 59555584 oldestRunningXid 59555585 rmgr: Standby len (rec/tot): 50/ 50, tx: 0, lsn: 76/7E000108, prev 76/7E0000D0, desc: RUNNING_XACTS nextXid 59555585 latestCompletedXid 59555584 oldestRunningXid 59555585 rmgr: XLOG len (rec/tot): 114/ 114, tx: 0, lsn: 76/7E000140, prev 76/7E000108, desc: CHECKPOINT_ONLINE redo 76/7E000108; tli 1; prev tli 1; fpw true; xid 0:59555585; oid 61620; multi 799; offset 1657; oldest xid 716 in DB 1; oldest multi 1 in DB 1; oldest/newest commit timestamp xid: 0/0; oldest running xid 59555585; online rmgr: XLOG len (rec/tot): 24/ 24, tx: 0, lsn: 76/7E0001B8, prev 76/7E000140, desc: SWITCH

结束

我希望您不需要太频繁地钻研WAL,希望您的pg_wal_switch事件很少。提醒一下,各种initdb选项和编译时选项可以改变计算和假设的结果。WAL是一个复杂的主题,任何与WAL相关的元素都应该非常小心

参考链接:https://www.crunchydata.com/blog/postgres-wal-files-and-sequuence-numbers

作者:Brian Pace

http://www.jsqmd.com/news/480448/

相关文章:

  • KingFusion 关系库查询核心:SQLQuery 与 AsynSQLQuery 函数全解析
  • 2001-2024年 我国农作物分布栅格数据(小麦、玉米、水稻、甘蔗等)
  • 352. Java IO API - Java 文件操作:java.io.File 与 java.nio.file 功能对比 - 4
  • M2LOrder模型内网穿透部署方案:安全访问本地情感分析服务
  • 学术写作新姿势:用万象熔炉·丹青幻境快速生成专业图表
  • 人工智能应用- 机器做梦:05.动态梦境:一步步走进幻想
  • 纯本地多模态AI怎么搭?mPLUG-Owl3-2B镜像免配置部署一文详解
  • 人工智能应用- 机器做梦:06.动态梦境:小结
  • 哪里可以回收山东一卡通?高效、安全又省心! - 团团收购物卡回收
  • YOLOv8.3 动态锚框进阶:无需预聚类,物流包裹多尺度检测 AP+3.2%(代码复用性强)
  • Phi-3-Mini-128K实操手册:Streamlit文件上传+PDF解析+128K喂入全流程
  • 零基础也能搞定!YOLOv5 模型训练全攻略:参数设置详解 + 训练过程监控(2026 避坑版)
  • 山东一卡通回收靠谱吗?小白必看的交易技巧 - 团团收购物卡回收
  • 硬核入门!Python爬虫实战:爬取豆瓣读书TOP250,书名+评分+简介,一键生成精美Excel书单(2026避坑版)
  • 基于AI浏览器架构缺陷的钓鱼攻击机理与防御重构
  • Harmonyos应用实例97:找次品动画演示
  • 山东一卡通回收指南:2026最佳平台与流程解读 - 团团收购物卡回收
  • Harmonyos应用实例98:约分和通分工具
  • 山东一卡通回收的秘密:如何快速变现闲置卡? - 团团收购物卡回收
  • Harmonyos应用实例99:表面积探索器
  • UG NX 曲率梳分析精要
  • UG NX 曲面连续性分析
  • 题111111
  • 19705成绩统计
  • Windows平台升级Node.js
  • Cursor中skills用法
  • 你们在OpenClaw上的token消耗如何?
  • 2026年靠谱的铝艺屏风品牌推荐:铝艺围栏/铝艺庭院门/郑州铝艺庭院门源头厂家推荐 - 行业平台推荐
  • 2026年知名的铁艺楼梯公司推荐:铁艺护栏口碑好的厂家推荐 - 行业平台推荐
  • 为什么有的程序员一聊到Vibe Coding让 “文科生”接触到了编程会“破防”?