閱讀147 返回首頁    go 阿裏雲 go 技術社區[雲棲]


PgSQL · 特性分析 · checkpoint機製淺析

背景

上期月報PgSQL · 特性分析 · Write-Ahead Logging機製淺析中簡單介紹了PostgreSQL中WAL機製,其中講到如果是創建checkpoint會觸發刷新xlog日誌頁到磁盤,本文主要分析下PostgreSQL中checkpoint機製。

checkpoint又名檢查點,一般checkpoint會將某個時間點之前的髒數據全部刷新到磁盤,以實現數據的一致性與完整性。目前各個流行的關係型數據庫都具備checkpoint功能,其主要目的是為了縮短崩潰恢複時間,以Oracle為例,在進行數據恢複時,會以最近的checkpoint為參考點執行事務前滾。而在WAL機製的淺析中,也提過PostgreSQL在崩潰恢複時會以最近的checkpoint為基礎,不斷應用這之後的WAL日誌。

檢查點發生時機

在xlog.h文件中,有如下代碼對checkpoint進行了相應的分類:

/*
 * OR-able request flag bits for checkpoints.  The "cause" bits are used only
 * for logging purposes.  Note: the flags must be defined so that it's
 * sensible to OR together request flags arising from different requestors.
 */

/* These directly affect the behavior of CreateCheckPoint and subsidiaries */
#define CHECKPOINT_IS_SHUTDOWN	0x0001	/* Checkpoint is for shutdown */
#define CHECKPOINT_END_OF_RECOVERY	0x0002		/* Like shutdown checkpoint,
												 * but issued at end of WAL
												 * recovery */
#define CHECKPOINT_IMMEDIATE	0x0004	/* Do it without delays */
#define CHECKPOINT_FORCE		0x0008	/* Force even if no activity */
/* These are important to RequestCheckpoint */
#define CHECKPOINT_WAIT			0x0010	/* Wait for completion */
/* These indicate the cause of a checkpoint request */
#define CHECKPOINT_CAUSE_XLOG	0x0020	/* XLOG consumption */
#define CHECKPOINT_CAUSE_TIME	0x0040	/* Elapsed time */
#define CHECKPOINT_FLUSH_ALL	0x0080	/* Flush all pages, including those
										 * belonging to unlogged tables */

也就是說,以下幾種情況會觸發數據庫操作係統做檢查點操作:

  1. 超級用戶(其他用戶不可)執行CHECKPOINT命令
  2. 數據庫shutdown
  3. 數據庫recovery完成
  4. XLOG日誌量達到了觸發checkpoint閾值
  5. 周期性地進行checkpoint
  6. 需要刷新所有髒頁

為了能夠周期性的創建檢查點,減少崩潰恢複時間,同時合並I/O,PostgreSQL提供了輔助進程checkpointer。它會對不斷檢測周期時間以及上麵的XLOG日誌量閾值是否達到,而周期時間以及XLOG日誌量閾值可以通過參數來設置大小,接下來介紹下與checkpoints相關的參數。

與檢查點相關參數

  • checkpoint_segments
    • WAL log的最大數量,係統默認值是3。超過該數量的WAL日誌,會自動觸發checkpoint。
  • checkpoint_timeout
    • 係統自動執行checkpoint之間的最大時間間隔。係統默認值是5分鍾。
  • checkpoint_completion_target
    • 該參數表示checkpoint的完成時間占兩次checkpoint時間間隔的比例,係統默認值是0.5,也就是說每個checkpoint需要在checkpoints間隔時間的50%內完成。
  • checkpoint_warning
    • 係統默認值是30秒,如果checkpoints的實際發生間隔小於該參數,將會在server log中寫入寫入一條相關信息。可以通過設置為0禁用。

創建檢查點具體過程

CreateCheckPoint具體過程

當PostgreSQL觸發checkpoint發生的條件後,會調用CreateCheckPoint函數創建具體的檢查點,具體過程如下:

  1. 遍曆所有的數據buffer,將髒頁塊狀態從BM_DIRTY改為BM_CHECKPOINT_NEEDED,表示這些髒頁將要被checkpoint刷新到磁盤
  2. 調用CheckPointGuts函數將共享內存中的髒頁刷出到磁盤
  3. 生成新的Checkpoint 記錄寫入到XLOG中
  4. 更新控製文件、共享內存裏XlogCtl的檢查點相關成員、檢查點的統計信息結構

PostgreSQL 控製文件pg_control裏存儲的數據是一個ControlFileData結構,具體如下:

