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.
需要注意的是,在pg10之前的版本,哈希索引也不會被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)