PostgreSQL事务快照源码分析

2023年 8月 15日 86.3k 0

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

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

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论