postgresql 13.5
typedef struct SnapshotData
{
/* 处理当前快照的函数 */
SnapshotSatisfiesFunc satisfies; /* tuple test function */
/* 多版本控制 */
TransactionId xmin; /* all XID < xmin are visible to me */
TransactionId xmax; /* all XID >= xmax are invisible to me */
/* 活跃事务列表 */
TransactionId *xip;
/* 活跃事务数量 */
uint32 xcnt; /* # of xact ids in xip[] */
/* 活跃子事务 */
TransactionId *subxip;
int32 subxcnt; /* # of xact ids in subxip[] */
bool suboverflowed; /* has the subxip array overflowed? */
bool takenDuringRecovery; /* recovery-shaped snapshot? */
bool copied; /* false if it's a static snapshot */
/* 事务中的command id 一个语句加1 */
CommandId curcid; /* in my xact, CID < curcid are visible */
/*
* An extra return value for HeapTupleSatisfiesDirty, not used in MVCC
* snapshots.
*/
uint32 speculativeToken;
/*
* Book-keeping information, used by the snapshot manager
*/
uint32 active_count; /* refcount on ActiveSnapshot stack */
uint32 regd_count; /* refcount on RegisteredSnapshots */
pairingheap_node ph_node; /* link in the RegisteredSnapshots heap */
/* 快照生成的时间 */
TimestampTz whenTaken; /* timestamp when snapshot was taken */
/* 快照生成时的lsn */
XLogRecPtr lsn; /* position in the WAL stream when taken */
} SnapshotData;
xmin、xmax、xip记录了整个系统事务状态。
xmax=latestCompletedXid+1
;当事务提交时,如果事务ID比latestCompletedXid大,需要更新latestCompletedXid为当前事务ID。也就是说大于等于xmax的事务一定是活跃的。
GetSnapshotData(Snapshot snapshot)
{
...
/* xmax is always latestCompletedXid + 1 */
xmax = ShmemVariableCache->latestCompletedXid;
...
}
小于xmin的一定结束了,大于xmax的一定是活跃的,那中间的事务需要查看xip活跃事务列表。不在xip中的事务一定已经结束了。
有了上述信息就可以判断元组的可见性了,判断会用到一批函数记录在SnapshotData->satisfies
中(PG10)
PG13更新了函数指向方式:
typedef struct SnapshotData
{
SnapshotType snapshot_type; /* type of snapshot */
...
每种类型对应不同的处理函数
生成快照需要遍历PGPROC和PGXACT结构,查询正在运行的所有事务信息。
快照获取:GetTransactionSnapshot 快照生成:GetSnapshotData
Snapshot
GetTransactionSnapshot(void)
{
// 逻辑解码直接拿HistoricSnapshot
if (HistoricSnapshotActive())
{
Assert(!FirstSnapshotSet);
return HistoricSnapshot;
}
// 第一次的事务拿快照会进这个分支,FirstSnapshotSet初始化=false
if (!FirstSnapshotSet)
{
// catalog的快照不能比事务快照旧,失效掉后面重新拿
InvalidateCatalogSnapshot();
...
// 如果隔离级别>=XACT_REPEATABLE_READ:可重复读或可串行化
if (IsolationUsesXactSnapshot())
{
// 初始化可串行化的控制结构
if (IsolationIsSerializable())
CurrentSnapshot = GetSerializableTransactionSnapshot(&CurrentSnapshotData);
else
// 可重复读直接获取当前快照
CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
/* Make a saved copy */
// 复制一份保存起来
CurrentSnapshot = CopySnapshot(CurrentSnapshot);
FirstXactSnapshot = CurrentSnapshot;
/* Mark it as "registered" in FirstXactSnapshot */
FirstXactSnapshot->regd_count++;
pairingheap_add(&RegisteredSnapshots, &FirstXactSnapshot->ph_node);
}
// 如果隔离级别是RC,直接获取一个当前快照
else
CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
// 下次进来不走这个分支
FirstSnapshotSet = true;
return CurrentSnapshot;
}
// 不是第一次调用了、而且隔离界别>=RR
if (IsolationUsesXactSnapshot())
return CurrentSnapshot;
/* Don't allow catalog snapshot to be older than xact snapshot. */
InvalidateCatalogSnapshot();
// RC级别、不是第一次拿快照:重新拿快照
CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
return CurrentSnapshot;
}