一、原理

当执行promote时,我们经常看到的结果是:生成一个新XLOG文件,名称为:时间线加1,段文件名为之前的段文件号。那么做这个动作的时机是什么时候呢?是否只有这一种现象,会不会有其他现象?先透露下,当执行promote动作前,最后一个XLOG日志是SWITCH时,段文件号会加1。下面我们对其流程做详细分析,并通过gdb理解其原理。

做这个动作的函数是exitArchiveRecovery,调用时机为startup进程退出的时刻,见堆栈:

Breakpoint1,exitArchiveRecovery(endTLI=1,endOfLog=23053968)atxlog.c:54755475InArchiveRecovery=false;(gdb)bt#0exitArchiveRecovery(endTLI=1,endOfLog=23053968)atxlog.c:5475#10x0815fc4binStartupXLOG()atxlog.c:7460#20x083dbc56inStartupProcessMain()atstartup.c:207#30x08173cbfinAuxiliaryProcessMain(argc=2,argv=0xbfa8a2f4)atbootstrap.c:451#40x083dab93inStartChildProcess(type=StartupProcess)atpostmaster.c:5386#50x083d5d86inPostmasterMain(argc=1,argv=0xa22e7e8)atpostmaster.c:1369#60x0831c76cinmain(argc=1,argv=0xa22e7e8)atmain.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、进入第一个断点,即函数入口

Breakpoint1,exitArchiveRecovery(endTLI=1,endOfLog=23053968)atxlog.c:54755475InArchiveRecovery=false;(gdb)bt#0exitArchiveRecovery(endTLI=1,endOfLog=23053968)atxlog.c:5475#10x0815fc4binStartupXLOG()atxlog.c:7460#20x083dbc56inStartupProcessMain()atstartup.c:207#30x08173cbfinAuxiliaryProcessMain(argc=2,argv=0xbfa8a2f4)atbootstrap.c:451#40x083dab93inStartChildProcess(type=StartupProcess)atpostmaster.c:5386#50x083d5d86inPostmasterMain(argc=1,argv=0xa22e7e8)atpostmaster.c:1369#60x0831c76cinmain(argc=1,argv=0xa22e7e8)atmain.c:228

2、接着计算出段文件号,这两个值相等,即执行promote时,lsn最后位置在文件中间。

5507if(endLogSegNo==startLogSegNo)(gdb)5517XLogSegmentOffset(endOfLog,wal_segment_size));(gdb)pendLogSegNo$1=1(gdb)pstartLogSegNo$2=1

3、lsn在文件中间,调用XlogFileCopy,upto为lsn在最后文件的偏移

XLogFileCopy(destsegno=1,srcTLI=1,srcsegno=1,upto=6276752)atxlog.c:34053405XLogFilePath(path,srcTLI,srcsegno,wal_segment_size);(gdb)p23053968%(16*1024*1024)$3=6276752

4、打开原文件000000010000000000000001,以及临时文件xlogtemp.29683

3406srcfd=OpenTransientFile(path,O_RDONLY|PG_BINARY);(gdb)ppath$4="pg_wal/00000001",'0'<repeats15times>,"1",'\000'<repeats869times>,"\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'<repeats13times>"\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)ptmppath$5="pg_wal/xlogtemp.29683",'\000'<repeats1002times>(gdb)n3420fd=OpenTransientFile(tmppath,O_RDWR|O_CREAT|O_EXCL|PG_BINARY);

5、循环进行拷贝,一次拷贝一页8192字节

3429for(nbytes=0;nbytes<wal_segment_size;nbytes+=sizeof(buffer))(gdb)psizeof(buffer)$6=8192(gdb)n3433nread=upto-nbytes;(gdb)3439if(nread<sizeof(buffer))(gdb)pnread$7=6276752(gdb)p6276752/8192$8=766

6、InstallXLogFileSegment函数重命名,path为000000020000000000000001

