海山数据库(He3DB)源码详解:RecordTransactionCommit
函数解读
RecordTransactionCommit
是 PostgreSQL 中用于记录事务提交的关键函数。它负责在事务结束时执行一系列操作,包括日志记录、清理资源、更新事务状态等。以下是对这个函数的逐行解释:
1. 函数目标
RecordTransactionCommit
的主要目标是:
- 记录事务提交的 XLOG(Write-Ahead Logging)记录。
- 清理事务相关的资源。
- 更新事务状态(如 CLOG)。
- 确保在同步提交模式下,所有必要的日志记录都被写入磁盘。
2. 函数逻辑
- 获取事务 ID:
- 如果事务有有效的
XID
,进入提交记录的准备阶段。 - 如果没有有效的
XID
,检查是否有待删除的文件或统计信息。
- 检查待删除的文件或统计信息:
- 如果有,抛出错误(不能提交删除文件但没有
XID
的事务)。 - 如果没有,检查是否有无效化消息。
- 检查无效化消息:
- 如果有无效化消息,记录这些消息。
- 如果没有,直接结束(不写入 XLOG)。
- 准备提交记录:
- 记录逻辑解码相关的无效化信息。
- 写入提交记录。
- 检查提交模式:
- 如果是同步提交,刷新 XLOG 并更新 CLOG,等待同步复制完成。
- 如果是异步提交,设置异步提交的 LSN,不立即刷新 XLOG。
- 清理和退出:
- 释放分配的资源,结束函数执行。
以下是函数执行的流程图:
3. 源码解析
1. 初始化变量
TransactionId xid = GetTopTransactionIdIfAny();
bool markXidCommitted = TransactionIdIsValid(xid);
TransactionId latestXid = InvalidTransactionId;
int nrels = 0;
RelFileNode *rels = NULL;
int nchildren = 0;
TransactionId *children = NULL;
int ndroppedstats = 0;
xl_xact_stats_item *droppedstats = NULL;
int nmsgs = 0;
SharedInvalidationMessage *invalMessages = NULL;
bool RelcacheInitFileInval = false;
bool wrote_xlog = false;
xid
:获取当前事务的事务 ID。markXidCommitted
:检查事务 ID 是否有效,用于后续判断是否需要记录提交。latestXid
:用于记录最新的事务 ID。nrels
、rels
:用于存储需要删除的表文件信息。nchildren
、children
:用于存储子事务的事务 ID。ndroppedstats
、droppedstats
:用于存储统计信息的删除记录。nmsgs
、invalMessages
:用于存储无效化消息。RelcacheInitFileInval
:标记是否需要无效化关系缓存初始化文件。wrote_xlog
:标记是否写入了 XLOG。
2. 记录逻辑解码的无效化信息
if (XLogLogicalInfoActive()) {
LogLogicalInvalidations();
}
- 如果逻辑解码功能启用,则记录逻辑解码相关的无效化信息。
3. 获取提交记录所需的数据
nrels = smgrGetPendingDeletes(true, &rels);
nchildren = xactGetCommittedChildren(&children);
ndroppedstats = pgstat_get_transactional_drops(true, &droppedstats);
if (XLogStandbyInfoActive()) {
nmsgs = xactGetCommittedInvalidationMessages(&invalMessages,
&RelcacheInitFileInval);
}
smgrGetPendingDeletes
:获取当前事务中待删除的表文件信息。xactGetCommittedChildren
:获取子事务的事务 ID。pgstat_get_transactional_drops
:获取统计信息的删除记录。xactGetCommittedInvalidationMessages
:获取无效化消息。
4. 检查是否写入了 XLOG
wrote_xlog = (XactLastRecEnd != 0);
- 如果
XactLastRecEnd
不为 0,说明当前事务写入了 XLOG。
5. 检查是否需要记录提交
if (!markXidCommitted)
{
if (nrels != 0 || ndroppedstats != 0) {
elog(ERROR, "cannot commit a transaction that deleted files but has no xid");
}
Assert(nchildren == 0);
if (nmsgs != 0) {
LogStandbyInvalidations(nmsgs, invalMessages, RelcacheInitFileInval);
wrote_xlog = true;
}
if (!wrote_xlog)
goto cleanup;
}
- 如果事务没有分配事务 ID(
xid
),则:- 确保没有待删除的表文件或统计信息。
- 如果有无效化消息,则记录这些消息。
- 如果没有写入 XLOG,则直接跳到清理阶段。
6. 记录提交 XLOG
else
{
bool replorigin = (replorigin_session_origin != InvalidRepOriginId &&
replorigin_session_origin != DoNotReplicateId);
BufmgrCommit();
START_CRIT_SECTION();
MyProc->delayChkptFlags |= DELAY_CHKPT_START;
XactLogCommitRecord(xactStopTimestamp,
nchildren, children, nrels, rels,
ndroppedstats, droppedstats,
nmsgs, invalMessages,
RelcacheInitFileInval,
MyXactFlags,
InvalidTransactionId, NULL /* plain commit */ );
if (replorigin) {
replorigin_session_advance(replorigin_session_origin_lsn, XactLastRecEnd);
}
TransactionTreeSetCommitTsData(xid, nchildren, children,
replorigin_session_origin_timestamp,
replorigin_session_origin);
}
replorigin
:检查是否启用了复制原点功能。BufmgrCommit
:通知缓冲区管理器准备提交。START_CRIT_SECTION
:进入提交的关键部分,防止并发的检查点操作干扰。XactLogCommitRecord
:记录提交的 XLOG 记录。replorigin_session_advance
:如果启用了复制原点功能,更新复制原点的 LSN。TransactionTreeSetCommitTsData
:设置提交时间戳数据。
7. 决定是否异步提交
if ((wrote_xlog && markXidCommitted &&
synchronous_commit > SYNCHRONOUS_COMMIT_OFF) ||
forceSyncCommit || nrels > 0)
{
XLogFlush(XactLastRecEnd);
if (markXidCommitted) {
TransactionIdCommitTree(xid, nchildren, children);
}
}
else
{
XLogSetAsyncXactLSN(XactLastRecEnd);
if (markXidCommitted) {
TransactionIdAsyncCommitTree(xid, nchildren, children, XactLastRecEnd);
}
}
- 如果需要同步提交(
synchronous_commit
配置为非关闭状态),或者事务有文件清理操作,则:- 强制刷新 XLOG。
- 更新 CLOG(提交日志)。
- 否则:
- 设置异步提交的 XLOG LSN。
- 异步更新 CLOG。
8. 离开提交关键部分
if (markXidCommitted)
{
MyProc->delayChkptFlags &= ~DELAY_CHKPT_START;
END_CRIT_SECTION();
}
- 如果事务有有效的
xid
,则离开提交关键部分,允许检查点继续。
9. 等待同步复制
if (wrote_xlog && markXidCommitted) {
SyncRepWaitForLSN(XactLastRecEnd, true);
}
- 如果事务写入了 XLOG 并且有有效的
xid
,则等待同步复制完成。
10. 清理
cleanup:
if (rels) {
pfree(rels);
}
if (ndroppedstats) {
pfree(droppedstats);
}
- 释放分配的资源。
11. 返回最新的事务 ID
latestXid = TransactionIdLatest(xid, nchildren, children);
return latestXid;
- 计算并返回最新的事务 ID。
总结
RecordTransactionCommit
是一个复杂的函数,负责处理事务提交时的多种情况,包括:
- 记录提交的 XLOG。
- 更新 CLOG 和其他事务状态。
- 清理资源。
- 等待同步复制。
- 支持同步和异步提交。
这个函数是 PostgreSQL 事务提交机制的核心部分,确保事务的原子性和持久性。