PgSQL · 追根究底 · WAL日志空間的意外增長


問題出現

我們在線上巡檢中發現,一個實例的pg_xlog目錄,增長到4G,很是疑惑。剛開始懷疑是日志歸檔過慢,日志堆積在pg_xlog目錄下面,未被清除導致。於是檢查歸檔目錄下的文件,內容如下。但發現新近完成寫入的日志文件都被歸檔成功了(即在pg_xlog/archive_status里面,有對應的xxx.done文件)。

ls -lrt pg_xlog
...
-rw------- 1 xxxx xxxx 16777216 Jun 14 18:39 0000000100000035000000DE
-rw------- 1 xxxx xxxx 16777216 Jun 14 18:39 0000000100000035000000DF
drwx------ 2 xxxx xxxx    73728 Jun 14 18:39 archive_status
-rw------- 1 xxxx xxxx 16777216 Jun 14 18:39 0000000100000035000000E0

ls -lrt pg_xlog/archive_status
...
-rw------- 1 xxxx xxxx 0 Jun 14 18:39 0000000100000035000000DE.done
-rw------- 1 xxxx xxxx 0 Jun 14 18:39 0000000100000035000000DF.done
-rw------- 1 xxxx xxxx 0 Jun 14 18:39 0000000100000035000000E0.done

仔細觀察,奇怪的是,pg_xlog里面還有一些日志文件,其文件名對應了還沒產生的日志號!如下所示,當前正在被寫入的日志號為100000035000000E0左右,卻出現了名為1000000360000000C的日志文件名,更蹊蹺的是,其修改時間還在很早以前,就是說不是新近創建或修改過的,如下面的文件修改或創建時間是在當前時間的一個小時之前:

ls -lrt pg_xlog
....
-rw------- 1 xxxx xxxx 16777216 Jun 14 17:37 00000001000000360000000C
-rw------- 1 xxxx xxxx 16777216 Jun 14 17:37 000000010000003600000014
....

這是怎么回事呢?難道是“幽靈日志”?下面我們要搞清楚兩個問題:1)為什么會出現”幽靈日志“?2)PG正常情況下日志空間大小是多少?

PG日志創建清理機制

要回答上述問題,需要先摸清PG的日志創建、保持和清理機制。與此直接相關的模塊有:日志寫入(WAL writer)進程和日志歸檔(archiver)進程。其實檢查點(checkpointer)進程和日志發送進程(WAL sender)也與此有關。

WAL writer負責異步把WAL日志刷入磁盤;與此同時,其他普通后台進程,也可能會同步的將WAL日志刷入磁盤,我們先從分析它們入手。從代碼里面不難看出,它們將日志寫入新的日志文件時,有如下函數調用:

XLogWrite -> XLogFileInit ->BasicOpenFile

BasicOpenFile負責打開一個新的日志文件,如果文件不存在,則新建文件。而其代碼注釋里面提到“Try to use existent file (checkpoint maker may have created it already)”,即打開的文件可能已經被checkpointer進程創建。

於是我們將目光轉向checkpointer。其主要函數CheckpointerMain的邏輯如下:

  1. 檢查是否有checkpoint request信號;
  2. 檢查是否checkpoint timeout時間已到;
  3. 調用CreateCheckPoint做檢查點操作;
  4. 調用WaitLatch等待checkpoint timeout或checkpoint request信號。

重點內容都在CreateCheckPoint函數中,其邏輯如下:

  1. 檢查上次檢查點后是否有WAL日志寫入,如果沒有直接返回;
  2. 調用CheckPointGuts將WAL日志fsync到磁盤;注意其中的CheckPointBuffers函數,會根據checkpoint_completion_target的值做一定的delay,使fsync操作的完成時間占兩個檢查點之間時間間隔的比例,約為checkpoint_completion_target;
  3. 在WAL中插入檢查點日志信息;
  4. 取系統前一次檢查點的日志位置指針,即此指針之前的日志文件,都可以刪除了;
  5. 由KeepLogSeg根據wal_keep_segments和replication slot的情況計算要額外保留的日志;
  6. 由RemoveOldXlogFiles做真正的日志刪除,而神奇的是RemoveOldXlogFiles並未實際刪除文件,而是將其回收,即將老文件rename成新文件,做了日志文件預分配;
  7. 完成檢查點返回。

可以看到,在這里出現日志刪除、預分配等邏輯。也就是說PG的日志文件可能是在做檢查點操作時預分配的!預分配的文件名使用了“未來”的目前還不存在的日志號,這就解釋了我們之前遇到的“幽靈日志”情況,也回答了我們的第一個問題。

