Postgresql源码(16)数据库快照数据结构与获取

2022-07-14 13:39:25 浏览数 (1)

postgresql 13.5

1 数据结构

  • 快照类型有很多,但使用通用结构来管理,SnapshotSatisfiesFunc是负责处理该快照的函数。
  • 快照主要记录 全局活跃事物列表中的 最小事务ID、最大事务ID、当前活跃事务列表、当前事务commandid
代码语言:javascript复制
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记录了整个系统事务状态。

  • xmin:当前所有活跃事务的最小事务ID, 如果有一个事务小于这个ID,说明这个事务已经提交或终止了。
  • xmax:当前活跃的最大事务,这个值从latestCompletedXid拿到的,xmax=latestCompletedXid 1;当事务提交时,如果事务ID比latestCompletedXid大,需要更新latestCompletedXid为当前事务ID。也就是说大于等于xmax的事务一定是活跃的。
代码语言:javascript复制
GetSnapshotData(Snapshot snapshot) 
{
    ...
	/* xmax is always latestCompletedXid   1 */
	xmax = ShmemVariableCache->latestCompletedXid;
    ...
}

小于xmin的一定结束了,大于xmax的一定是活跃的,那中间的事务需要查看xip活跃事务列表。不在xip中的事务一定已经结束了。

2 快照处理函数

有了上述信息就可以判断元组的可见性了,判断会用到一批函数记录在SnapshotData->satisfies中(PG10)

PG13更新了函数指向方式:

代码语言:javascript复制
typedef struct SnapshotData
{
	SnapshotType snapshot_type; /* type of snapshot */
...

每种类型对应不同的处理函数

  • SNAPSHOT_MVCC
    • 使用快照判断MVCC可见、本事务写入可见
  • SNAPSHOT_SELF
    • 如果元组记录的事务已经提交,可见
    • 如果元组记录的事务正在运行,且是当前事务,可见
  • SNAPSHOT_ANY
    • 总是可见
  • SNAPSHOT_TOAST
    • toast可见性依赖表上元组
  • SNAPSHOT_DIRTY
    • 如果元组是当前事务写入的 或 写入的事务已经提交或终止,则可见性和SELF一致
    • 如果写入的事务还在运行,和SELF不同,收集当前元组的版本信息保存到快照中
  • SNAPSHOT_HISTORIC_MVCC
    • 逻辑复制中逻辑解码的可见性判断
  • SNAPSHOT_NON_VACUUMABLE
    • false表示元组已经对所有人不可见

3 快照获取

生成快照需要遍历PGPROC和PGXACT结构,查询正在运行的所有事务信息。

  • 对于RC级别的事务,每次操作都需要重新获取快照。
  • 对于RR、S级别的事务,只使用第一次获取的快照。

快照获取:GetTransactionSnapshot 快照生成:GetSnapshotData

代码语言:javascript复制
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;
}

0 人点赞