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<RedoRecPtr - PriorRedoPtr时:RedoRecPtr - PriorRedoPtr
2、CheckPointDistanceEstimate>=RedoRecPtr - PriorRedoPtr时:0.9*CheckPointDistanceEstimate+0.1*(RedoRecPtr - PriorRedoPtr)
*/
UpdateCheckPointDistanceEstimate(RedoRecPtr - PriorRedoPtr);
//_logSegNo = (PriorRedoPtr) / XLogSegSize
XLByteToSeg(PriorRedoPtr, _logSegNo);
KeepLogSeg(recptr, &_logSegNo);
_logSegNo--;
RemoveOldXlogFiles(_logSegNo, PriorRedoPtr, recptr);
2、两个宏定义
#define UsableBytesInPage (XLOG_BLCKSZ - SizeOfXLogShortPHD)//注意:不是文件第一页
#define UsableBytesInSegment ((XLOG_SEG_SIZE / XLOG_BLCKSZ) * UsableBytesInPage - (SizeOfXLogLongPHD - SizeOfXLogShortPHD))
3、函数XLogBytePosToRecPtr
static XLogRecPtr
XLogBytePosToRecPtr(uint64 bytepos)
{
//bytepos:不包括xlog页的页头等额外字节占用的大小
fullsegs = bytepos / UsableBytesInSegment;
bytesleft = bytepos % UsableBytesInSegment;
/*
1、如果bytesleft < XLOG_BLCKSZ-32,则表示定位到第一页上,则文件偏移值跳过第一页页头大小
2、如果bytesleft >= XLOG_BLCKSZ-32,则表示定位不是第一页
*/
if (bytesleft < XLOG_BLCKSZ - SizeOfXLogLongPHD){
/* fits on first page of segment */
seg_offset = bytesleft + SizeOfXLogLongPHD;
}else{
/* account for the first page on segment with long header */
seg_offset = XLOG_BLCKSZ;//先跳过第一页
bytesleft -= XLOG_BLCKSZ - SizeOfXLogLongPHD;//去掉第一页存放XLOG的大小
fullpages = bytesleft / UsableBytesInPage;//剩下的需要几个页
bytesleft = bytesleft % UsableBytesInPage;//剩下的偏移
// 文件偏移=第一页大小+剩下的几个页大小+剩下的偏移+最后一页的页头
seg_offset += fullpages * XLOG_BLCKSZ + bytesleft + SizeOfXLogShortPHD;
}
//result=(fullsegs) * XLOG_SEG_SIZE + seg_offset
XLogSegNoOffsetToRecPtr(fullsegs, seg_offset, result);
return result;
}
4、函数KeepLogSeg
static void
KeepLogSeg(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 < segno)
segno = slotSegNo;
}
/* don't delete WAL segments newer than the calculated segment */
if (segno < *logSegNo)
*logSegNo = segno;
//note:
//如果计算出的segno比上次checkpoint时的文件号logSegNo还有小,则取这次计算的segno
//如果计算出的segno比上次checkpoint时的文件号logSegNo大,则取上次checkpoint时的文件号。
//因为恢复时如果是主机,读取最新checkpoint记录失败后,会读取上一次checkpoint记录,如果上次checkpoint的文件被删除,这里就读取不到记录了
}
5、函数RemoveOldXlogFiles
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;
}

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
神奇高效的Linux命令行
一、为什么要学linux命令 Linux是由命令行组成的操作系统,精髓在命令行,无论图形界面发展到什么水平,命令行方式的操作永远是不会变的。Linux命令有许多强大的功能:从简单的磁盘操作、文件存取,到进行复杂的多媒体图像和流媒体文件的制作,都离不开命令行。虽然Linux也有桌面系统,但是X-window也只是运行在命令行模式下的一个应用程序。 因此,可以说命令是学习Linux系统的基础,在很大程度上学习Linux就是学习命令,很多Linux高手其实都是玩儿命令很熟练的人。 也许对于刚刚从Windows系统进入Linux学习的初学者来说,立刻进入枯燥的命令学习实在太难,但是一旦学会就爱不释手。因为它的功能实在太强大了。 有很多初学者都会遇到这么一个问题,自己对系统的每个命令都很熟悉,但是在系统出现故障的时候,就无从下手了,甚至不知道在什么时候用什么命令去检查系统,这是很多Linux新手最无奈的事情了。说到底,就是学习的理论知识没有很好地与系统实际操作相结合。 很多Linux知识,例如每个命令的参数含义,在书本上说得很清楚,看起来也很容易理解,但是一旦组合起来使用,却并不那么容易,没有多...
-
下一篇
如何使用Python搞定数据分析
大数据是当前比较火的方向,依托于这一行业,互联网公司对数据分析人员需求也逐年递增,数据分析师也成为是当前比较火的从业方向。 数据是企业数字资产,如何让这些资产转化为实际价值? 通过数据分析工具或者数据分析人员对数据进行挖掘,挖掘潜在价值,为指导工作及公司决策层提供数据支撑。 数据分析师主要工作是什么? 数据分析师主要工作包括:数据收集,清洗,存储,建模,可视化分析,分析决策等;不同生产环节所花费时间与工作量是不同的,数据收集,清洗,存储可能需要占用大量工作与时间。整个数据处理流程如下图: 数据分析师需要有良好的大局观,能够利用数据分析工具,掌握知识点,结合所在行业为公司创造更大价值。按从业方向,数据分析师可以分为下面两种: 1>偏产品运营,通过对数据分析提供运营效率;2>注重数据挖掘,通过算法,模型找到数据价值; 数据分析师从事行业比较广泛,例如:互联网公司,金融行业,智能零售等; 作为初学者,如何快速入门? 这个问题太大,没有目的性,老猫先来提出几个基础问题: 1>如何找到数据分析切入点?2>是否了解常用的数据分析指标?3>是否掌握一种数据分析工具或者编...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2全家桶,快速入门学习开发网站教程
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2整合Redis,开启缓存,提高访问速度
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Dcoker安装(在线仓库),最新的服务器搭配容器使用