1. 數據庫物理存儲結構
數據庫文件默認保存在initdb創建的數據目錄中,它包含數據文件、參數文件、控制文件、數據庫運行日志及WAL日志文件。PG不支持裸設備和塊設備。
1.1 軟件目錄結構
$ ls -l /ups/app/pgsql-11/
total 16
drwxr-xr-x 2 root root 4096 Oct 8 15:06 bin # 二進制可執行文件,是PG數據庫的所有相關命令工具
drwxr-xr-x 6 root root 4096 Oct 8 15:06 include # 頭文件目錄
drwxr-xr-x 4 root root 4096 Oct 8 15:06 lib # 動態庫目錄,PostgreSQL運行所需要的動態庫
drwxr-xr-x 8 root root 4096 Oct 8 15:06 share # 文檔和配置模板文件,擴展插件的SQL文件在此目錄下的extension中
$
1.2 文件布局
數據庫集簇所使用的配置和數據文件都被一起存儲在集簇的數據目錄里, 通常用PGDATA
來引用。
PGDATA
目錄包含幾個子目錄以及一些控制文件
PGDATA
的內容
項 | 描述 |
---|---|
postgresql.conf | 數據庫實例主要配置文件 |
pg_hba.conf | 認證配置文件 |
pg_ident.conf | 認證方式的用戶映射文件 |
PG_VERSION |
一個包含PostgreSQL主版本號的文件 |
base |
包含每個數據庫對應的子目錄的子目錄 |
current_logfiles |
記錄當前被日志收集器寫入的日志文件的文件 |
global |
包含集簇范圍的表的子目錄,比如pg_database |
pg_commit_ts |
包含事務提交時間戳數據的子目錄 |
pg_dynshmem |
包含被動態共享內存子系統所使用的文件的子目錄 |
pg_logical |
包含用於邏輯復制的狀態數據的子目錄 |
pg_multixact |
包含多事務(multi-transaction)狀態數據的子目錄(用於共享的行鎖) |
pg_notify |
包含LISTEN/NOTIFY狀態數據的子目錄 |
pg_replslot |
包含復制槽數據的子目錄 |
pg_serial |
包含已提交的可序列化事務信息的子目錄 |
pg_snapshots |
包含導出的快照的子目錄 |
pg_stat |
包含用於統計子系統的永久文件的子目錄 |
pg_stat_tmp |
包含用於統計信息子系統的臨時文件的子目錄 |
pg_subtrans |
包含子事務狀態數據的子目錄 |
pg_tblspc |
包含指向表空間的符號鏈接的子目錄 |
pg_twophase |
包含用於2階段提交分布式事務狀態文件的子目錄 |
pg_wal |
包含 WAL (預寫日志)文件的子目錄 |
pg_xact |
包含事務提交狀態數據的子目錄(pg9以前為pg_clog目錄)-Commit log |
postgresql.auto.conf |
一個用於存儲由ALTER SYSTEM 設置的配置參數的文件 |
postmaster.opts |
一個記錄服務器最后一次啟動時使用的命令行參數的文件 |
postmaster.pid |
一個鎖文件,記錄着當前的 postmaster 進程ID(PID)、集簇數據目錄路徑、postmaster啟動時間戳、端口號、Unix域套接字目錄路徑(Windows上為空)、第一個可用的listen_address(IP地址或者* ,或者為空表示不在TCP上監聽)以及共享內存段ID(服務器關閉后該文件不存在) |
對於集簇里的每個數據庫,在PGDATA/base
里都有一個子目錄對應, 子目錄的名字為該數據庫在pg_database
里的 OID。
1.3 數據文件
PG中,每個表和索引單獨一個文件存儲。對於大小超出1GB表數據文件,默認情況下,自動切分多個文件存儲。用OID.<順序號>來命名(pg_class.relfilenode.1)。
在PG中,保存在磁盤中的數據塊稱為page,而內存中的數據塊稱為Buffer,表和索引稱為 Relation ,行稱為 Tuple。數據的讀寫是以 Page 為最小單位, 每個Page 默認大小為 8kB ,在編譯 PostgreSQL 時指定--with-blocksize=BLOCKSIZE
選項決定 Page 的大小。每個表文件由多個 BLCKSZ 字節大小的 Page 組成, 每個 Page 包含若干 Tuple 。
1.3.1 表和索引(Page)
每個表和索引都存儲在獨立的文件里。對於普通關系,這些文件以表或索引的filenode號命名,它可以在pg_class
.relfilenode
中找到。但是對於臨時關系,文件名的形式為tBBB_FFF
,其中BBB
是創建該文件的后台的后台ID,FFF
是文件節點號。在每種情況下,在主文件(a/k/a 主分支)之外,每個表和索引有一個空閑空間映射,它存儲關系中可用空閑空間的信息。空閑空間映射存儲在一個文件中,該文件以節點號加上后綴_fsm
命名。表還有一個可見性映射,存儲在一個后綴為_vm
的分支中,它用於跟蹤哪些頁面已知含有非死亡元組。可見性映射將在中進一步描述。不被日志記錄的表和索引還有第三個分支,即初始化分支,它存儲在后綴為_init
的分支中。
在表或者索引超過 1GB 之后,它就被划分成1G大小的段。 第一個段的文件名和文件節點相同;隨后的段被命名為 filenode.1、filenode.2等等。這樣的安排避免了在某些有文件大小限制的平台上的問題(實際上,1GB只是默認的段尺寸。段尺寸可以在編譯PostgreSQL時使用配置選項--with-blocksize=BLOCKSIZE
進行調整)。
page的物理位置在$PGDATA/BASE/DATABASE_OID/PG_CLASS.RELFILENODE
-- 返回對象oid
SELECT relfilenode FROM pg_class WHERE relname='test_t1';
-- 返回文件路徑
SELECT pg_relation_filepath('test_t1');
-- 數據根目錄 $PGDATA
show data_directory;
表物理文件
每個表對應三個文件
- 一個是存儲數據的文件,文件名是該表的OID
- 一個是管理表空閑空間的文件,文件名為OID_fsm
- 一個是管理表塊可見性的文件。文件名是OID_vm
- 13881是數據庫對象的OID
- 16387是表對象的OID
數據塊結構
塊頭信息,它記錄塊中各個數據行指針,實際的數據行內容從塊尾向前填充,行數據指針和行數據之間的區間時空閑空間。
塊頭信息包含
- 塊的checksum值
- 空閑空間的起始地址和結束位置
- 特殊數據的起始位置
行指針是一個32位的數字
- 行內容的偏移量,占用15bit
- 指針的標識,占用2bit
- 行內容的長度,占用15bit
行指針長度15bit,其最大偏移量2^15=32768,那么塊的最大大小32768.
1.3.2 Tuple布局
假設一個字節8位,item(項)指的是存儲在一個頁面里的獨立數據值。如:在表中,一個項是一行記錄;在索引中,一個項是一條索引記錄。
頁面組成
項 | 描述 |
---|---|
PageHeaderData | 24字節長。包含關於頁面的一般信息,包括空閑空間指針。 |
ItemIdData | 一個記錄(偏移量,長度)對的數組,指向實際項。每個項 4 字節。 |
Free space | 未分配的空間(空閑空間)。新項指針從這個區域的開頭開始分配,新項從其結尾開始分配。 |
Items | 實際的項本身。 |
Special space | 索引訪問模式相關的數據。不同的索引訪問方式存放不同的數據。在普通表中為空。 |
每個頁面的頭24個字節組成頁頭(PageHeaderData
),在頁頭后面是項標識符(ItemIdData
),每個占用四個字節。
PageHeaderData布局
域 | 類型 | 長度 | 描述 |
---|---|---|---|
pd_lsn | PageXLogRecPtr | 8 bytes | LSN: 最后修改這個頁面的WAL記錄最后一個字節后面的第一個字節 |
pd_checksum | uint16 | 2 bytes | 頁面校驗碼 |
pd_flags | uint16 | 2 bytes | 標志位 |
pd_lower | LocationIndex | 2 bytes | 到空閑空間開頭的偏移量 |
pd_upper | LocationIndex | 2 bytes | 到空閑空間結尾的偏移量 |
pd_special | LocationIndex | 2 bytes | 到特殊空間開頭的偏移量 |
pd_pagesize_version | uint16 | 2 bytes | 頁面大小和布局版本號信息 |
pd_prune_xid | TransactionId | 4 bytes | 頁面上最老未刪除XMAX,如果沒有則為0 |
所有細節都可以在src/include/storage/bufpage.h
中找到。
表和序列都使用一種叫做 HeapTupleHeaderData
的結構。
表行組成
行有一個定長的頭部(在大多數機器上占據 23 個字節), 后面跟着一個可選的空值位圖、一個可選的對象 ID 域以及用戶數據。
HeapTupleHeaderData
結構:
域 | 類型 | 長度 | 描述 |
---|---|---|---|
t_xmin | TransactionId | 4 bytes | 插入XID標志 |
t_xmax | TransactionId | 4 bytes | 刪除XID標志 |
t_cid | CommandId | 4 bytes | 插入和/或刪除CID標志(覆蓋t_xvac) |
t_xvac | TransactionId | 4 bytes | VACUUM操作移動一個行版本的XID |
t_ctid | ItemPointerData | 6 bytes | 當前版本的TID或者指向更新的行版本 |
t_infomask2 | uint16 | 2 bytes | 一些屬性,加上多個標志位 |
t_infomask | uint16 | 2 bytes | 多個標志位 |
t_hoff | uint8 | 1 byte | 到用戶數據的偏移量 |
所有細節都可以在src/include/access/htup_details.h
中找到
空閑空間映射
每一個堆和索引關系(除了哈希索引)都有一個空閑空間映射(Free Space Map-FSM)來保持對關系中可用空間的跟蹤。它伴隨着主關系數據被存儲在一個獨立的關系分支中,以關系的文件節點號加上一個_fsm
后綴命名。例如,如果一個關系的文件節點是12345,那么FSM被存儲在一個名為12345_fsm
的文件中,該文件與主關系文件在同一個目錄中。
空閑空間映射被組織成一棵FSM頁面的樹。底層FSM頁面存儲了在每一個堆(或索引)頁面中可用的空閑空間,對於每一個這樣的頁面使用一個字節來表示。上層FSM頁面則聚集來自於下層頁面的信息。
在每一個FSM頁面中是一個二叉樹,存儲在一個數組中,每一個節點一個字節。每個葉節點表示一個堆頁面或者一個下層FSM頁面。在每一個非葉節點中存儲了它孩子節點中的最大值。因此葉節點中的最大值被存儲在根中。
關於如何構建、更新和搜索FSM的更多信息請參考src/backend/storage/freespace/README
。pg_freespace()函數可以用來檢查存儲在空閑空間映射中的信息。
-- 根據 FSM,返回由blkno指定的關系頁面上的空閑空間總量。
pg_freespace(rel regclass IN, blkno bigint IN) 返回 int2
-- 根據 FSM,顯示關系的每個頁面上的空閑空間總量。
pg_freespace(rel regclass IN, blkno OUT bigint, avail OUT int2)
-- 示例
SELECT * FROM pg_freespace('student');
可見性映射
每一個堆關系都有一個可見性映射(VM)用來跟蹤只包含已知對所有活動事務可見的元組或未被凍結的元組的頁面。它隨着主關系數據被存儲在一個獨立的關系分支中, 以該關系的文件節點號加上一個_vm
后綴來命名。例如, 如果一個關系的文件節點為12345,其VM被存儲在名為12345_vm
的文件中, 該文件域主關系文件在同一個目錄中。注意索引沒有VM。
可見性映射僅為每個堆頁面存儲兩個位。第一位如果被設置,表示該頁面上的元組都是可見的,或者換句話說該頁面不含有任何需要被清理的元組。這些信息也可以被使用覆蓋索引返回查詢結果。第二位如果被設置,表示該頁面上的元組都已經被凍結。這也意味着防回卷清理操作也不需要重新訪問該頁面。
pg_visibility模塊可以被用來檢查存儲在可見性映射中的信息。
Lazy VACUUM使用可見性映射文件中標識的數據塊進行快速數據清理工作。
1.4 控制文件
它主要包含初始化靜態信息、WAL及檢查點的動態信息、一些配置信息
控制文件路徑
# 控制文件路徑
ls -tr ${PGDATA}/global/pg_control
查看控制文件內容
# 查看控制文件內容
${PGHOME}/bin/pg_controldata
1.5 日志文件
1.5.1 文件種類
- PG運行日志:服務運行過程日志信息(文件路徑:
$PGDATA/log/*.log
,pg10之前為$PGDATA/pg_log
) - WAL日志:記錄數據變更記錄(
$PGDATA/pg_wal/
,pg10之前為$PGDATA/pg_xlog
) - 事務提交日志:(
$PGDATA/pg_xact/
,pg10之前為$PGDATA/pg_clog
)
運行日志
默認沒有開啟,運行日志記錄服務器與DB的狀態,比如各種Error信息,定位慢查詢SQL,數據庫的啟動關閉信息,發生checkpoint過於頻繁等的告警信息。該日志有.csv格式和.log。
1.5.2 WRITE-AHEAD LOG(WAL)
重做日志是數據庫的重要組成部分,存儲了數據庫系統中所有更改和操作的歷史,以確保數據庫不會因為故障(例如掉電或其他導致服務器崩潰的故障)而丟失數據。在PostgreSQL(以下簡稱PG)中,重做日志文件稱為Write Ahead Log(以下簡稱WAL),在較低(pg10以前)的版本中,也稱為xlog。當數據庫異常退出時,在數據庫重啟后,通過讀取wal日志前滾操作確保數據一致性和完整性。
1.5.2.1 相關術語
-
REDO log:通常稱為重做日志,在寫入數據文件前,每個變更都會先行寫入到Redo log中。其用途和意義在於存儲數據庫的所有修改歷史,用於數據庫故障恢復(Recovery)、增量備份(Incremental Backup)、PITR(Point In Time Recovery)和復制(Replication)。
-
WAL segment file:為了便於管理,PG把事務日志文件划分為N個segment,每個segment稱為WAL segment file,每個WAL segment file大小默認為16MB。
-
XLOG Record:這是一個邏輯概念,可以理解為PG中的每一個變更都對應一條XLOG Record,這些XLOG Record存儲在WAL segment file中。PG讀取這些XLOG Record進行故障恢復/基於時間點恢復(point-in-time recovery-PITR)等操作。
-
WAL buffer:WAL緩沖區,不管是WAL segment file的header還是XLOG Record都會先行寫入到WAL緩沖區中,在"合適的時候"再通過WAL writer寫入到WAL segment file中。
-
LSN:LSN即日志序列號Log Sequence Number。表示XLOG record記錄寫入到事務日志中位置。LSN的值為無符號64位整型(uint64)。在事務日志中,LSN單調遞增且唯一。
-
checkpointer:checkpointer是PG中的一個后台進程,該進程周期性地執行checkpoint。當執行checkpoint時,該進程會把包含checkpoint信息的XLOG Record寫入到當前的WAL segment file中,該XLOG Record記錄包含了最新Redo pint的位置。
-
checkpoint:檢查點checkpoint由checkpointer進程執行,主要的處理流程如下:
- 獲取Redo point,構造包含此Redo point檢查點(詳細請參考Checkpoint結構體)信息的XLOG Record並寫入到WAL segment file中;
- 刷新Dirty Page到磁盤上;
- 更新Redo point等信息到pg_control文件中。
-
REDO point:REDO point是PG啟動恢復的起始點,是最后一次checkpoint啟動時事務日志文件的末尾亦即寫入Checkpoint XLOG Record時的位置(這里的位置可以理解為事務日志文件中偏移量)。
1.5.2.2 存儲路徑
wal默認保存在pg_wal文件夾(低版本保持在wal_log/pg_xlog)中,該目錄會產生多個wal日志,每個wal日志默認16M, 不需要的過期wal日志會被自動覆蓋掉。數據庫初始化($PGHOME/bin/initdb)時,通過參數--wal-segsize指定每個日志文件大小
ls -rt $PGDATA/pg_wal
total 131076
-rw------- 1 postgres postgres 16777216 Oct 31 2019 000000010000000000000001
-rw------- 1 postgres postgres 16777216 Oct 31 2019 000000010000000000000002
-rw------- 1 postgres postgres 337 Oct 31 2019 000000010000000000000002.00000028.backup
-rw------- 1 postgres postgres 16777216 Oct 31 2019 000000010000000000000003
-rw------- 1 postgres postgres 16777216 Nov 7 2019 000000010000000000000004
-rw------- 1 postgres postgres 16777216 Mar 8 11:48 000000010000000000000005
-rw------- 1 postgres postgres 16777216 May 12 10:37 000000010000000000000006
-rw------- 1 postgres postgres 16777216 Jun 4 10:17 000000010000000000000007
-rw------- 1 postgres postgres 16777216 May 30 09:41 000000010000000000000008
drwx------ 2 postgres postgres 281 May 12 10:37 archive_status
相關配置參數
wal_level
wal_level用於確定將多少信息寫入WAL。 每個級別都包含所有較低級別記錄的信息。 此參數只能在服務器啟動時設置。
- replica: (默認值) 它寫入足夠的數據以支持WAL歸檔和復制,包括在備用服務器上運行只讀查詢
- minimal:刪除除崩潰或立即關閉所需的信息之外的所有日志記錄
- logical: 添加了支持邏輯解碼所需的信息
fsync
該參數直接控制日志是否先寫入磁盤。默認值是ON(先寫入),表示更新數據寫入磁盤時系統必須等待WAL的寫入完成。可以配置該參數為OFF,表示更新數據寫入磁盤完全不用等待WAL的寫入完成。
關閉fsync的安全環境示例包括從備份文件初始加載新數據庫集群,使用數據庫集群處理一批數據,之后將丟棄並重新創建數據庫,或者使用只讀數據庫經常重新創建並且不用於故障轉移的克隆。單獨使用高質量硬件並不足以成為關閉fsync的理由。
為了在將fsync更改為on時可靠恢復,必須強制內核中的所有已修改緩沖區為持久存儲。這可以在群集關閉時或通過運行initdb --sync-only,運行同步,卸載文件系統或重新啟動服務器來啟用fsync時完成。
在許多情況下,關閉非關鍵事務的synchronous_commit可以提供關閉fsync的大部分潛在性能優勢,而不會伴隨數據損壞的風險。
fsync只能在postgresql.conf文件或服務器命令行中設置。如果關閉此參數,還請考慮關閉full_page_writes。
synchronous_commit
參數配置是否等待WAL完成后才返回給用戶事務的狀態信息。默認值是ON,表明必須等待WAL完成后才返回事務狀態信息;配置成OFF能夠更快地反饋回事務狀態。
wal_sync_method
WAL寫入磁盤的控制方式,默認值是fsync,可選用值包括open_datasync、fdatasync、fsync_writethrough、fsync、open_sync。open_datasync和open_sync分別表示在打開WAL文件時使用O_DSYNC和O_SYNC標志;fdatasync和fsync分別表示在每次提交時調用fdatasync和fsync函數進行數據寫入,兩個函數都是把操作系統的磁盤緩存寫回磁盤,但前者只寫入文件的數據部分,而后者還會同步更新文件的屬性;fsync_writethrough表示在每次提交並寫回磁盤會保證操作系統磁盤緩存和內存中的內容一致。
full_page_writes
啟用此參數后, PostgreSQL 服務器會在檢查點之后第一次修改該頁面時將每個磁盤頁面的全部內容寫入 WAL 。
這是必需的,因為在操作系統崩潰期間正在處理的頁面寫入可能僅部分完成,從而導致包含舊數據和新數據混合的磁盤上頁面。
通常存儲在 WAL 中的行級更改數據將不足以在崩潰后恢復期間完全恢復此類頁面。 存儲整頁圖像可確保頁面可以正確恢復,但代價是增加必須寫入 WAL 的數據量。 (因為 WAL 重放總是從檢查點開始,所以在檢查點之后的每個頁面的第一次更改期間執行此操作就足夠了。因此,降低整頁寫入成本的一種方法是增加檢查點間隔參數。)
關閉此參數可加快正常操作,但可能會在系統出現故障后導致無法恢復的數據損壞或靜默數據損壞。 風險類似於關閉 fsync ,雖然較小,但應僅根據為該參數推薦的相同情況關閉。
關閉此參數不會影響使用 WAL 歸檔進行時間點恢復( PITR )(請參閱第 25.3 節)。
此參數只能在 postgresql.conf 文件或服務器命令行中設置。 默認打開。
wal_log_hints
當此參數打開時,PostgreSQL服務器會在檢查點之后第一次修改該頁面時將每個磁盤頁面的全部內容寫入WAL,即使對於所謂的提示位的非關鍵修改也是如此。
如果啟用了數據校驗和,則提示位更新始終是WAL記錄的,並忽略此設置。 您可以使用此設置來測試如果您的數據庫啟用了數據校驗和,將會發生多少額外的WAL日志記錄。
此參數只能在服務器啟動時設置。 默認值為off。
wal_compression
When this parameter is on
, the PostgreSQL server compresses a full page image written to WAL when is on or during a base backup. A compressed page image will be decompressed during WAL replay. The default value is off
. Only superusers can change this setting.
Turning this parameter on can reduce the WAL volume without increasing the risk of unrecoverable data corruption, but at the cost of some extra CPU spent on the compression during WAL logging and on the decompression during WAL replay.
wal_writer_delay
WalWriter 進程的寫間隔時間,默認值是 200 毫秒,如果時間過長可能造成 WAL 緩沖區的內存不足;時間過短將會引起 WAL 的不斷寫入,增加磁盤 I/O 負擔。
請注意,在許多系統上,睡眠延遲的有效解決方案是 10 毫秒;將 wal-writer-delay 設置為不是 10 的倍數的值可能與將其設置為 10 的下一個更高倍數的結果相同。此參數只能在 postgresql.conf 文件或服務器命令行中設置。
wal_writer_flush_after
Specifies how often the WAL writer flushes WAL. If the last flush happened less than wal_writer_delay
milliseconds ago and less than wal_writer_flush_after
bytes of WAL have been produced since, then WAL is only written to the operating system, not flushed to disk. If wal_writer_flush_after
is set to 0
then WAL data is flushed immediately. The default is 1MB
. This parameter can only be set in the postgresql.conf
file or on the server command line.
commit_delay
表示一個已經提交的數據在 WAL 緩沖區中存放的時間,默認值是 0 毫秒,表示不用延遲;設置為非 0 值時事務執行 commit 后不會立即寫入 WAL 中,而仍存放在 WAL 緩沖區中,等待 WalWriter 進程周期性地寫入磁盤。
commit_siblings
表示當一個事務發出提交請求時,如果數據庫中正在執行的事務數量大於 commit_siblings 值,則該事務將等待一段時間( commit_delay 的值);否則該事務則直接寫入 WAL 。系統默認值是 5 ,該參數還決定了 commit_delay 的有效性。
1.5.2.3 WAL文件組成
PG使用無符號64bit整型(uint64)作為事務日志文件的尋址空間,理論上,PG的事務日志空間最大為2^64Bytes(即16EB)。假設每天可以產生16TB的日志文件,那么要達到事務日志文件大小的上限需要的時間是1024*1024/365天≈2800年。
對於16EB的文件,OS是無法高效管理的,為此,PG把事務日志文件划分為N個大小為16M(默認值)的WAL segment file。
WAL segment file命名日志格式
- TimeLineID:取值范圍是0x00000000 -> 0xFFFFFFFF
- 邏輯文件ID:取值范圍是0x00000000 -> 0xFFFFFFFF
- 物理文件ID:取值范圍是0x00000000 -> 0x000000FF
邏輯文件ID、物理文件ID和文件大小這三部分的組合,實現了64bit的尋址空間:
-
邏輯文件ID是32bit的uint32(unsigned int 32bit)
-
物理文件ID是8bit的unit8
-
16M的文件大小是24bit的unit24
三者共同組成unit64(32+8+24),達到最大64bit的文件尋址空間。
LSN(事務日志偏移位置)
事務日志文件的LSN表示XLOG Record記錄寫入到事務日志文件中的位置。LSN可以理解為XLOG Record在事務日志文件中的偏移(Offset)。
LSN由3部分組成,分別是邏輯文件ID,物理文件ID和文件內偏移。如LSN:1/4288E228,其中1為邏輯文件ID,42為物理文件ID,88E228為WAL segment file文件內偏移(注:16MB大小的WAL Segment file需要3 Bytes的尋址空間)。
按此規則,給定一個LSN,很容易根據LSN號推算得到其對應的日志文件(假定時間線TimeLineID為1)。
如:LSN 1/4288E228對應的WAL segment file文件為00000001 00000001 00000042,該文件名稱的前8位為時間線ID(00000001),中間8位(00000001)為邏輯文件ID,最后8位(00000042)為物理文件ID。
# 命名格式(文件名稱由24個16進制[0~F]的字符組成,每8個字符一組,每組的意義如下:
00000001 00000000 00000001
-------- -------- --------
時間線 邏輯id 物理id
# 查看當前日志LSN
SELECT pg_walfile_name(pg_current_wal_lsn()), pg_current_wal_lsn();
# 查看當前日志LSN
postgres->postgres@[local]:5432=# SELECT pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/7DD2E70
(1 row)
# LSN對應的文件名稱
postgres->postgres@[local]:5432=# SELECT pg_walfile_name(pg_current_wal_lsn());
pg_walfile_name
--------------------------
000000010000000000000007
(1 row)
# WAL段文件
postgres->postgres@[local]:5432=# SELECT name, size, to_char(modification,'yyyy-MM-dd HH24-MI-SS') as modification FROM pg_ls_waldir() ORDER BY modification;
name | size | modification
------------------------------------------+----------+---------------------
000000010000000000000001 | 16777216 | 2019-10-31 10-10-52
000000010000000000000002 | 16777216 | 2019-10-31 10-10-53
000000010000000000000002.00000028.backup | 337 | 2019-10-31 10-10-53
000000010000000000000003 | 16777216 | 2019-10-31 10-31-17
000000010000000000000004 | 16777216 | 2019-11-07 13-33-21
000000010000000000000005 | 16777216 | 2020-03-08 11-48-24
000000010000000000000006 | 16777216 | 2020-05-12 10-37-35
000000010000000000000008 | 16777216 | 2020-05-30 09-41-19
000000010000000000000007 | 16777216 | 2020-06-04 10-17-25
(9 rows)
postgres->postgres@[local]:5432=#
# pg_waldump查看wal文件內容
${PGHOME}/bin/pg_waldump 000000010000000000000007|more
# 切換Wal日志
SELECT pg_switch_wal();
1.5.2.4 WAL Segment file內部結構
WAL segment file內部划分為N個page(Block),每個page大小為8192 Bytes即8K,每個WAL segment file第1個page的header在PG源碼中相應的數據結構是XLogLongPageHeaderData,后續其他page的header對應的數據結構是XLogPageHeaderData。在一個page中,page header之后是N個XLOG Record。
XLOG Record
XLOG Record由兩部分組成
- 第一部分是XLOG Record的頭部信息,大小固定(24 Bytes),對應的結構體是XLogRecord;
- 第二部分是XLOG Record data。
XLOG Record按存儲的數據內容來划分,大體可以分為三類:
- Record for backup block:存儲full-write-page的block,這種類型Record是為了解決page部分寫的問題。在checkpoint完成后第一次修改數據page,在記錄此變更寫入事務日志文件時整頁寫入(需設置相應的初始化參數,默認為打開);
- Record for tuple data block:存儲page中的tuple變更,使用這種類型的Record記錄;
- Record for Checkpoint:在checkpoint發生時,在事務日志文件中記錄checkpoint信息(其中包括Redo point)。
其中XLOG Record data是存儲實際數據的地方,由以下幾部分組成:
- 0..N個XLogRecordBlockHeader,每一個XLogRecordBlockHeader對應一個block data;
- XLogRecordDataHeader[Short|Long],如數據大小<256 Bytes,則使用Short格式,否則使用Long格式;
- block data:full-write-page data和tuple data。對於full-write-page data,如啟用了壓縮,則數據壓縮存儲,壓縮后該page相關的元數據存儲在XLogRecordBlockCompressHeader中;
- main data: /checkpoint等日志數據
以INSERT數據為例,在插入數據時的XLOG Record data內部結構如下圖所示:
1.5.2.5 WAL segment file內容剖析
(linux) hexdump工具查看WAL文件中的內容
hexdump -C $PGDATA/pg_wal/000000010000000000000008
pg_waldump工具
PG已提供了dump事務日志的工具:pg_waldump
用於查看事務日志文件中的內容
${PGHOME}/bin/pg_waldump --help
pg_waldump decodes and displays PostgreSQL write-ahead logs for debugging.
Usage:
pg_waldump [OPTION]... [STARTSEG [ENDSEG]]
Options:
-b, --bkp-details output detailed information about backup blocks
-e, --end=RECPTR stop reading at WAL location RECPTR
-f, --follow keep retrying after reaching end of WAL
-n, --limit=N number of records to display
-p, --path=PATH directory in which to find log segment files or a
directory with a ./pg_wal that contains such files
(default: current directory, ./pg_wal, $PGDATA/pg_wal)
-r, --rmgr=RMGR only show records generated by resource manager RMGR;
use --rmgr=list to list valid resource manager names
-s, --start=RECPTR start reading at WAL location RECPTR
-t, --timeline=TLI timeline from which to read log records
(default: 1 or the value used in STARTSEG)
-V, --version output version information, then exit
-x, --xid=XID only show records with transaction ID XID
-z, --stats[=record] show statistics instead of records
(optionally, show per-record statistics)
-?, --help show this help, then exit
查看文件pg_wal/000000010000000000000007
內容,對應起始LSN:0/07000000
# 查看最早的4個XLOG Record
${PGHOME}/bin/pg_waldump -p $PGDATA/pg_wal -s 0/07000000 -n 4
WAL日志解析工具(wal2json)
wal2json作為紅帽開源項目Debezium的組成部分,用於提供基於PG庫級別的DML日志挖掘工作。
Debezium 目地在於提供一個分布式的平台,將數據庫日志中的事件記錄轉化為事件流,使得外部應用能夠對數據庫中的行級操作做出快速響應。Debezium 可以建立在Apache kafka的上層,為kafka connect提供可兼容的連接器用於監控和管理特定的數據庫。
# 1. 軟件地址
Debezium針對幾類主流數據庫的連接器:https://debezium.io/docs/connectors/
Wal2json安裝頁面:https://debezium.io/docs/install/postgres-plugins/
# 2.部署配置
$ git clone https://github.com/eulerto/wal2json -b master --single-branch \
&& cd wal2json \
&& git checkout d2b7fef021c46e0d429f2c1768de361069e58696 \
&& make && make install \
&& cd .. \
&& rm -rf wal2json
# 3. 參數配置 postgresql.conf
############ REPLICATION ##############
# MODULES
shared_preload_libraries = 'wal2json'
# REPLICATION
wal_level = logical
max_wal_senders = 4
max_replication_slots = 4
# 4. 創建具有Replication和Login授權的用戶
CREATE ROLE name REPLICATION LOGIN;
# 5. pg_hba.conf 遠程或本地訪問數據庫
############ REPLICATION ##############
local replication postgres trust
host replication postgres 127.0.0.1/32 trust
host replication postgres ::1/128 trust
# 6. 測試環境
# 6.1 連接窗口1創建數據
CREATE DATABASE test;
CREATE TABLE test_table (
id char(10) NOT NULL,
code char(10),
PRIMARY KEY (id)
);
# 新建一個連接窗口2
# 6.2 創建復制槽 test_slot
pg_recvlogical -d test --slot test_slot --create-slot -P wal2json
# 6.3 接收變更數據輸出
pg_recvlogical -d test --slot test_slot --start -o pretty-print=1 -f -
# 6.4 回到連接窗口1 插入數據
test=# INSERT INTO test_table (id, code) VALUES('id1', 'code1');
INSERT 0 1
test=# update test_table set code='code2' where id='id1';
UPDATE 1
test=# delete from test_table where id='id1';
DELETE 1
# 6.5 連接窗口2查看輸出
切換wal日志
select pg_switch_wal();
當前日志
select pg_walfile_NAME_OFFSET(pg_current_wal_lsn());
select pg_current_wal_lsn();
-- 10.0以后版本
select pg_current_wal_lsn(),
pg_walfile_name(pg_current_wal_lsn()),
pg_walfile_name_offset(pg_current_wal_lsn());
-- 10.0以前版本
select pg_current_xlog_location(),
pg_xlogfile_name(pg_current_xlog_location()),
pg_xlogfile_name_offset(pg_current_xlog_location());
- pg_current_wal_lsn():獲得當前wal日志寫入位置。
- pg_walfile_name(lsn pg_lsn): 轉換wal日志位置為文件名。
- pg_walfile_name_offset(lsn pg_lsn): 返回轉換后的wal日志文件名和偏移量。
LSN與WAL 名稱
LSN: 0/16E8370
-
LSN 由三部分組成: 0 1 6E8370
1.5.3 事務提交日志(pg_xact)
pg_xact是事務提交日志,記錄了事務的元數據。默認開啟。內容一般不能直接讀。默認存儲在目錄$PGDATA/pg_xact/
1.5.4 歸檔日志
將wal日志文件歸檔存放到指定的一個目錄。
參數文件
-
postgresql.conf:默認在
$PGDATA
下。在9.6后,支持通過alter system
命令修改參數配置,而alter命令修改的參數保存在$PGDATA/postgresql.auto.conf
文件中。 -
pg_hba.conf:客戶端登錄認證配置文件
-
pg_ident.conf:用戶映射配置文件,用來配置哪些操作系統用戶可以映射為數據庫用戶。結合pg_hba.conf中,method為ident可以用特定的操作系統用戶和指定的數據庫用戶登錄數據庫。