简介
PostgreSQL利用快照和多版本实现了MVCC技术和快照隔离。利用快照判断元组的可见性在是事务操作中最为关键的一个环节,接口函数为HeapTupleSatisfiesMVCC。
快照相关概念知识: Postgres 源码解析4 MVCC 快照 (Snapshot)获取
元组可见性判断:postgres 源码解析2 元组可见性判断 t_infomask标识位
源码解析
1 接口定义
/*
* HeapTupleSatisfiesMVCC
* True iff heap tuple is valid for the given MVCC snapshot.
*
* See SNAPSHOT_MVCC's definition for the intended behaviour.
*
* Notice that here, we will not update the tuple status hint bits if the
* inserting/deleting transaction is still running according to our snapshot,
* even if in reality it's committed or aborted by now. This is intentional.
* Checking the true transaction state would require access to high-traffic
* shared data structures, creating contention we'd rather do without, and it
* would not change the result of our visibility check anyway. The hint bits
* will be updated by the first visitor that has a snapshot new enough to see
* the inserting/deleting transaction as done. In the meantime, the cost of
* leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
* call will need to run TransactionIdIsCurrentTransactionId in addition to
* XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
* coding where we tried to set the hint bits as soon as possible, we instead
* did TransactionIdIsInProgress in each call --- to no avail, as long as the
* inserting/deleting transaction was still running --- which was more cycles
* and more contention on ProcArrayLock.
*/
// 在快照范围内的事务不会更新其插入/删除状态元组的标识位。标志位的更新时机为事务完成后元组的第
// 一次访问。
static bool
HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
Buffer buffer)
2 流程分析
2.1 xmin未提交,元组在生成阶段
static bool
HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
Buffer buffer)
{
HeapTupleHeader tuple = htup->t_data;
Assert(ItemPointerIsValid(&htup->t_self));
Assert(htup->t_tableOid != InvalidOid);
// xmin未提交
if (!HeapTupleHeaderXminCommitted(tuple))
{
// xmin无效,元组正在插入
if (HeapTupleHeaderXminInvalid(tuple))
return false;
/* Used by pre-9.0 binary upgrades */
if (tuple->t_infomask & HEAP_MOVED_OFF)
{
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
if (TransactionIdIsCurrentTransactionId(xvac))
return false;
if (!XidInMVCCSnapshot(xvac, snapshot))
{
if (TransactionIdDidCommit(xvac))
{
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
InvalidTransactionId);
return false;
}
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
InvalidTransactionId);
}
}
/* Used by pre-9.0 binary upgrades */
else if (tuple->t_infomask & HEAP_MOVED_IN)
{
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
if (!TransactionIdIsCurrentTransactionId(xvac))
{
if (XidInMVCCSnapshot(xvac, snapshot))
return false;
if (TransactionIdDidCommit(xvac))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
InvalidTransactionId);
else
{
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
InvalidTransactionId);
return false;
}
}
}
// 插入元组时当前事务
else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
{
// 插入元组的动作 在获取快照之后 ==》 元组插入 cid 》= snapshotcurcid
// 不可见
if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
return false; /* inserted after scan started */
// xmax ,元组被删除或更新,但是操作被回滚,则元组对于本事务可见
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return true;
// 元组只是被锁住 ,可见
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
return true;
// multi事务
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
// 元组在事务内生成,但被其他事务修改 ===》 这种操作只能发生在该事务的子事务中
TransactionId xmax;
// 获取xmax
xmax = HeapTupleGetUpdateXid(tuple);
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
/* updating subtransaction must have aborted */
// 更新子事务终止,则元组可见
if (!TransactionIdIsCurrentTransactionId(xmax))
return true;
// 在快照获取后更新,可见,反之不可见
else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
return true; /* updated after scan started */
else
return false; /* updated before scan started */
}
// 子事务操作回滚,元组可见
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
{
/* deleting subtransaction must have aborted */
//子事务回滚, 设置标识位
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
InvalidTransactionId);
return true;
}
// 在获取快照前执行删除,元组可见,反之不可见
if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
return true; /* deleted after scan started */
else
return false; /* deleted before scan started */
}
// xmin处于活跃事务链表中,不可见
else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
return false;
// 元组提交,则添加HEAP_XMIN_COMMITTED标识位
else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
HeapTupleHeaderGetRawXmin(tuple));
// 回滚,则添加 HEAP_XMIN_INVALID标识位
else
{
/* it must have aborted or crashed */
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
InvalidTransactionId);
return false;
}
}
2.2 xmin提交,但xmax未提交
else
{
/* xmin is committed, but maybe not according to our snapshot */
// xmin 提交,但处于活跃快照列表中,则不可见 ===》等价视为运行中
if (!HeapTupleHeaderXminFrozen(tuple) &&
XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
return false; /* treat as still in progress */
}
/* by here, the inserting transaction has committed */
// xmin提交,xmax无效,说明插入成功或者删除回滚,元组可见
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
return true;
// 元组被锁住,可见
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
return true;
// xmax为 multi事务,说明元组被其他事务修改
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
TransactionId xmax;
/* already checked above */
Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
// 获取xmax
xmax = HeapTupleGetUpdateXid(tuple);
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
// 如果修改操作为当前事务,进一步判断
if (TransactionIdIsCurrentTransactionId(xmax))
{
// 修改动作迟于快照获取时机,则元组可见,反之不可见
if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
return true; /* deleted after scan started */
else
return false; /* deleted before scan started */
}
// 修改操作事务处于活跃事务列表,元组可见
if (XidInMVCCSnapshot(xmax, snapshot))
return true;
/ 修改操作事务已经提交,则元组不可见,反之可见
if (TransactionIdDidCommit(xmax))
return false; /* updating transaction committed */
/* it must have aborted or crashed */
return true;
}
2.3 xmin提交,xmax也提交
// xmin提交,但元组标识位还未更新
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
{
// 删除操作为当前事务
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
{
// 删除动作发生在获取快照之后,元组可见 ==》 对快照来说,删除还未发生
// 反之可见
if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
return true; /* deleted after scan started */
else
return false; /* deleted before scan started */
}
// 删除操作在活跃事务列表中,元组可见 ===》 正在删除,删除动作未完成
if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
return true;
// xmax 为commit, 删除操作回滚, 更新 HEAP_XMAX_INVALID标识位
// ===》 通过clog 获取事务提交状态
if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
{
/* it must have aborted or crashed */
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
InvalidTransactionId);
return true;
}
//
/* xmax transaction committed */
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
HeapTupleHeaderGetRawXmax(tuple));
}
// xamx 提交,且在快照中,则元组可见 ===》等价于更新操作未完成
else
{
/* xmax is committed, but maybe not according to our snapshot */
if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
return true; /* treat as still in progress */
}
/* xmax transaction committed */
// 提交,不可见
return false;
}