Postgresql事务在事务提交时(执行commit的最后阶段)会通过加锁阻塞checkpoint的执行,尽管时间非常短,下面分析为什么需要这样做? 不这样做会有什么问题。
1 提交堆栈
看一下事务提交堆栈
代码语言:javascript复制#1 0x0000000000539175 in CommitTransaction () at xact.c:2079
#2 0x0000000000539e04 in CommitTransactionCommand () at xact.c:2824
#3 0x000000000087d1ea in finish_xact_command () at postgres.c:2482
#4 0x000000000087af27 in exec_simple_query (query_string=0x24050e0 "insert into t1 values (1,1);") at postgres.c:1154
2 函数调用过程
关键流程
代码语言:javascript复制CommitTransaction
...
latestXid = RecordTransactionCommit();
...
BufmgrCommit()
START_CRIT_SECTION()
【关键流程】
END_CRIT_SECTION()
latestXid = TransactionIdLatest(xid, nchildren, children);
SyncRepWaitForLSN(XactLastRecEnd, true);
return latestXid;
...
ProcArrayEndTransaction(MyProc, latestXid);
...
// clean ...
3 关键流程
delayChkpt阻塞checkpoint发生位置:
1 事务提交配置delayChkpt
代码语言:javascript复制RecordTransactionCommit
...
START_CRIT_SECTION();
MyPgXact->delayChkpt = true;
/* 写XLOG:COMMIT */
/* 写CLOG:内存写不刷盘 */
MyPgXact->delayChkpt = false;
...
2 CreateCheckPoint等待delayChkpt
联动CreateCheckPoint,会在【2】等在所有Xact的delayChkpt为false才能继续
代码语言:javascript复制CreateCheckPoint
// 【1】计算位置(重要)
WALInsertLockAcquireExclusive();
curInsert = XLogBytePosToRecPtr(Insert->CurrBytePos);
freespace = INSERT_FREESPACE(curInsert);
if (freespace == 0)
{
if (curInsert % XLogSegSize == 0)
curInsert = SizeOfXLogLongPHD;
else
curInsert = SizeOfXLogShortPHD;
}
checkPoint.redo = curInsert;
RedoRecPtr = XLogCtl->Insert.RedoRecPtr = checkPoint.redo;
WALInsertLockRelease();
// 【2】通过delayChkpt等其他所有正在提交中、正在写日志的事务
vxids = GetVirtualXIDsDelayingChkpt(&nvxids);
if (nvxids > 0)
{
do
{
pg_usleep(10000L); /* wait for 10 msec */
} while (HaveVirtualXIDsDelayingChkpt(vxids, nvxids));
}
pfree(vxids);
// 【3】刷数据
CheckPointGuts(checkPoint.redo, flags);
// 【4】记chkpt日志
XLogBeginInsert();
XLogRegisterData((char *) (&checkPoint), sizeof(checkPoint));
recptr = XLogInsert(RM_XLOG_ID,
shutdown ? XLOG_CHECKPOINT_SHUTDOWN :
XLOG_CHECKPOINT_ONLINE);
XLogFlush(recptr);
3 为什么checkpoint需要等事务提交?
原因:情况二的分析。
确定REDO位点是在createCheckpoint的函数前面执行的,checkpoint和事务提交并发会有下面三种情况发生(假设没有delayChkpt会有情况二发生)
- 情况一:redo point在commit提交前,那么如果crash发生了,redo过程会覆盖这条xlog,不会有问题
- 情况二:如果没有delayChkpt,redo point可能发生在上图中的位置(然后checkpoint刷完数据后,当前事务才写clog),XLOG已经先写了,如果crash发生了,redo过程不会覆盖这条xlog,而且clog信息不存在,那么commit信息彻底丢掉了。
- 情况三:redo point在事务提交后,redo时xlog虽然还是做不到,但是clog一定会被刷下去,所以我们不会丢失事务提交信息。