XLOG段檔案跳號現象分析
一、原理
當執行promote 時,我們經常看到的結果是:生成一個新 XLOG 檔案,名稱為:時間線加 1 ,段檔名為之前的段檔案號。那麼做這個動作的時機是什麼時候呢?是否只有這一種現象,會不會有其他現象?先透露下,當執行 promote 動作前,最後一個 XLOG 日誌是 SWITCH 時,段檔案號會加 1 。下面我們對其流程做詳細分析,並透過 gdb 理解其原理。
做這個動作的函式是 exitArchiveRecovery , 呼叫時機為startup 程式退出的時刻,見堆疊:
Breakpoint 1, exitArchiveRecovery (endTLI=1, endOfLog=23053968) at xlog.c:5475 5475 InArchiveRecovery = false; (gdb) bt #0 exitArchiveRecovery (endTLI=1, endOfLog=23053968) at xlog.c:5475 #1 0x0815fc4b in StartupXLOG () at xlog.c:7460 #2 0x083dbc56 in StartupProcessMain () at startup.c:207 #3 0x08173cbf in AuxiliaryProcessMain (argc=2, argv=0xbfa8a2f4) at bootstrap.c:451 #4 0x083dab93 in StartChildProcess (type=StartupProcess) at postmaster.c:5386 #5 0x083d5d86 in PostmasterMain (argc=1, argv=0xa22e7e8) at postmaster.c:1369 #6 0x0831c76c in main (argc=1, argv=0xa22e7e8) at main.c:228
具體程式碼行為恢復完成之後:
StartupXLOG-> 讀取 checkpoint-> 恢復 ->exitArchiveRecovery : EndOfLog 為當前回放日誌最後的位置, EndOfLogTLI 為當前退出時回放日誌的時間線。當 data 目錄下有 standby.signal 檔案即該機器是備時 ArchiveRecoveryRequested 為 TRUE 。
exitArchiveRecovery 函式呼叫流程如下:首先透過 endOfLog 即回放最後的位置計算出段檔案日誌號: endLogSegNo= (endOfLog - 1) / (wal_segment_size) ; startLogSegNo= endOfLog / (wal_segment_size) 。如果 endLogSegNo 等於 startLogSegNo ,表示回放位置為檔案中間位置,在呼叫 XLogFileCopy 生成一個新檔案,並將上個 XLOG 檔案內容複製到新檔案中;段檔案號相同,時間線加 1 。如果 endLogSegNo 不等於 startLogSegNo ,即回放位置正好是檔案大小的末尾處,或者正好是 SWITCH 這個日誌,那麼呼叫 XLogFileInit 函式進行初始化檔案:
XlogFileCopy 函式呼叫:呼叫 XLogFilePath 函式獲取源 XLOG 檔名, OpenTransientFile 開啟該檔案,建立並開啟一個臨時 XLOG 檔案 pg_wal/xlogtemp.pid , pid 為 startup 程式的 ID 號。 sizeof(buffer) 為一頁大小 8192 位元組,從原始檔每次 read 一頁資料並將之 write 到 xlogtemp 檔案,最後一頁資料如果不夠 8192 位元組,則有多少讀取多少並寫入檔案。當檔案複製完成後,執行一次 sync 。最後呼叫 InstallXLogFileSegment 將檔案重新命名。
InstallXLogFileSegment 函式: XlogFileCopy 呼叫時, find_free 為 false ,直接將檔案重新命名為時間線加 1 的檔名; XLogFileInit 呼叫時為 TRUE ,將段檔案號加 1 後(注意這裡不是加 1 ,是因為正好是檔案末尾,求得的是下一個段檔案號,只是現象上看是加 1 ),重新命名為時間線加 1 的檔案,會先 stat 下這個檔案,該流程返回值是 2 即該檔案不存在,所以不會再將 segno 加 1 ,直接跳過虛框內的步驟,進入重新命名流程 durable_like_or_rename 。
XLogFileInit 函式的呼叫:首先獲取新檔案的檔名,即時間線加 1 ,段檔名為原檔名,本次 exitArchiveRecovery 函式的呼叫流程中, use_existent 為 TRUE 所以會檢視開啟該檔案。當然因為該檔案不存在所以開啟失敗。然後建立並開啟一個臨時檔案 xlogtemp.pid ,將該檔案全部清 0 ,最後 sync 。之後呼叫 InstallXLogFileSegment 函式重新命名。最後開啟新檔案以供之後使用。
二、GDB跟蹤-lsn位置在xlog檔案中間
1 、進入第一個斷點,即函式入口
Breakpoint 1, exitArchiveRecovery (endTLI=1, endOfLog=23053968) at xlog.c:5475 5475 InArchiveRecovery = false; (gdb) bt #0 exitArchiveRecovery (endTLI=1, endOfLog=23053968) at xlog.c:5475 #1 0x0815fc4b in StartupXLOG () at xlog.c:7460 #2 0x083dbc56 in StartupProcessMain () at startup.c:207 #3 0x08173cbf in AuxiliaryProcessMain (argc=2, argv=0xbfa8a2f4) at bootstrap.c:451 #4 0x083dab93 in StartChildProcess (type=StartupProcess) at postmaster.c:5386 #5 0x083d5d86 in PostmasterMain (argc=1, argv=0xa22e7e8) at postmaster.c:1369 #6 0x0831c76c in main (argc=1, argv=0xa22e7e8) at main.c:228
2 、接著計算出段檔案號,這兩個值相等,即執行 promote 時, lsn 最後位置在檔案中間。
5507 if (endLogSegNo == startLogSegNo) (gdb) 5517 XLogSegmentOffset(endOfLog, wal_segment_size)); (gdb) p endLogSegNo $1 = 1 (gdb) p startLogSegNo $2 = 1
3 、 lsn 在檔案中間,呼叫 XlogFileCopy , upto 為 lsn 在最後檔案的偏移
XLogFileCopy (destsegno=1, srcTLI=1, srcsegno=1, upto=6276752) at xlog.c:3405 3405 XLogFilePath(path, srcTLI, srcsegno, wal_segment_size); (gdb) p 23053968%(16*1024*1024) $3 = 6276752
4 、開啟原檔案 00000001 0000000000000001 ,以及臨時檔案 xlogtemp.29683
3406 srcfd = OpenTransientFile(path, O_RDONLY | PG_BINARY); (gdb) p path $4 = "pg_wal/00000001", '0' <repeats 15 times>, "1", '\000' <repeats 869 times>, "\b\221\250\277gmE\b\004\005ĥ\000\000\000\001\030\221\250\277\bnE\b\004\005ĥ\000\000\000\001X\221\250\277$\207E\b\004\005ĥ\000\000\000\001", '\000' <repeats 13 times>"\270, \004\246X\221\250\277\000\000\000\000\000\000\000 \000\270\004\000\000\000\000\000\000\000\000\000\n\000\000\000\000\270\004\246\270\221\250\277dd\025\b\000\005ĥ\352\320a\b\341\n\000\000~\016b\b\000\000\000" (gdb) n (gdb) p tmppath $5 = "pg_wal/xlogtemp.29683", '\000' <repeats 1002 times> (gdb) n 3420 fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
5 、迴圈進行複製,一次複製一頁 8192 位元組
3429 for (nbytes = 0; nbytes < wal_segment_size; nbytes += sizeof(buffer)) (gdb) p sizeof(buffer) $6 = 8192 (gdb) n 3433 nread = upto - nbytes; (gdb) 3439 if (nread < sizeof(buffer)) (gdb) p nread $7 = 6276752 (gdb) p 6276752/8192 $8 = 766
6 、 InstallXLogFileSegment 函式重新命名, path 為 000000020000000000000001
Breakpoint 2, InstallXLogFileSegment (segno=0xbfa86968, tmppath=0xbfa88974 "pg_wal/xlogtemp.29683", find_free=false, max_segno=0, use_lock=false) at xlog.c:3545 3545 XLogFilePath(path, ThisTimeLineID, *segno, wal_segment_size); (gdb) p path $10 = "pg_wal/00000002", '0' <repeats 15 times>, "1\000 \212\022\251\322\"[\000\000\000\000\000\277 `\b\204\211\250\277\347e\250\277\005\000\000\000\003\000\000\000 NҬ\003\000\000\000\002\000\000\000\005\000\000\000;\000\000\000\000\000\000\000\030f\250\277\206\032`\b\000\000\000\000\350h\250\277\350h\250\277\350h\250\277\240.{\262d\000\000\000\363s\000\000\000\000\000\000\240\020[\264\003\000\000\000\000\000\000\000\003\000\000\000\240\362:\266\003\000\000\000\220\251t\267\003\000\000\000\240\324\032\270\003\000\000\000\220\213T\271\003\000\000\000 \361\003\272\003\000\000\000\020\250=\273\003\000\000\000 \323\343\273\322\"[29683\000\000\000\000\n", '\000' <repeats 15 times>, "\001\000\000\000\373\333y\b\005\000\000\000\000\000\000\000\001\000\000\000\270\240\250\277\310h\250\277\225\017`\b\363s\000\000\000\000\000\000d", '\000' <repeats 27 times>"\350"... (gdb) n
7 、將臨時檔案重新命名為 000000020000000000000001
3579 if (durable_link_or_rename(tmppath, path, LOG) != 0) (gdb) p path $11 = "pg_wal/00000002", '0' <repeats 15 times>, "1\000 \212\022\251\322\"[\000\000\000\000\000\277 `\b\204\211\250\277\347e\250\277\005\000\000\000\003\000\000\000 NҬ\003\000\000\000\002\000\000\000\005\000\000\000;\000\000\000\000\000\000\000\030f\250\277\206\032`\b\000\000\000\000\350h\250\277\350h\250\277\350h\250\277\240.{\262d\000\000\000\363s\000\000\000\000\000\000\240\020[\264\003\000\000\000\000\000\000\000\003\000\000\000\240\362:\266\003\000\000\000\220\251t\267\003\000\000\000\240\324\032\270\003\000\000\000\220\213T\271\003\000\000\000 \361\003\272\003\000\000\000\020\250=\273\003\000\000\000 \323\343\273\322\"[29683\000\000\000\000\n", '\000' <repeats 15 times>, "\001\000\000\000\373\333y\b\005\000\000\000\000\000\000\000\001\000\000\000\270\240\250\277\310h\250\277\225\017`\b\363s\000\000\000\000\000\000d", '\000' <repeats 27 times>"\350"... (gdb) n
三、GDB跟蹤-lsn位置在xlog檔案尾或最後一個為SWITCH
1 、 lsn 位於檔案尾,呼叫函式 XLogFileInit
Breakpoint 3, exitArchiveRecovery (endTLI=3, endOfLog=50331648) at xlog.c:5475 5475 InArchiveRecovery = false; (gdb) n 5480 UpdateMinRecoveryPoint(InvalidXLogRecPtr, true); (gdb) 5486 if (readFile >= 0) (gdb) 5488 close(readFile); (gdb) 5489 readFile = -1; (gdb) 5498 XLByteToPrevSeg(endOfLog, endLogSegNo, wal_segment_size); (gdb) 5499 XLByteToSeg(endOfLog, startLogSegNo, wal_segment_size); (gdb) 5507 if (endLogSegNo == startLogSegNo) (gdb) 5525 bool use_existent = true; (gdb) 5528 fd = XLogFileInit(startLogSegNo, &use_existent, true); (gdb)
2 、 path 為 000000040000000000000003, 該檔案不存在
Breakpoint 2, XLogFileInit (logsegno=3, use_existent=0xbfbd366f, use_lock=true) at xlog.c:3216 3216 XLogFilePath(path, ThisTimeLineID, logsegno, wal_segment_size); (gdb) n 3221 if (*use_existent) (gdb) p path $1 = "pg_wal/00000004", '0' <repeats 15 times>, "3\000\000\000\000\000\346!`\bX6\275\277\060\000\000\000\a\000\000\000\030\333\177\t\364\257`\000\270\331\177\t\000\000\000\000\a\000\000\000\000\224M\000\000\000\000\000h2\275\277: `\b4\000\000\000\330\065\275\277\330\065\275\277\000\000\000\000h2\275\277L\274M\000\004\000\000\000\000\000\000\000?\000\000\000\a\000\000\000\b3\275\277\206\032`\b\000\000\000\000\330\065\275\277\330\065\275\277\330\065\275\277\373\210}\tX\025w\b\004\000\000\000\000\000\000\000\177\323a\bX\000\000\000\000\000\000\000\b", '\000' <repeats 19 times>, "\b\000\000\000\000\000\000\000\060", '\000' <repeats 11 times>, "recovering 00000\322\"[\000\000\270\004\246\277 `\b`6\275\277\360\253a\b\b", '\000' <repeats 11 times>... (gdb) n 3223 fd = BasicOpenFile(path, O_RDWR | PG_BINARY | get_sync_bit(sync_method)); (gdb) 3224 if (fd < 0) (gdb) 3226 if (errno != ENOENT) (gdb) p fd $2 = -1 (gdb) p errno $3 = 2
3 、建立並開啟臨時檔案,將 zbuffer.data 清 0 ,然後一頁一頁的將檔案清 0
3243 snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); (gdb) 3245 unlink(tmppath); (gdb) 3248 fd = BasicOpenFile(tmppath, O_RDWR | O_CREAT | O_EXCL | PG_BINARY); (gdb) 3249 if (fd < 0) (gdb) 3254 memset(zbuffer.data, 0, XLOG_BLCKSZ); (gdb) p fd $4 = 3 3269 for (nbytes = 0; nbytes < wal_segment_size; nbytes += XLOG_BLCKSZ) (gdb) 3271 errno = 0; (gdb) 3272 if (write(fd, zbuffer.data, XLOG_BLCKSZ) != XLOG_BLCKSZ)
4 、進入 InstallXLogFileSegment 函式, 000000040000000000000003 檔案 stat 失敗,呼叫 durable_link_or_rename 重新命名。
InstallXLogFileSegment (segno=0xbfbd0de0, tmppath=0xbfbd2de8 "pg_wal/xlogtemp.31765", find_free=true, max_segno=45, use_lock=true) at xlog.c:3550 3550 if (use_lock) (gdb) n 3551 LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); (gdb) 3553 if (!find_free) (gdb) 3561 while (stat(path, &stat_buf) == 0) (gdb) p path $5 = "pg_wal/00000004", '0' <repeats 15 times>, "3\000 \212\022\251\322\"[\000\000\000\000\000\277 `\b\370-\275\277W\n\275\277\005\000\000\000\003\000\000\000 NҬ\003\000\000\000\003\000\000\000\005\000\000\000;\000\000\000\000\000\000\000\210\n\275\277\206\032`\b\000\000\000\000X\r\275\277X\r\275\277X\r\275\277\240.{\262d\000\000\000\025|\000\000\000\000\000\000\240\020[\264\003\000\000\000\000\000\000\000\003\000\000\000\240\362:\266\003\000\000\000\220\251t\267\003\000\000\000\240\324\032\270\003\000\000\000\220\213T\271\003\000\000\000 \361\003\272\003\000\000\000\020\250=\273\003\000\000\000 \323\343\273\322\"[31765\000\000\000\000\n", '\000' <repeats 15 times>, "\001\000\000\000\373\333y\b\005\000\000\000\000\000\000\000\003\000\000\000(E\275\277\070\r\275\277\225\017`\b\025|\000\000\000\000\000\000"... (gdb) n 3579 if (durable_link_or_rename(tmppath, path, LOG) != 0) (gdb) p errno $6 = 2
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31493717/viewspace-2658878/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- SQL Server解惑——標識列的限制和跳號現象SQLServer
- PostgreSQL如何刪除不使用的xlog檔案SQL
- Spark Shuffle Write階段磁碟檔案分析Spark
- [20210224]控制檔案序列號滿的分析.txt
- 分析go中slice的奇怪現象Go
- FastHook——實現.dynsym段和.symtab段符號查詢ASTHook符號
- PostgreSQL的xlog/Wal歸檔及日誌清理SQL
- APK 檔案分析APK
- hex檔案分析
- 入口檔案開始,分析Vue原始碼實現Vue原始碼
- 戶外直播訊號差,老出現卡頓現象怎麼辦?
- MySQL update一個詭異現象的分析--個人未分析出MySql
- hdfs小檔案分析
- ELF檔案逆向分析
- 【DBA】Oracle 中DBA地址的檔案號使用的是相對檔案號(RELATIVE_FNO)Oracle
- dotNet符號檔案(pdb),符號包(snupkg)和SourceLink符號
- MogDB/openGauss誤刪未歸檔的xlog日誌如何解決
- 民營公司招聘流程不專業的現象與分析
- PostgreSQL xlog格式之checkpointSQL
- 分析Mach-O檔案Mac
- 致遠AnalyticsCloud分析雲任意檔案讀取漏洞復現Cloud
- 如何實現公眾號選單欄跳轉小程式
- 從SEQUENCE跳號說起
- 號外號外,GitHub 支援上傳視訊檔案啦!Github
- [20201126]檔案相對號與絕對號問題.txt
- 【Blazor】1-Blazor專案檔案分析Blazor
- coe檔案資料後的逗號
- maven怎樣跳過測試階段Maven
- WriteFile 奇怪的現象
- 短路:五維現象
- Apache Tomcat檔案包含漏洞分析ApacheTomcat
- 標頭檔案的作用分析
- REdis AOF檔案結構分析Redis
- vue原始碼分析系列之入口檔案分析Vue原始碼
- js實現使用檔案流下載csv檔案JS
- 分析專案管理執行階段有什麼技巧專案管理
- 二進位制檔案和符號檔案(PDB)如何校驗是否匹配符號
- PHP實現單檔案、多檔案上傳 封裝 物件導向實現檔案上傳PHP封裝物件