typedefstruct ControlFileData
{
    uint64    system_identifier;
    uint32    pg_control_version;     /*PG_CONTROL_VERSION */
    uint32    catalog_version_no;     /* seecatversion.h */
    DBState      state;       /* see enum above */
    pg_time_t time;        /* time stamp of last pg_control update */

        XLogRecPtr	checkPoint;		/* 最近一次創建checkpoint的LSN*/
        XLogRecPtr	prevCheckPoint; /* 最近一次之前創建checkpoint的LSN */
        /*由於一個檢查點的時間比較長,所以有可能係統在所有頁麵寫完之前崩潰,這樣磁盤上的檢查點可能是不完全的,因此將最後一個完全檢查點位置寫在prevCheckPoint上*/

	CheckPoint	checkPointCopy; /* 最近一次checkpoint對應的CheckPoint對象 */

	XLogRecPtr	minRecoveryPoint;
	TimeLineID	minRecoveryPointTLI;
	XLogRecPtr	backupStartPoint;
	XLogRecPtr	backupEndPoint;
	bool		backupEndRequired;
   ......

其中,minRecoveryPoint和minRecoveryPointTLI確定數據庫啟動前,如果做歸檔恢複,我們必須恢複到的最小檢查點。其中minRecoveryPoint指向該檢查點對應的LSN位置,minRecoveryPointTLI指向該檢查點對應的時間線。其具體的用法,我們將在之後的PostgreSQL崩潰恢複中分析,這裏我們主要分析下PostgreSQL中的時間線概念。

PostgreSQL中WAL日誌段名稱,由時間線ID、日誌ID、段ID的八位16進製數依次構成。例如:

00000001 00000001 0000008F
時間線TimeLineID 邏輯日誌ID 段ID

其中時間線是作為日誌段名稱的一部分,用來標識數據庫歸檔恢複後產生的一係列新的WAL記錄。在每次歸檔恢複完成後,都會產生一個新的時間線和新的WAL日誌段。時間線可以理解為平行時空中的各個平行宇宙,我們完全可以恢複到某個時間點,重開一條時間線,繼續進行數據操作,這樣就可以實現完全的PTIR。

在PostgreSQL中,一個新的時間線產生,係統伴隨它會建立一個以“新TimeLineID+.history”命名的“時間線曆史”文件(timeline history),它是一個類似於txt的文件,其中包含所有在當前時間線以前的時間線,同時記錄了每個時間線開始時的第一個WAL段,這樣數據庫恢複時,通過讀取時間線曆史文件文件,根據目標時間點可以快速找到正確的日誌段文件。如果上一次恢複是恢複到具體某時刻,在時間線曆史文件中還會記錄該時間線對應的具體時刻。

在PITR恢複時,無需掃描所有WAL日誌文件,而是通過時間線直接定位某個WAL段,再從該WAL段中找到符合該時間點的日誌記錄,這樣就大大提高了效率。同時數據庫恢複時,默認是沿著基備份開始時的時間點進行,即利用從基備份完成後產生的第一個日誌段文件做恢複,如果想恢複到指定時間點(時間線),需要在recovery.conf配置文件中設置目標時間線(target timeline ID),但是target timeline ID不能指定為基備份以前的時間線。

CheckPointGuts函數

CheckPointGuts函數將共享內存裏的數據刷出並文件同步到磁盤,具體定義如下:

staticvoid
CheckPointGuts(XLogRecPtrcheckPointRedo,int flags)
{
   CheckPointCLOG();
   CheckPointSUBTRANS();
   CheckPointMultiXact();
   CheckPointPredicate();
   CheckPointRelationMap();
   CheckPointBuffers(flags);   /* performs all required fsyncs */
   /* We deliberately delay 2PC checkpointingas long as possible */
   CheckPointTwoPhase(checkPointRedo);
}

可以看出,CheckPointGuts根據不同的緩存類型,把clog、subtrans、multixact、predicate、relationmap、buffer(數據文件)和twophase相應緩存分別調用不同的方法,將緩存刷到磁盤中:

  • 提交事務日誌管理器的方法CheckPointClog
  • 子事務日誌管理器的方法CheckPointSUBTRANS
  • 多事務日誌管理器的方法CheckPointMultiXact
  • 支持序列化事務隔離級別的謂詞鎖模塊的方法CheckPointPredicate
  • 目錄/係統表到文件節點映射模塊的方法CheckPointRelationMap
  • 緩存管理器的方法CheckPointBuffers
  • 兩階段提交模塊的方法CheckPointTwoPhase

其中,前四個函數最後都調用了SLRU模塊的SimpleLruFlush(簡單最近最少使用)方法,把相應的共享內存數據寫到磁盤,並通過調用pg_fsync方法把相應文件刷到磁盤上對應文件。

後二個函數沒有使用SLRU算法,直接調用pg_fsync方法把相應文件刷到磁盤上對應文件。

而目錄/係統表到文件節點映射模塊的方法CheckPointRelationMap,會將共享內存裏係統表和對應物理文件映射的map文件刷到磁盤。

總結

至此,我們大體了解了checkpoint的用法和整個實現過程,但是還需要對一些特別的地方做出說明。

  • 每個檢查點後,第一次數據頁的變化會導致整個頁麵會被記錄在XLOG日誌中
  • 檢查點的開銷比較高,可以用checkpoint_warning自檢,相應調大checkpoint_segments
  • 檢查點的位置保存在文件 pg_control,pg_control文件被損壞可能會導致數據庫不可用

其中,如果pg_control文件損壞,在數據庫崩潰恢複時可能出現一些問題,這些問題我們將在分析PostgreSQL數據庫崩潰恢複時具體分析。

最後更新:2017-04-21 09:01:15

  上一篇:go MySQL · 特性分析 · common table expression
  下一篇:go MySQL · 引擎介紹 · Sphinx源碼剖析(二)