PostgreSQL如何刪除不使用的xlog文件
一、問題

經常會在復制的時候遇到這樣的問題,需要復制的xlog文件找不到了。那么xlog文件什么時候刪除?又會刪除多少保留多少個xlog文件?都有哪些xlog文件需要保留?本文將從原理上對這些問題進行解讀。
二、原理
每次checkpoint后都會根據需要刪除或者回收不再需要的xlog文件。
1、首先估算兩次checkpoint之間產生的xlog量,根據這個量會計算出未來最大的日志文件號從而回收不再需要的文件將其重命名為未來即將使用的日志文件號:
1.1 UpdateCheckPointDistanceEstimate估算checkpoint之前產生的日志量:
if (CheckPointDistanceEstimate < nbytes)//上次估算量比這次估算的小,則更新為這次的估算量
CheckPointDistanceEstimate = nbytes;
else//否則,適當增加
CheckPointDistanceEstimate =(0.90?CheckPointDistanceEstimate + 0.10?(double) nbytes);
2、計算上一次checkpoint時,所在的文件段號_logSegNo:
XLByteToSeg(PriorRedoPtr, _logSegNo);
3、計算需要保留的文件段號:從該段號_logSegNo開始的文件都不能被刪除,之前的需要刪除或回收:根據備機請求以及wal_keep_segments計算KeepLogSeg(recptr, &_logSegNo);
4、遍歷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則直接刪除該文件
--RemoveXlogFile
三、代碼流程
1、checkpoint頂層函數CreateCheckPoint:
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: ????????1、CheckPointDistanceEstimate
2、兩個宏定義
#define?UsableBytesInPage?(XLOG_BLCKSZ?-?SizeOfXLogShortPHD)//注意:不是文件第一頁#define?UsableBytesInSegment?((XLOG_SEG_SIZE?/?XLOG_BLCKSZ)?*?UsableBytesInPage?-?(SizeOfXLogLongPHD?-?SizeOfXLogShortPHD))
3、函數XLogBytePosToRecPtr
static?XLogRecPtrXLogBytePosToRecPtr(uint64?bytepos){????//bytepos:不包括xlog頁的頁頭等額外字節占用的大小 ????fullsegs?=?bytepos?/?UsableBytesInSegment; ????bytesleft?=?bytepos?%?UsableBytesInSegment;????/* ????1、如果bytesleft?=?XLOG_BLCKSZ-32,則表示定位不是第一頁 ????*/ ????if?(bytesleft?
4、函數KeepLogSeg
static?voidKeepLogSeg(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得到的限制: ????????1、比如wal_keep_segments值是10,若當前insert的位置的文件號segno為5,那么向前推進到1 ????????2、否則向前推進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?
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)));
/* ?構建一個log文件名,用于判斷,該文件之前的xlog可以刪除。用不到時間線,所以可以使用0 ?*/XLogFileName(lastoff,?0,?segno);while?((xlde?=?ReadDir(xldir,?XLOGDIR))?!=?NULL){????/*?忽略非xlog文件?*/ ????if?(!IsXLogFileName(xlde->d_name)?&& ????????!IsPartialXLogFileName(xlde->d_name))????????continue;????/* ????1、跳過時間線進行比較 ?????*/ ????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); ????????} ????} }
}
6、函數RemoveXlogFile
RemoveXlogFile(const char *segname, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr)
{
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); }
}
7、函數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;
}
-------------------------------
本文轉自yzs的專欄博客51CTO博客
數據庫 postgresql
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。