1、問題
常常會在複製的時候遇到這樣的問題,須要複製的xlog文件找不到了。那麼xlog文件何時刪除?又會刪除多少保留多少個xlog文件?都有哪些xlog文件須要保留?本文將從原理上對這些問題進行解讀。ide
2、原理
每次checkpoint後都會根據須要刪除或者回收再也不須要的xlog文件。
一、首先估算兩次checkpoint之間產生的xlog量,根據這個量會計算出將來最大的日誌文件號從而回收再也不須要的文件將其重命名爲將來即將使用的日誌文件號:
1.1 UpdateCheckPointDistanceEstimate估算checkpoint以前產生的日誌量:
if (CheckPointDistanceEstimate < nbytes)//上次估算量比此次估算的小,則更新爲此次的估算量
CheckPointDistanceEstimate = nbytes;
else//不然,適當增長
CheckPointDistanceEstimate =(0.90 CheckPointDistanceEstimate + 0.10 (double) nbytes);
二、計算上一次checkpoint時,所在的文件段號_logSegNo:
XLByteToSeg(PriorRedoPtr, _logSegNo);
三、計算須要保留的文件段號:從該段號_logSegNo開始的文件都不能被刪除,以前的須要刪除或回收:根據備機請求以及wal_keep_segments計算KeepLogSeg(recptr, &_logSegNo);函數
四、遍歷pg_wal目錄下的全部xlog文件,進行刪除:RemoveOldXlogFiles
4.1 跳過期間線進行比較,若是pg_wal目錄下的文件比_logSegNo小則被刪除或回收。那麼什麼條件下次被回收?
--RemoveXlogFile
4.2 計算回收文件重命名的將來最大文件段號recycleSegNo:
1)若是本次是第一次checkpoint,則將來最大段號recycleSegNo=當前段文件號+10
2)不然調用函數XLOGfileslop計算:
2.1 估算下一次checkpoint結束時日誌位置:
distance=(2.0+checkpoint_completion_target)CheckPointDistanceEstimate
distance=1.1
recycleSegNo = (XLogSegNo) ceil(((double) PriorRedoPtr + distance) / XLOG_SEG_SIZE);
2.2 minSegNo = PriorRedoPtr / XLOG_SEG_SIZE + ConvertToXSegs(min_wal_size_mb) - 1;
maxSegNo = PriorRedoPtr / XLOG_SEG_SIZE + ConvertToXSegs(max_wal_size_mb) - 1;
2.3 if (recycleSegNo < minSegNo)
recycleSegNo = minSegNo;
if (recycleSegNo > maxSegNo)
recycleSegNo = maxSegNo;
4.3 若是當前段文件號endlogSegNo < recycleSegNo,則調用InstallXLogFileSegment進行回收:
1)在endlogSegNo和recycleSegNo之間找一個free slot num,即沒有該段文件號的xlog文件
2)將須要刪除的文件名命名爲該free slot號的文件名
3)若是沒有找到free slot則直接刪除該文件
--RemoveXlogFileui
3、代碼流程
一、checkpoint頂層函數CreateCheckPoint:this
CreateCheckPoint: XLogCtlInsert *Insert = &XLogCtl->Insert;//標識插入的位置 curInsert = XLogBytePosToRecPtr(Insert->CurrBytePos);//添加頁頭大小後的位置 //(((curInsert) % XLOG_BLCKSZ == 0) ? 0 : (XLOG_BLCKSZ - (curInsert) % XLOG_BLCKSZ)) freespace = INSERT_FREESPACE(curInsert);//curInsert所在頁是否有空閒空間 if (freespace == 0){ if (curInsert % XLogSegSize == 0)//正好一個xlog段文件用完,即將使用下一個段文件,則跳過36字節 curInsert += SizeOfXLogLongPHD;//36字節 else//xlog段文件中正好一頁用完,即將使用下一頁,則跳過20字節 curInsert += SizeOfXLogShortPHD;//20字節 } checkPoint.redo = curInsert;//xlog文件上,實際的即將插入位置 RedoRecPtr = XLogCtl->Insert.RedoRecPtr = checkPoint.redo; ... //插入checkpoint記錄後末尾位置,即下一個xlog開始的位置 recptr = XLogInsert(RM_XLOG_ID,shutdown ? XLOG_CHECKPOINT_SHUTDOWN :XLOG_CHECKPOINT_ONLINE); ... PriorRedoPtr = ControlFile->checkPointCopy.redo;//上一次checkpoint的起始位置 ... if (PriorRedoPtr != InvalidXLogRecPtr){//上一次checkpoint開始到這一次checkpoint開始,產生的XLOG大小爲入參 /* CheckPointDistanceEstimate: 一、CheckPointDistanceEstimate<RedoRecPtr - PriorRedoPtr時:RedoRecPtr - PriorRedoPtr 二、CheckPointDistanceEstimate>=RedoRecPtr - PriorRedoPtr時:0.9*CheckPointDistanceEstimate+0.1*(RedoRecPtr - PriorRedoPtr) */ UpdateCheckPointDistanceEstimate(RedoRecPtr - PriorRedoPtr); //_logSegNo = (PriorRedoPtr) / XLogSegSize XLByteToSeg(PriorRedoPtr, _logSegNo); KeepLogSeg(recptr, &_logSegNo); _logSegNo--; RemoveOldXlogFiles(_logSegNo, PriorRedoPtr, recptr);
二、兩個宏定義spa
#define UsableBytesInPage (XLOG_BLCKSZ - SizeOfXLogShortPHD)//注意:不是文件第一頁 #define UsableBytesInSegment ((XLOG_SEG_SIZE / XLOG_BLCKSZ) * UsableBytesInPage - (SizeOfXLogLongPHD - SizeOfXLogShortPHD))
三、函數XLogBytePosToRecPtr日誌
static XLogRecPtr XLogBytePosToRecPtr(uint64 bytepos) { //bytepos:不包括xlog頁的頁頭等額外字節佔用的大小 fullsegs = bytepos / UsableBytesInSegment; bytesleft = bytepos % UsableBytesInSegment; /* 一、若是bytesleft < XLOG_BLCKSZ-32,則表示定位到第一頁上,則文件偏移值跳過第一頁頁頭大小 二、若是bytesleft >= XLOG_BLCKSZ-32,則表示定位不是第一頁 */ if (bytesleft < XLOG_BLCKSZ - SizeOfXLogLongPHD){ /* fits on first page of segment */ seg_offset = bytesleft + SizeOfXLogLongPHD; }else{ /* account for the first page on segment with long header */ seg_offset = XLOG_BLCKSZ;//先跳過第一頁 bytesleft -= XLOG_BLCKSZ - SizeOfXLogLongPHD;//去掉第一頁存放XLOG的大小 fullpages = bytesleft / UsableBytesInPage;//剩下的須要幾個頁 bytesleft = bytesleft % UsableBytesInPage;//剩下的偏移 // 文件偏移=第一頁大小+剩下的幾個頁大小+剩下的偏移+最後一頁的頁頭 seg_offset += fullpages * XLOG_BLCKSZ + bytesleft + SizeOfXLogShortPHD; } //result=(fullsegs) * XLOG_SEG_SIZE + seg_offset XLogSegNoOffsetToRecPtr(fullsegs, seg_offset, result); return result; }
四、函數KeepLogSegcode
static void KeepLogSeg(XLogRecPtr recptr, XLogSegNo *logSegNo) { //segno爲當前xlog即將插入位置在第幾個文件上 XLByteToSeg(recptr, segno); //XLogCtl->replicationSlotMinLSN;備機上請求預留的最小值? keep = XLogGetReplicationSlotMinimumLSN(); /* compute limit for wal_keep_segments first */ if (wal_keep_segments > 0){ /* 首先計算wal_keep_segments獲得的限制: 一、好比wal_keep_segments值是10,若當前insert的位置的文件號segno爲5,那麼向前推動到1 二、不然向前推動wal_keep_segments後的segno前的可刪除 */ if (segno <= wal_keep_segments) segno = 1; else segno = segno - wal_keep_segments; } /* then check whether slots limit removal further */ //計算slots限制,若是其算出的值小於wal_keep_segments計算出的值,則須要使用slotSegNo,slots還有用,不能刪除 if (max_replication_slots > 0 && keep != InvalidXLogRecPtr){ XLByteToSeg(keep, slotSegNo); if (slotSegNo <= 0) segno = 1; else if (slotSegNo < segno) segno = slotSegNo; } /* don't delete WAL segments newer than the calculated segment */ if (segno < *logSegNo) *logSegNo = segno; //note: //若是計算出的segno比上次checkpoint時的文件號logSegNo還有小,則取此次計算的segno //若是計算出的segno比上次checkpoint時的文件號logSegNo大,則取上次checkpoint時的文件號。 //由於恢復時若是是主機,讀取最新checkpoint記錄失敗後,會讀取上一次checkpoint記錄,若是上次checkpoint的文件被刪除,這裏就讀取不到記錄了 } 五、函數RemoveOldXlogFiles
static void
RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr)
{
//首先獲取xlog目錄
xldir = AllocateDir(XLOGDIR);
if (xldir == NULL)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open write-ahead log directory \"%s\": %m",
XLOGDIR)));ci
/* 構建一個log文件名,用於判斷,該文件以前的xlog能夠刪除。用不到時間線,因此可使用0 */ XLogFileName(lastoff, 0, segno); while ((xlde = ReadDir(xldir, XLOGDIR)) != NULL){ /* 忽略非xlog文件 */ if (!IsXLogFileName(xlde->d_name) && !IsPartialXLogFileName(xlde->d_name)) continue; /* 一、跳過期間線進行比較 */ if (strcmp(xlde->d_name + 8, lastoff + 8) <= 0){ if (XLogArchiveCheckDone(xlde->d_name)){//若是沒有開啓歸檔:老是TRUE;不然,歸檔完成後才爲TRUE /* Update the last removed location in shared memory first */ //XLogCtl->lastRemovedSegNo = segno; UpdateLastRemovedPtr(xlde->d_name); RemoveXlogFile(xlde->d_name, PriorRedoPtr, endptr); } } }
}rem
六、函數RemoveXlogFile
RemoveXlogFile(const char *segname, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr)
{get
XLByteToSeg(endptr, endlogSegNo); if (PriorRedoPtr == InvalidXLogRecPtr) recycleSegNo = endlogSegNo + 10; else recycleSegNo = XLOGfileslop(PriorRedoPtr); snprintf(path, MAXPGPATH, XLOGDIR "/%s", segname); if (endlogSegNo <= recycleSegNo && lstat(path, &statbuf) == 0 && S_ISREG(statbuf.st_mode) && InstallXLogFileSegment(&endlogSegNo, path, true, recycleSegNo, true)) { endlogSegNo++; }else{ rc = durable_unlink(path, LOG); }
}
七、函數InstallXLogFileSegment
static bool
InstallXLogFileSegment(XLogSegNo segno, char tmppath,
bool find_free, XLogSegNo max_segno,
bool use_lock)
{
XLogFilePath(path, ThisTimeLineID, segno);
/
We want to be sure that only one process does this at a time.
*/
if (use_lock)
LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
if (!find_free)
{
/ Force installation: get rid of any pre-existing segment file /
durable_unlink(path, DEBUG1);//刪除文件並持久化到磁盤
}else{
/ Find a free slot to put it in /
while (stat(path, &stat_buf) == 0){//獲取文件信息並保存到stat_buf中,成功返回0
//在segno和max_segno之間找一個空閒的段號,即目錄中沒有這個段號的xlog文件
if ((segno) >= max_segno){
/ Failed to find a free slot within specified range /
if (use_lock)
LWLockRelease(ControlFileLock);
return false;
}
(segno)++;
XLogFilePath(path, ThisTimeLineID, segno);}}if (durable_link_or_rename(tmppath, path, LOG) != 0){//將tmppath重命名爲path並持久化if (use_lock)LWLockRelease(ControlFileLock);/ durable_link_or_rename already emitted log message */return false;}if (use_lock)LWLockRelease(ControlFileLock);return true;}