當然,需要說明的是,日志的保留和刪除還和是否被archiver進程歸檔成功有關。

日志空間大小

繼續看第二個問題。前面提到的日志空間暴增讓我們如臨大敵,那么PG日志到底最多會占用多少空間?我們遇到的漲到3G情況正常嗎?

從日志清理邏輯(重點是KeepLogSegRemoveOldXlogFiles函數)的分析,我們得到下面的結論:

  1. 日志的刪除和預分配只在檢查點剛完成時進行;
  2. 刪除時,保證上一次檢查點之后到現在的日志不會被刪除;
  3. 保證從目前日志位置往前wal_keep_segments個日志文件不會刪除;
  4. 預分配的過程是,對所有不再需要的舊文件重命名為一個未來的日志號,直到預分配的文件數量達到XLOGfileslop,即2*checkpoint\_segments + 1。checkpoint_segments為一個可配置的參數,控制了兩個檢查點間產生的日志文件數量。

另外,為討論方便,下面我們先做如下假設:

  1. 有足夠多(即大於2*checkpoint_segments + 1)的不再需要的舊日志文件,可以用於預分配;
  2. 每次檢查點操作完成的時間,正好占兩個檢查點之間時間間隔的checkpoint_completion_target(線上目前我們設為0.9)。

設某次檢查點操作完成時的時間點為A,則此時做日志預分配的情形如下圖所示:

候選被回收的文件是在時間點C之前的、並且大於wal_keep_segments個文件間隔的文件;這些文件將重命名為預分配文件,文件號為從A對應的日志開始遞增,直到達到2*checkpoint_segments + 1個文件為止。

做檢查點操作過程中,是不斷產生新日志文件的,而且兩次檢查點之間的日志文件數為一個穩定的值,即checkpoint_segments。因此,在時間點B到A之間產生的日志數約為

checkpoint_segments * checkpoint_completion_target

待A時間點預分配完日志文件,並刪除其他不需要的日志后,新產生的日志將使用預分配空間,日志空間不會增大,日志空間大小達到一個穩定狀態。而此時日志的空間至少為:保留的日志空間 + 預分配空間 + 正在被寫入的那個文件,即為:

max(wal_keep_segments, checkpoint_segments + checkpoint_segments*checkpoint_completion_target) + 2 * checkpoint_segments + 1 + 1

這就是在日志大小達到穩定狀態時,所能達到的最大值。所謂“穩定狀態”是指,一旦達到這個狀態,優先使用預分配空間,一般不會增大;即使日志文件繼續增加,也會被刪除(如果archiver和wal sender都正常工作的話)。而日志大小也不會明顯減少,因為處於預分配狀態的日志數量、前一次檢查點到當前時間點的日志量都沒有大的變化。

回到我們的問題,PG的日志空間占用的正常值,可以用上面的公式計算出來。如果wal_keep_segments為80,checkpoint_segments為64,checkpoint_completion_target為0.9,那么根據公式計算結果為4.02G。即日志空間增加到4G也是正常的。並且可以通過減小checkpoint_segements的值,減少日志空間占用。

幾個問題

通過上面分析得出的公式,我們在處理日志時遇到的一些問題就迎刃而解了,例如:

Q: 增加wal_keep_segments會增大日志空間嗎?
A: 如果增加wal_keep_segments后,其值仍小於(checkpoint_segments + checkpoint_segments * checkpoint_completion_target),則增加wal_keep_segments並不會增大日志占用空間。

Q: checkpoint_segments與日志空間大小有什么關系?
A: 在wal_keep_segments較小時,checkpoint_segments對日志空間占用有至關重要的影響。日志空間大小基本上可以用4倍checkpoint_segments來估算出來。但當wal_keep_segments較大時,比如是checkpoint_segments的10倍,則checkpoint_segments對日志空間大小的影響相對就小很多了。

思考題

上面的分析中,我們做了兩點假設。一個是系統中有足夠多的舊日志可供回收,這種情況會出現嗎(提示:archiver進程或replication slot對日志刪除的影響)?另一個是,檢查點操作會及時完成,那么如果檢查點操作未及時完成,會出現什么情況?會導致日志空間占用比我們的公式更大嗎?

 

參考:

http://mysql.taobao.org/monthly/2015/06/08/

 

注:

1、XLOGfileslop:預分配wal文件數量,為:2*checkpoint_segments + 1

2、最后的兩個思考題,還未想出答案。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM