🚫 Ad Blocker Detected

Please disable your AD blocker to continue using this site. Ads help us keep the content free! please press keyboard F5 to refresh page after disabled AD blocker

請關閉廣告攔截器以繼續使用本網站。廣告有助於我們保證內容免費。謝謝! 關閉後請按 F5 刷新頁面

0%

postgresql WAL (Write-Ahead Logging) 機制

前言

WAL 是一種 Tx Log實踐機制

WAL 核心概念是先寫 tx log,在把資料寫資料,資料的修改必须發生在寫入 Tx Log之後,使用 WAL 紀錄資料庫系統在commit transaction

使用 WAL 機制後我們不需要在每次 Commit Transaction 後就把資料 flush 到 Disk 上(提高IO效率), WAL 需要保證 Dirty Block flush 到 Disk 之前,該 Block 對應 Tx log 紀錄已經 flush 到 Disk 中

因為假如系統突然崩潰我們可以藉由 WAL 寫入 Tx Log 來 roll-forward recovery (REDO)

WAL 寫入時機簡介

LSN序號是一個 globale 變數(透過 info_lck 輕量鎖設計避免同一時間取得同一個 LSN )

寫入資料會經由下面幾個步驟

  1. Log (LSN–Log) 是一個有順序性標誌的紀錄 (可以想像是一個 sequence ) ,一開始存在 RAM 中,在 RAM 中有一個 flushLSN 位置會記錄 log LSN 寫入 Disk 最後位置.
  2. 每個 block 在 pageheader 會有一個欄位 PageXLogRecPtr pd_lsn; 指到 log 紀錄位置,此位置在 Dirty Block 存在 buffer Pool 時就會決定
  3. 最後把 Dirty Block flush 到 Disk 之前 postgresSQL 會檢查是否滿足此條件 pd_lsn <= flushLSN ,確保 Log 已經寫入 Disk 上才會把資料 flush 到資料庫中
  4. 如果是 synchronous_commit = ON 代表同步提交,在 Transaction commit 時會把對應的 Tx Log 馬上 flush 進 Disk中才能返回成功

經過上面步驟我們就可以確保 先寫 Tx Log 歷程,後寫 Dirty Block

假如目前系統某個 Block 操作很頻繁,系統不會 block 等待資料寫入 是因為我們可以一直寫 (LSN–Log) 後面在補寫資料 (兩個不衝突 )

查看系統目前 LSN

經過前面說明我們可以理解 LSN 重要性,

我們可以透過 select pg_current_wal_lsn(); 取得目前系統的 LSN

1
2
3
4
CREATE TABLE tt1(id int);
SELECT pg_current_wal_lsn();
insert into tt1 values (1);
SELECT pg_current_wal_lsn();

執行結果如下,能發現每次 DDL or DML 異動 LSN 都會增長,因為這是我們 Tx log 重要歷程流水號

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
postgres=# CREATE TABLE tt1(id int);
CREATE TABLE
postgres=# SELECT pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/E00D6C8
(1 row)

postgres=# insert into tt1 values (1);
INSERT 0 1
postgres=# SELECT pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/E00D768
(1 row)

postgres=# SELECT pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/E00D768
(1 row)

透過 pg_waldump 查看 WAL log

我們可以透過 pg_waldump

postgresql/data/pg_wal 有一個資料夾裡面會放 WAL log 歷程記錄

我們可以透過 pg_waldump 查詢

  • -s:查詢開始 LSN 編號
  • -e:查詢結束 LSN 編號
1
2
3
4
5
root@ce3649bb9b5e:/var/lib/postgresql/data/pg_wal#  pg_waldump  -s 0/E00D6C8 -e 0/E00D768

rmgr: Heap len (rec/tot): 59/ 59, tx: 1730, lsn: 0/0E00D6C8, prev 0/0E00D530, desc: INSERT+INIT off 1, blkref #0: rel 1663/13067/24677 blk 0
rmgr: Transaction len (rec/tot): 34/ 34, tx: 1730, lsn: 0/0E00D708, prev 0/0E00D6C8, desc: COMMIT 2021-10-07 01:46:16.557911 UTC
rmgr: Standby len (rec/tot): 54/ 54, tx: 0, lsn: 0/0E00D730, prev 0/0E00D708, desc: RUNNING_XACTS nextXid 1731 latestCompletedXid 1729 oldestRunningXid 1730; 1 xacts: 1730

藉由此命令我們就可以很清楚看到 log 歷程,Commit Transation時間,資料新增到哪個區域

