海山数据库(He3DB)源码详解:He3DB-CLOG日志管理器函数(2)
背景
本文主要针对海山数据库中CLOG日志管理器部分关键函数源码进行研读。主要涉及ExtendCLOG、TruncateCLOG
ExtendCLOG
背景:
为新分配的事务ID创建CLOG 日志空间。当一个事务的 CLOG 日志记录位于一个CLOG日志页面的首部时,若这个事务要写一条CLOG 日志记录,就需要调用 ExtendCLOG 以创建一个新的CLOG 日志页面供该条CLOG日志记录写入
void
ExtendCLOG(TransactionId newestXact)
{
int pageno;
/*
* No work except at first XID of a page. But beware: just after
* wraparound, the first XID of page zero is FirstNormalTransactionId.
*/
if (TransactionIdToPgIndex(newestXact) != 0 &&
!TransactionIdEquals(newestXact, FirstNormalTransactionId))
return;
pageno = TransactionIdToPage(newestXact);
LWLockAcquire(XactSLRULock, LW_EXCLUSIVE);
/* Zero the page and make an XLOG entry about it */
ZeroCLOGPage(pageno, true);
LWLockRelease(XactSLRULock);
}
void
ExtendCLOG(TransactionId newestXact)
newestXact
表示最新的事务id
if (TransactionIdToPgIndex(newestXact) != 0 &&
!TransactionIdEquals(newestXact, FirstNormalTransactionId))
return;
判断新事务id是不是新页面的第一个事务id:
TransactionIdToPgIndex(newestXact)
将新事务id转换为CLOG中页面的索引,如果结果不为0,说明这不是新页面的开始- 其次检测
newestXact
是否不等于FirstNormalTransactionId
,这是因为在事务ID回绕(wraparound)之后,第一个页面的第一个事务ID将是FirstNormalTransactionId
。如果这两个条件都不满足,函数将直接返回,不做任何操作。
pageno = TransactionIdToPage(newestXact);
- 计算新事务id对应的页号
LWLockAcquire(XactSLRULock, LW_EXCLUSIVE);
ZeroCLOGPage(pageno, true);
LWLockRelease(XactSLRULock);
- 获取名为
XactSLRULock
的轻量级排他锁 - 调用
ZeroCLOGPage
函数将页号为pageno
的页面清零,并设置第二个参数为true
以指示需要记录xlog日志 -
- 这意味着该操作将在 PostgreSQL 的写前日志(WAL)中留下记录,以确保在系统崩溃后能够恢复。
- 释放轻量级锁
TruncateCLOG
作用:删除过时的CLOG日志记录,以节省磁盘空间并优化性能
源码:
void
TruncateCLOG(TransactionId oldestXact, Oid oldestxid_datoid)
{
int cutoffPage;
cutoffPage = TransactionIdToPage(oldestXact);
/* Check to see if there's any files that could be removed */
if (!SlruScanDirectory(XactCtl, SlruScanDirCbReportPresence, &cutoffPage))
return; /* nothing to remove */
/*
* Advance oldestClogXid before truncating clog, so concurrent xact status
* lookups can ensure they don't attempt to access truncated-away clog.
*
* It's only necessary to do this if we will actually truncate away clog
* pages.
*/
AdvanceOldestClogXid(oldestXact);
/*
* Write XLOG record and flush XLOG to disk. We record the oldest xid
* we're keeping information about here so we can ensure that it's always
* ahead of clog truncation in case we crash, and so a standby finds out
* the new valid xid before the next checkpoint.
*/
WriteTruncateXlogRec(cutoffPage, oldestXact, oldestxid_datoid);
/* Now we can remove the old CLOG segment(s) */
SimpleLruTruncate(XactCtl, cutoffPage);
}
int cutoffPage;
cutoffPage = TransactionIdToPage(oldestXact);
计算旧事务idoldestXact
对应的CLOG页面号,并赋给cutoffPage
if (!SlruScanDirectory(XactCtl, SlruScanDirCbReportPresence, &cutoffPage))
return;
使用 SlruScanDirectory
函数和回调函数 SlruScanDirCbReportPresence
,以cutoffPage页面为参照,扫描 CLOG 目录,以检查是否有文件可以被移除。如果没有文件可以移除(即没有早于 cutoffPage
的页面),则函数直接返回。
AdvanceOldestClogXid(oldestXact);
在截断 CLOG 之前,通过 AdvanceOldestClogXid
函数更新 oldestClogXid
,确保并发事务状态查找不会尝试访问已截断的 CLOG 页面。
WriteTruncateXlogRec(cutoffPage, oldestXact, oldestxid_datoid);
通过WriteTruncateXlogRec
函数写入一个 XLOG 记录,记录截断操作的截断点和最旧事务ID。这是为了确保在系统崩溃或恢复时,可以正确地恢复 CLOG 状态。
SimpleLruTruncate(XactCtl, cutoffPage);
使用 SimpleLruTruncate
函数实际执行截断操作,移除早于 cutoffPage
的 CLOG 页面