- 源码版本:PG 13.3
- 源码文件:src/backend/utils/time/snapmgr.c
1. 事务快照数据结构
typedef struct SnapshotData
{
SnapshotType snapshot_type; /* type of snapshot */
TransactionId xmin; /* 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? */
CommandId curcid; /* in my xact, CID < curcid are visible */
uint32 speculativeToken;
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 */
XLogRecPtr lsn; /* position in the WAL stream when taken */
} SnapshotData;
2. 初始化共享内存
- SnapMgrInit() 初始化共享内存
- SnapMgrShmemSize(),获取共享内存大小
共享内存中存储的是 OldSnapshotControlData 结构的数据。用于 GUC 参数 old_snapshot_threshold 相关特性,默认值为 -1,该特性默认是关闭的。
3. 获取事务快照
GetTransactionSnapshot()
GetSnapshotData()
GetSnapshotData() 函数在 procarray.c 文件中定义,其主要逻辑如下:
- snapshot->xip 通过 malloc 库函数动态分配内存,分配的事务数量为 GetMaxSnapshotXidCount() 函数的返回值
- snapshot->subxip 通过 malloc 库函数动态分配内存,分配的事务数量为 GetMaxSnapshotSubxidCount() 函数的返回值
- 以 ShmemVariableCache->latestCompletedXid+1 为基数,赋值给 globalxmin、xmin 和 xmax。遍历 procArray 中的所有 pgxact,其中最小的 pgxact->xmin 赋值给 globalxmin,最小的 pgxact->xid 赋值给 xmin。所有 pgxact->xid 都会放入 snapshot->xip 数组中。子事务没有溢出时, proc->subxids.xids 会放入 snapshot->subxip 数组中。
- 其他的 snapshot 成员赋值,如下:
snapshot->xmin = xmin; // 所有 pgxact->xid 的最小值
snapshot->xmax = xmax; // 为 ShmemVariableCache->latestCompletedXid+1
snapshot->xcnt = count; // 事务数量
snapshot->subxcnt = subcount; // 子事务数量
snapshot->suboverflowed = suboverflowed; // 子事务是否溢出
snapshot->curcid = GetCurrentCommandId(false); // 当前的命令ID
4. 四个事务号全局变量的含义
在调用 GetSnapshotData() 函数时,会对如下四个全局变量进行更新,这四个事务号相关的全局变量含义如下:
- TransactionXmin
- RecentXmin
- RecentGlobalXmin,
- RecentGlobalDataXmin
(1) TransactionXmin
// TransactionXmin 是所有 pgxact->xid 中的最小值,只在第一次调用时设置
if (!TransactionIdIsValid(MyPgXact->xmin))
MyPgXact->xmin = TransactionXmin = xmin;
(2) RecentXmin
// RecentXmin 是所有 pgxact->xid 中的最小值,每次调用都会更新该变量
RecentXmin = xmin;
(3) RecentGlobalXmin 和 RecentGlobalDataXmin
(1) globalxmin 是所有 pgxact->xmin 和 pgxact->xid 中的最小值
(2) RecentGlobalXmin = globalxmin - vacuum_defer_cleanup_age;
(3) 如果 replication_slot_xmin 比 RecentGlobalXmin 小,则:
RecentGlobalXmin = replication_slot_xmin
(4) RecentGlobalDataXmin = RecentGlobalXmin;
(5) 如果 replication_slot_catalog_xmin 比 RecentGlobalXmin 小,则:
RecentGlobalXmin = replication_slot_catalog_xmin;
RecentGlobalDataXmin 不会与 replication_slot_catalog_xmin 进行比较。
5. 根据快照判断 xid 的状态
XidInMVCCSnapshot() 函数,通过快照判断事务 xid 是否正在进行中(in-progress),这是 PG MVCC 可见性判断的核心函数,其主要逻辑如下:
- 如果 xid xmin,返回 false,即 xid 不处于 in-progress 状态。
- 如果 xid >= snapshot->xmax,返回 true,即 xid 处于 in-progress 状态。
- 如果 xid 在 snapshot->subxip 数组中,则返回 true
- 如果 xid 是子事务,获取其最顶层的父事务的 xid,如果顶层父事务 xid xmin,返回 false
- 如果 xid 在 snapshot->xip 数组中,返回 true。
- 其他情况,返回 false
6. 导出事务快照
pg_export_snapshot() 函数用于导出事务快照,内部调用 ExportSnapshot() 函数,导出目录为 pg_snapshots,命名格式为:
snprintf(path, sizeof(path), SNAPSHOT_EXPORT_DIR "/%08X-%08X-%d",
MyProc->backendId, MyProc->lxid, list_length(exportedSnapshots) + 1);
7. 导入事务快照
执行 set transaction snapshot 命令时,调用 ImportSnapshot() 函数,用于导入事务快照。