因為此資料表沒有 index 所以資料只有新增在 heap table 上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
postgres=# SELECT pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/E057FD0
(1 row)


postgres=# CREATE INDEX ix_id ON tt1(id);
CREATE INDEX
postgres=# SELECT pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/E064950
(1 row)


postgres=# UPDATE tt1
postgres-# SET ID = ID + 1;
UPDATE 1
postgres=# SELECT pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/E064A38
(1 row)

上面跑完後我們可以看到每個執行過程的 LSN 接著我們在透過 pg_waldump 來查詢相關紀錄資訊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
root@ce3649bb9b5e:/var/lib/postgresql/data/pg_wal#  pg_waldump  -s  0/E057FD0 -e  0/E064A38

rmgr: Storage len (rec/tot): 42/ 42, tx: 0, lsn: 0/0E057FD0, prev 0/0E057F98, desc: CREATE base/13067/24690
rmgr: Standby len (rec/tot): 42/ 42, tx: 1770, lsn: 0/0E058018, prev 0/0E057FD0, desc: LOCK xid 1770 db 13067 rel 24690
rmgr: Heap len (rec/tot): 54/ 2282, tx: 1770, lsn: 0/0E058048, prev 0/0E058018, desc: INSERT off 2, blkref #0: rel 1663/13067/1259 blk 5 FPW
rmgr: Btree len (rec/tot): 53/ 2093, tx: 1770, lsn: 0/0E058938, prev 0/0E058048, desc: INSERT_LEAF off 100, blkref #0: rel 1663/13067/2662 blk 2 FPW
rmgr: Btree len (rec/tot): 53/ 5489, tx: 1770, lsn: 0/0E059168, prev 0/0E058938, desc: INSERT_LEAF off 37, blkref #0: rel 1663/13067/2663 blk 1 FPW
rmgr: Btree len (rec/tot): 53/ 3713, tx: 1770, lsn: 0/0E05A6F8, prev 0/0E059168, desc: INSERT_LEAF off 177, blkref #0: rel 1663/13067/3455 blk 4 FPW
rmgr: Heap len (rec/tot): 54/ 7182, tx: 1770, lsn: 0/0E05B580, prev 0/0E05A6F8, desc: INSERT off 48, blkref #0: rel 1663/13067/1249 blk 48 FPW
rmgr: Btree len (rec/tot): 53/ 2773, tx: 1770, lsn: 0/0E05D1A8, prev 0/0E05B580, desc: INSERT_LEAF off 96, blkref #0: rel 1663/13067/2658 blk 13 FPW
rmgr: Btree len (rec/tot): 53/ 3093, tx: 1770, lsn: 0/0E05DC80, prev 0/0E05D1A8, desc: INSERT_LEAF off 150, blkref #0: rel 1663/13067/2659 blk 9 FPW
rmgr: Heap len (rec/tot): 54/ 938, tx: 1770, lsn: 0/0E05E8B0, prev 0/0E05DC80, desc: INSERT off 5, blkref #0: rel 1663/13067/2610 blk 3 FPW
rmgr: Btree len (rec/tot): 53/ 2913, tx: 1770, lsn: 0/0E05EC60, prev 0/0E05E8B0, desc: INSERT_LEAF off 140, blkref #0: rel 1663/13067/2678 blk 1 FPW
rmgr: Btree len (rec/tot): 53/ 2913, tx: 1770, lsn: 0/0E05F7C8, prev 0/0E05EC60, desc: INSERT_LEAF off 141, blkref #0: rel 1663/13067/2679 blk 1 FPW
rmgr: Heap len (rec/tot): 54/ 2658, tx: 1770, lsn: 0/0E060348, prev 0/0E05F7C8, desc: INSERT off 43, blkref #0: rel 1663/13067/2608 blk 55 FPW
rmgr: Btree len (rec/tot): 53/ 7261, tx: 1770, lsn: 0/0E060DB0, prev 0/0E060348, desc: INSERT_LEAF off 224, blkref #0: rel 1663/13067/2673 blk 39 FPW
rmgr: Btree len (rec/tot): 53/ 7261, tx: 1770, lsn: 0/0E062A28, prev 0/0E060DB0, desc: INSERT_LEAF off 149, blkref #0: rel 1663/13067/2674 blk 45 FPW
rmgr: XLOG len (rec/tot): 49/ 109, tx: 1770, lsn: 0/0E0646A0, prev 0/0E062A28, desc: FPI , blkref #0: rel 1663/13067/24690 blk 1 FPW
rmgr: XLOG len (rec/tot): 49/ 129, tx: 1770, lsn: 0/0E064710, prev 0/0E0646A0, desc: FPI , blkref #0: rel 1663/13067/24690 blk 0 FPW
rmgr: Heap len (rec/tot): 188/ 188, tx: 1770, lsn: 0/0E064798, prev 0/0E064710, desc: INPLACE off 2, blkref #0: rel 1663/13067/1259 blk 5
rmgr: Transaction len (rec/tot): 242/ 242, tx: 1770, lsn: 0/0E064858, prev 0/0E064798, desc: COMMIT 2021-10-07 09:59:29.422711 UTC; inval msgs: catcache 50 catcache 49 catcache 50 catcache 49 catcache 7 catcache 6 catcache 32 relcache 24677 relcache 24690 relcache 24690 relcache 24677 snapshot 2608
rmgr: Standby len (rec/tot): 50/ 50, tx: 0, lsn: 0/0E064950, prev 0/0E064858, desc: RUNNING_XACTS nextXid 1771 latestCompletedXid 1770 oldestRunningXid 1771
rmgr: Heap len (rec/tot): 69/ 69, tx: 1771, lsn: 0/0E064988, prev 0/0E064950, desc: UPDATE off 2 xmax 1771 ; new off 3 xmax 0, blkref #0: rel 1663/13067/24677 blk 0
rmgr: Btree len (rec/tot): 64/ 64, tx: 1771, lsn: 0/0E0649D0, prev 0/0E064988, desc: INSERT_LEAF off 2, blkref #0: rel 1663/13067/24690 blk 1
rmgr: Transaction len (rec/tot): 34/ 34, tx: 1771, lsn: 0/0E064A10, prev 0/0E0649D0, desc: COMMIT 2021-10-07 09:59:29.458812 UTC

我們會發現上面 tx log 長很多,大部分都是在新建立 Btree 跟新增 Btree 中的資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
rmgr: Storage     len (rec/tot):     42/    42, tx:          0, lsn: 0/0E057FD0, prev 0/0E057F98, desc: CREATE base/13067/24690
rmgr: Standby len (rec/tot): 42/ 42, tx: 1770, lsn: 0/0E058018, prev 0/0E057FD0, desc: LOCK xid 1770 db 13067 rel 24690
rmgr: Heap len (rec/tot): 54/ 2282, tx: 1770, lsn: 0/0E058048, prev 0/0E058018, desc: INSERT off 2, blkref #0: rel 1663/13067/1259 blk 5 FPW
rmgr: Btree len (rec/tot): 53/ 2093, tx: 1770, lsn: 0/0E058938, prev 0/0E058048, desc: INSERT_LEAF off 100, blkref #0: rel 1663/13067/2662 blk 2 FPW
rmgr: Btree len (rec/tot): 53/ 5489, tx: 1770, lsn: 0/0E059168, prev 0/0E058938, desc: INSERT_LEAF off 37, blkref #0: rel 1663/13067/2663 blk 1 FPW
rmgr: Btree len (rec/tot): 53/ 3713, tx: 1770, lsn: 0/0E05A6F8, prev 0/0E059168, desc: INSERT_LEAF off 177, blkref #0: rel 1663/13067/3455 blk 4 FPW
rmgr: Heap len (rec/tot): 54/ 7182, tx: 1770, lsn: 0/0E05B580, prev 0/0E05A6F8, desc: INSERT off 48, blkref #0: rel 1663/13067/1249 blk 48 FPW
rmgr: Btree len (rec/tot): 53/ 2773, tx: 1770, lsn: 0/0E05D1A8, prev 0/0E05B580, desc: INSERT_LEAF off 96, blkref #0: rel 1663/13067/2658 blk 13 FPW
rmgr: Btree len (rec/tot): 53/ 3093, tx: 1770, lsn: 0/0E05DC80, prev 0/0E05D1A8, desc: INSERT_LEAF off 150, blkref #0: rel 1663/13067/2659 blk 9 FPW
rmgr: Heap len (rec/tot): 54/ 938, tx: 1770, lsn: 0/0E05E8B0, prev 0/0E05DC80, desc: INSERT off 5, blkref #0: rel 1663/13067/2610 blk 3 FPW
rmgr: Btree len (rec/tot): 53/ 2913, tx: 1770, lsn: 0/0E05EC60, prev 0/0E05E8B0, desc: INSERT_LEAF off 140, blkref #0: rel 1663/13067/2678 blk 1 FPW
rmgr: Btree len (rec/tot): 53/ 2913, tx: 1770, lsn: 0/0E05F7C8, prev 0/0E05EC60, desc: INSERT_LEAF off 141, blkref #0: rel 1663/13067/2679 blk 1 FPW
rmgr: Heap len (rec/tot): 54/ 2658, tx: 1770, lsn: 0/0E060348, prev 0/0E05F7C8, desc: INSERT off 43, blkref #0: rel 1663/13067/2608 blk 55 FPW
rmgr: Btree len (rec/tot): 53/ 7261, tx: 1770, lsn: 0/0E060DB0, prev 0/0E060348, desc: INSERT_LEAF off 224, blkref #0: rel 1663/13067/2673 blk 39 FPW
rmgr: Btree len (rec/tot): 53/ 7261, tx: 1770, lsn: 0/0E062A28, prev 0/0E060DB0, desc: INSERT_LEAF off 149, blkref #0: rel 1663/13067/2674 blk 45 FPW
rmgr: XLOG len (rec/tot): 49/ 109, tx: 1770, lsn: 0/0E0646A0, prev 0/0E062A28, desc: FPI , blkref #0: rel 1663/13067/24690 blk 1 FPW
rmgr: XLOG len (rec/tot): 49/ 129, tx: 1770, lsn: 0/0E064710, prev 0/0E0646A0, desc: FPI , blkref #0: rel 1663/13067/24690 blk 0 FPW
rmgr: Heap len (rec/tot): 188/ 188, tx: 1770, lsn: 0/0E064798, prev 0/0E064710, desc: INPLACE off 2, blkref #0: rel 1663/13067/1259 blk 5
rmgr: Transaction len (rec/tot): 242/ 242, tx: 1770, lsn: 0/0E064858, prev 0/0E064798, desc: COMMIT 2021-10-07 09:59:29.422711 UTC; inval msgs: catcache 50 catcache 49 catcache 50 catcache 49 catcache 7 catcache 6 catcache 32 relcache 24677 relcache 24690 relcache 24690 relcache 24677 snapshot 2608

