1. 程式人生 > >Postgresql之CheckPoint機制

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進位制數依次構成。例如:

00000001000000010000008F
時間線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資料庫崩潰恢復時具體分析。