Breakpoint2,InstallXLogFileSegment(segno=0xbfa86968,tmppath=0xbfa88974"pg_wal/xlogtemp.29683",find_free=false,max_segno=0,use_lock=false)atxlog.c:35453545XLogFilePath(path,ThisTimeLineID,*segno,wal_segment_size);(gdb)ppath$10="pg_wal/00000002",'0'<repeats15times>,"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\000NҬ\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'<repeats15times>,"\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'<repeats27times>"\350"...(gdb)n

7、将临时文件重命名为000000020000000000000001

3579if(durable_link_or_rename(tmppath,path,LOG)!=0)(gdb)ppath$11="pg_wal/00000002",'0'<repeats15times>,"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\000NҬ\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'<repeats15times>,"\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'<repeats27times>"\350"...(gdb)n三、GDB跟踪-lsn位置在xlog文件尾或最后一个为SWITCH

1、lsn位于文件尾,调用函数XLogFileInit

Breakpoint3,exitArchiveRecovery(endTLI=3,endOfLog=50331648)atxlog.c:54755475InArchiveRecovery=false;(gdb)n5480UpdateMinRecoveryPoint(InvalidXLogRecPtr,true);(gdb)5486if(readFile>=0)(gdb)5488close(readFile);(gdb)5489readFile=-1;(gdb)5498XLByteToPrevSeg(endOfLog,endLogSegNo,wal_segment_size);(gdb)5499XLByteToSeg(endOfLog,startLogSegNo,wal_segment_size);(gdb)5507if(endLogSegNo==startLogSegNo)(gdb)5525booluse_existent=true;(gdb)5528fd=XLogFileInit(startLogSegNo,&use_existent,true);(gdb)

2、path为000000040000000000000003,该文件不存在

Breakpoint2,XLogFileInit(logsegno=3,use_existent=0xbfbd366f,use_lock=true)atxlog.c:32163216XLogFilePath(path,ThisTimeLineID,logsegno,wal_segment_size);(gdb)n3221if(*use_existent)(gdb)ppath$1="pg_wal/00000004",'0'<repeats15times>,"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\000h3\275\277:`\b4\000\000\000\330\065\275\277\330\065\275\277\000\000\000\000h3\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'<repeats19times>,"\b\000\000\000\000\000\000\000\060",'\000'<repeats11times>,"recovering00000\322\"[\000\000\270\004\246\277`\b`6\275\277\360\253a\b\b",'\000'<repeats11times>...(gdb)n3223fd=BasicOpenFile(path,O_RDWR|PG_BINARY|get_sync_bit(sync_method));(gdb)3224if(fd<0)(gdb)3226if(errno!=ENOENT)(gdb)pfd$2=-1(gdb)perrno$3=2

3、创建并打开临时文件,将zbuffer.data清0,然后一页一页的将文件清0

3243snprintf(tmppath,MAXPGPATH,XLOGDIR"/xlogtemp.%d",(int)getpid());(gdb)3245unlink(tmppath);(gdb)3248fd=BasicOpenFile(tmppath,O_RDWR|O_CREAT|O_EXCL|PG_BINARY);(gdb)3249if(fd<0)(gdb)3254memset(zbuffer.data,0,XLOG_BLCKSZ);(gdb)pfd$4=33269for(nbytes=0;nbytes<wal_segment_size;nbytes+=XLOG_BLCKSZ)(gdb)3271errno=0;(gdb)3272if(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)atxlog.c:35503550if(use_lock)(gdb)n3551LWLockAcquire(ControlFileLock,LW_EXCLUSIVE);(gdb)3553if(!find_free)(gdb)3561while(stat(path,&stat_buf)==0)(gdb)ppath$5="pg_wal/00000004",'0'<repeats15times>,"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\000NҬ\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'<repeats15times>,"\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)n3579if(durable_link_or_rename(tmppath,path,LOG)!=0)(gdb)perrno$6=2