而在最後 Update 時會對於 Heap 跟 Btree 異動,原因是我們異動 ID 這個欄位,在 Btree 和 Heap 資料表都有

詳細原因我有寫一篇文章 postgresql HOT (heap only tuple) update 深入淺出

1
2
3
rmgr: Heap        len (rec/tot):     69/    69, tx:       1771, lsn: 0/0E064988, prev 0/0E064950, desc: UPDATE off 2 xmax 1771 ; new off 3 xmax 0, blkref #0: rel 1663/13067/24677 blk 0
rmgr: Btree len (rec/tot): 64/ 64, tx: 1771, lsn: 0/0E0649D0, prev 0/0E064988, desc: INSERT_LEAF off 2, blkref #0: rel 1663/13067/24690 blk 1
rmgr: Transaction len (rec/tot): 34/ 34, tx: 1771, lsn: 0/0E064A10, prev 0/0E0649D0, desc: COMMIT 2021-10-07 09:59:29.458812 UTC

pg_waldump 屬性簡介

下面我針對於幾個我認為比較重要的欄位來說明

  • rmgr: PostgreSQL 把 WAL 歸類許多不同的種類,除了 Heap 還有 Btree,Transaction….等等。我們可以透過 pg_waldump --rmgr=list 查看所有資源
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    root@ce3649bb9b5e:/var/lib/postgresql/data/pg_wal# pg_waldump  --rmgr=list
    XLOG
    Transaction
    Storage
    CLOG
    Database
    Tablespace
    MultiXact
    RelMap
    Standby
    Heap2
    Heap
    Btree
    Hash
    Gin
    Gist
    Sequence
    SPGist
    BRIN
    CommitTs
    ReplicationOrigin
    Generic
    LogicalMessage
  • tx: 1770 Transaction 編號(同一個編號代表同一個Transaction)
  • lsn: 0/0E0649D0 目前記錄的LSN
  • prev: 0/0E064950 前一個紀錄的LSN

小結

PostgreSQL WAL 用一句話來解釋是 先寫 Tx Log 歷程,後寫 Dirty Block ,但實際過程中會有許多細節需要考慮 (ex: checkpoint機制,資料庫突然崩潰…等等)

透過 pg_waldump 可以讓我們更好學習 WAL 檔案中的內容

__此文作者__:Daniel Shih(石頭)
__此文地址__: https://isdaniel.github.io/postgresql-wal-introduce/
__版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!

如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^