PostgreSQL的预写式日志(wal日志)

Posted 明矾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PostgreSQL的预写式日志(wal日志)相关的知识,希望对你有一定的参考价值。

PostgreSQL通过预写式日志(wal日志)来保证数据不丢失

wal日志记录哪些内容

数据库的哪些操作会被wal日志记录,哪些操作不会被wal日志记录,参考如下:

the following operations are WAL-logged:

  • Changes to pages in the buffer cache (mostly table and index pages) — since it takes some time for the page changed to get to disk.
  • Transactions\' commits and aborts — since a change of the status is done in XACT buffers and it also takes some time for the change to get to disk.
  • File operations (creation and deletion of files and directories, such as creation of files during creation of a table) — since these operations must be synchronous with the changes to data.

The following is not WAL-logged:

  • Operations with unlogged tables — their name is self-explanatory.
  • Operations with temporary tables — logging makes no sense since the lifetime of such tables does not exceed the lifetime of the session that created them.

需要注意的是,哈希索引也不会被wal记录

可以通过如下命令查询wal日志内容列表

pg_waldump -r list

=====================

XLOG
Transaction
Storage
CLOG
Database
Tablespace
MultiXact
RelMap
Standby
Heap2
Heap
Btree
Hash
Gin
Gist
Sequence
SPGist
BRIN
CommitTs
ReplicationOrigin
Generic
LogicalMessage

物理结构

WAL在$PGDATA/pg_wal目录中作为文件存储在磁盘上。每个文件默认大小为16MB,在PostgreSQL 11之前,这个大小需要在编译时指定,现在可以在初始化数据库时通过--wal-segsize指定wal日志大小;

在服务器的共享内存中,为WAL分配了特殊的缓冲区。也可以通过wal_buffers参数指定WAL缓存的大小(默认值为buffer cache的1/32)。

查看当前wal日志的lsn

postgres=# SELECT pg_current_wal_lsn(), pg_current_wal_insert_lsn();
 pg_current_wal_lsn | pg_current_wal_insert_lsn
--------------------+---------------------------
 3/867F6E78         | 3/867F6E78
(1 row)

pg_lsn是一个64位整数,表示记录的开始相对于WAL的开始的字节偏移量

查看当前的lsn对应的wal日志以及日志位置

SELECT file_name, upper(to_hex(file_offset)) file_offset
FROM pg_walfile_name_offset(\'3/867F6E78\');
        file_name         | file_offset
--------------------------+-------------
 000000010000000300000086 | 7F6E78
(1 row)
---查看日志位置
postgres=# SELECT * FROM pg_ls_waldir() WHERE name = \'000000010000000300000086\'; name | size | modification --------------------------+----------+------------------------ 000000010000000300000086 | 16777216 | 2021-05-26 22:21:50+08 (1 row)

pg如何记录wal日志

PostgreSQL有一个扩展,使我们能够查看wal日志内部。

  • 安装扩展

1)开启postgresql 服务

2)进入:contrib/pageinspect 目录

3)运行 :gmake  然后,运行 gmake install

4)再运行 psql , 在psql 状态下,运行:create extension pageinspect

先创建一张表

CREATE TABLE wal(id integer);
INSERT INTO wal VALUES (1);

开始一个事务,并记住插入WAL的位置:

BEGIN;
SELECT pg_current_wal_insert_lsn();
postgres=*# SELECT pg_current_wal_insert_lsn();
 pg_current_wal_insert_lsn
---------------------------
 3/8700FAF8
(1 row)

更新一行数据

 UPDATE wal set id = id + 1;

此更改已被WAL记录,并且插入位置已更改:

 SELECT pg_current_wal_insert_lsn();
postgres=*# SELECT pg_current_wal_insert_lsn();
 pg_current_wal_insert_lsn
---------------------------
 3/8700FBC0
(1 row)

为了确保在WAL记录之前不会将更改后的数据页刷新到磁盘,与该页相关的最后一个WAL记录的LSN存储在页头中:

SELECT lsn FROM page_header(get_raw_page(\'wal\',0));
postgres=*# SELECT lsn FROM page_header(get_raw_page(\'wal\',0));
    lsn
------------
 3/8700FBC0
(1 row)

此时提交事务

 COMMIT;

提交也被WAL记录,并且位置再次更改:

SELECT pg_current_wal_insert_lsn();
postgres=# SELECT pg_current_wal_insert_lsn();
 pg_current_wal_insert_lsn
---------------------------
 3/8700FC20
(1 row)

创建的WAL记录将被一次写入磁盘

SELECT 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
--------------------+---------------------------
 3/8700FC58         | 3/8700FC58
(1 row)

注意,commit的LSN可以小于该函数刚返回的值pg_current_wal_insert_lsn,因为有新的操作记录了新的lsn。

通过lsn,我们可以比较两个lsn之间的wal记录数量(单位是字节),现在查一下begin到commit

SELECT \'3/8700FC20\'::pg_lsn - \'3/8700FAF8\'::pg_lsn;
postgres=# SELECT \'3/8700FC20\'::pg_lsn - \'3/8700FAF8\'::pg_lsn;
 ?column?
----------
      296
(1 row)

可知整个过程在WAL中需要296个字节,这可以评估服务器在一定负载下每单位时间生成的WAL记录的数量。

可以使用pg_waldump查看创建的wal记录情况

/usr/local/pgsql/bin/pg_waldump -p /usr/local/pgsql/data/pg_wal/ -s 3/8700FAF8  -e 3/8700FC20  000000010000000300000087
[postgres@iZbp1fpui5cmgd2buwhk5fZ pg_wal]$ /usr/local/pgsql/bin/pg_waldump -p /usr/local/pgsql/data/pg_wal/ -s 3/8700FAF8  -e 3/8700FC20  000000010000000300000087
rmgr: Heap        len (rec/tot):     65/   197, tx:      58212, lsn: 3/8700FAF8, prev 3/8700FAC0, desc: HOT_UPDATE off 1 xmax 58212 flags 0x40 ; new off 3 xmax 0, blkref #0: rel 1663/12723/25858 blk 0 FPW
rmgr: Standby     len (rec/tot):     54/    54, tx:          0, lsn: 3/8700FBC0, prev 3/8700FAF8, desc: RUNNING_XACTS nextXid 58213 latestCompletedXid 58211 oldestRunningXid 58212; 1 xacts: 58212
rmgr: Transaction len (rec/tot):     34/    34, tx:      58212, lsn: 3/8700FBF8, prev 3/8700FBC0, desc: COMMIT 2021-05-27 09:29:10.860331 CST

 第一行可以看到第一个是HOT_UPDATE操作,与堆资源管理器有关,文件名和页码在blkref字段中指定,此处为1663/12723/25858

 我们查一下

postgres=# SELECT pg_relation_filepath(\'wal\');
 pg_relation_filepath
----------------------
 base/12723/25858
(1 row)

第三行可以看到COMMIT,与事务资源管理器有关。

 这些记录的好处在于我们可以在数据库服务崩溃后恢复数据到任意时间点(PITR)

以上是关于PostgreSQL的预写式日志(wal日志)的主要内容,如果未能解决你的问题,请参考以下文章

数据库如何使用预写式日志(Write Ahead Log, WAL) 解决 IO 问题的?

PostgreSQL的WAL日志管理

分布式技术 之 Write-Ahead Log 预写式日志

pg_resetxlog - 重置一个 PostgreSQL 数据库集群的预写日志以及其它控制内容

PostgreSQL故障恢复能力之检查点(Checkpoint)

我们常听到的WAL到底是什么