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检查点间隔过长
检查参数配置
以上为个人经验,希望能给大家一个参考,也希望大家多多支持毛票票。如有错误或未考虑完全的地方,望不吝赐教。
 