PostgreSQL事务快照源码分析

  • 源码版本:PG 13.3
  • 源码文件:src/backend/utils/time/snapmgr.c

1. 事务快照数据结构

typedef struct SnapshotData
{
    SnapshotType snapshot_type; /* type of snapshot */
    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? */

    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 文件中定义,其主要逻辑如下:

  1. snapshot->xip 通过 malloc 库函数动态分配内存,分配的事务数量为 GetMaxSnapshotXidCount() 函数的返回值
  2. snapshot->subxip 通过 malloc 库函数动态分配内存,分配的事务数量为 GetMaxSnapshotSubxidCount() 函数的返回值
  3. 以 ShmemVariableCache->latestCompletedXid+1 为基数,赋值给 globalxmin、xmin 和 xmax。遍历 procArray 中的所有 pgxact,其中最小的 pgxact->xmin 赋值给 globalxmin,最小的 pgxact->xid 赋值给 xmin。所有 pgxact->xid 都会放入 snapshot->xip 数组中。子事务没有溢出时, proc->subxids.xids 会放入 snapshot->subxip 数组中。
  4. 其他的 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 可见性判断的核心函数,其主要逻辑如下:

  1. 如果 xid < snapshot->xmin,返回 false,即 xid 不处于 in-progress 状态。
  2. 如果 xid >= snapshot->xmax,返回 true,即 xid 处于 in-progress 状态。
  3. 如果 xid 在 snapshot->subxip 数组中,则返回 true
  4. 如果 xid 是子事务,获取其最顶层的父事务的 xid,如果顶层父事务 xid < snapshot->xmin,返回 false
  5. 如果 xid 在 snapshot->xip 数组中,返回 true。
  6. 其他情况,返回 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() 函数,用于导入事务快照。

文章评论

0条评论