Postgresql中xlog生成和清理逻辑操作
0前言
1、2部分是对XLOG生成和清理逻辑的分析,XLOG暴涨的处理直接看第3部分。
1WAL归档
#在自动的WAL检查点之间的日志文件段的最大数量 checkpoint_segments= #在自动WAL检查点之间的最长时间 checkpoint_timeout= #缓解io压力 checkpoint_completion_target= #日志文件段的保存最小数量,为了备库保留更多段 wal_keep_segments= #已完成的WAL段通过archive_command发送到归档存储 archive_mode= #强制timeout切换到新的wal段文件 archive_timeout= max_wal_size= min_wal_size=
1.1不开启归档时
文件数量受下面几个参数控制,通常不超过
(2+checkpoint_completion_target)*checkpoint_segments+1
或
checkpoint_segments+wal_keep_segments+1个文件。
如果一个旧段文件不再需要了会重命名然后继续覆盖使用,如果由于短期的日志输出高峰导致了超过
3*checkpoint_segments+1个文件,直接删除文件。
1.2开启归档时
文件数量:删除归档成功的段文件
抽象来看一个运行的PG生成一个无限长的WAL日志序列。每段16M,这些段文件的名字是数值命名的,反映在WAL序列中的位置。在不用WAL归档的时候,系统通常只是创建几个段文件然后循环使用,方法是把不再使用的段文件重命名为更高的段编号。
当且仅当归档命令成功时,归档命令返回零。在得到一个零值结果之后,PostgreSQL将假设该WAL段文件已经成功归档,稍后将删除段文件。一个非零值告诉PostgreSQL该文件没有被归档,会周期性的重试直到成功。
2PG源码分析
2.1删除逻辑
触发删除动作
RemoveOldXlogFiles >CreateCheckPoint >CreateRestartPoint
wal_keep_segments判断(调用这个函数修改_logSegNo,然后再传入RemoveOldXlogFiles)
staticvoid KeepLogSeg(XLogRecPtrrecptr,XLogSegNo*logSegNo) { XLogSegNosegno; XLogRecPtrkeep; XLByteToSeg(recptr,segno); keep=XLogGetReplicationSlotMinimumLSN(); /*computelimitforwal_keep_segmentsfirst*/ if(wal_keep_segments>0) { /*avoidunderflow,don'tgobelow1*/ if(segno<=wal_keep_segments) segno=1; else segno=segno-wal_keep_segments; } /*thencheckwhetherslotslimitremovalfurther*/ if(max_replication_slots>0&&keep!=InvalidXLogRecPtr) { XLogSegNoslotSegNo; XLByteToSeg(keep,slotSegNo); if(slotSegNo<=0) segno=1; elseif(slotSegNo删除逻辑
staticvoid RemoveOldXlogFiles(XLogSegNosegno,XLogRecPtrendptr) { ... ... while((xlde=ReadDir(xldir,XLOGDIR))!=NULL) { /*IgnorefilesthatarenotXLOGsegments*/ if(strlen(xlde->d_name)!=24|| strspn(xlde->d_name,"0123456789ABCDEF")!=24) continue; /* *WeignorethetimelinepartoftheXLOGsegmentidentifiersin *decidingwhetherasegmentisstillneeded.Thisensuresthatwe *won'tprematurelyremoveasegmentfromaparenttimeline.Wecould *probablybealittlemoreproactiveaboutremovingsegmentsof *non-parenttimelines,butthatwouldbeawholelotmore *complicated. * *Weusethealphanumericsortingpropertyofthefilenamestodecide *whichonesareearlierthanthelastoffsegment. */ if(strcmp(xlde->d_name+8,lastoff+8)<=0) { if(XLogArchiveCheckDone(xlde->d_name)) #归档关闭返回真 #存在done文件返回真 #存在.ready返回假 #recheck存在done文件返回真 #重建.ready文件返回假 { /*Updatethelastremovedlocationinsharedmemoryfirst*/ UpdateLastRemovedPtr(xlde->d_name); #回收或者直接删除,清理.done和.ready文件 RemoveXlogFile(xlde->d_name,endptr); } } } ... ... }2.2归档逻辑
staticvoid pgarch_ArchiverCopyLoop(void) { charxlog[MAX_XFN_CHARS+1]; #拿到最老那个没有被归档的xlog文件名 while(pgarch_readyXlog(xlog)) { intfailures=0; for(;;) { /* *Donotinitiateanymorearchivecommandsafterreceiving *SIGTERM,norafterthepostmasterhasdiedunexpectedly.The *firstconditionistotrytokeepfromhavinginitSIGKILLthe *command,andthesecondistoavoidconflictswithanother *archiverspawnedbyanewerpostmaster. */ if(got_SIGTERM||!PostmasterIsAlive()) return; /* *Checkforconfigupdate.Thisissothatwe'lladoptanew *settingforarchive_commandassoonaspossible,evenifthere *isabacklogoffilestobearchived. */ if(got_SIGHUP) { got_SIGHUP=false; ProcessConfigFile(PGC_SIGHUP); } #archive_command没设的话不再执行 #我们的command没有设置,走的是这个分支 if(!XLogArchiveCommandSet()) { /* *ChangeWARNINGtoDEBUG1,sincewewillleftarchive_commandemptyto *letexternaltoolstomanagearchive */ ereport(DEBUG1, (errmsg("archive_modeenabled,yetarchive_commandisnotset"))); return; } #执行归档命令! if(pgarch_archiveXlog(xlog)) { #成功了,把.ready改名为.done pgarch_archiveDone(xlog); /* *TellthecollectorabouttheWALfilethatwesuccessfully *archived */ pgstat_send_archiver(xlog,false); break;/*outofinnerretryloop*/ } else { /* *TellthecollectorabouttheWALfilethatwefailedto *archive */ pgstat_send_archiver(xlog,true); if(++failures>=NUM_ARCHIVE_RETRIES) { ereport(WARNING, (errmsg("archivingtransactionlogfile\"%s\"failedtoomanytimes,willtryagainlater", xlog))); return;/*giveuparchivingfornow*/ } pg_usleep(1000000L);/*waitabitbeforeretrying*/ } } } }2.3ready生成逻辑
staticvoid XLogWrite(XLogwrtRqstWriteRqst,boolflexible) { ... if(finishing_seg) { issue_xlog_fsync(openLogFile,openLogSegNo); /*signalthatweneedtowakeupwalsenderslater*/ WalSndWakeupRequest(); LogwrtResult.Flush=LogwrtResult.Write;/*endofpage*/ #归档打开&&wal_level>=archive if(XLogArchivingActive()) #生成ready文件 XLogArchiveNotifySeg(openLogSegNo); XLogCtl->lastSegSwitchTime=(pg_time_t)time(NULL); ...2.4总结
ready文件只要满足archive_mode=on和wal_lever>=archive,就总会生成(XLogWrite函数调用生成)
因为archive_command设置空,所以ready文件的消费完全由外部程序控制
done文件的处理由PG完成,两个地方会触发done文件处理,检查点和重启点
处理多少done文件受wal_keep_segments和replication_slot控制(KeepLogSeg函数)
3WAL段累积的原因(长求总?)
注意:无论如何注意不要手动删除xlog文件
注意:checkpoint产生的日志回不立即生成ready文件,是在下一个xlog后一块生成的
3.1ReplicationSlot
打开流了复制槽
--流复制插槽 --如果restart_lsn和当前XLOG相差非常大的字节数,需要排查slot的订阅者是否能正常接收XLOG, --或者订阅者是否正常.长时间不将slot的数据取走,pg_xlog目录可能会撑爆 selectpg_xlog_location_diff(pg_current_xlog_location(),restart_lsn),* frompg_replication_slots;删除
selectpg_drop_replication_slot('xxx');删除后PG会在下一个checkpoint清理xlog
3.2较大的wal_keep_segments
检查参数配置,注意打开这个参数会使xlog和ready有一定延迟
3.3回收出现问题
如果不使用PG自动回收机制,数据库依赖外部程序修改.ready文件,需要检测回收进程
(archive_mode=onarchive_command='')3.4检查点间隔过长
检查参数配置
以上为个人经验,希望能给大家一个参考,也希望大家多多支持毛票票。如有错误或未考虑完全的地方,望不吝赐教。