Postgresql源码(121)事务状态中childXids的作用

总结

  • PG的子事务回滚是真回滚(直接回滚了,不管顶层事务提交还是回滚)。

  • PG的子事务提交是假提交(子事务提交后会把决定权交给顶层事务,随顶层事务提交、回滚)。

子事务提交后,将xid记录到父事务的childXids,父事务的childXids就表示下面已经提交的子事务,这些子事务xid在后续mvcc计算中,会完全等效与当前的事务xid。

childXids

在Postgresql的事务状态中,存在childXids数组,本篇分析该结构的用途和原理。

c 复制代码
typedef struct TransactionStateData
{
	...

	TransactionId *childXids;	/* subcommitted child XIDs, in XID order */
	int			nChildXids;		/* # of subcommitted child XIDs */
	int			maxChildXids;	/* allocated size of childXids[] */

	...

	struct TransactionStateData *parent;	/* back link to parent */
} TransactionStateData;

typedef TransactionStateData *TransactionState;

用途

1 读取childXids:TransactionIdIsCurrentTransactionId

判断事务ID是否为当前事务。

如果xid和当前事务的xid不同,另外会从当前事务记录的childXids中再找一遍。

  • 因为childXids里面记录了当前事务下,已经提交了的子事务(只有提交了的,没有回滚的),所以这些提交的子事务xid就等同于主事务xid。
  • childXids是有序的,二分法即可。
c 复制代码
bool
TransactionIdIsCurrentTransactionId(TransactionId xid)
{
	...

	if (TransactionIdEquals(xid, GetTopTransactionIdIfAny()))
		return true;

	...

	for (s = CurrentTransactionState; s != NULL; s = s->parent)
	{
		...

		low = 0;
		high = s->nChildXids - 1;
		while (low <= high)
		{
			int			middle;
			TransactionId probe;

			middle = low + (high - low) / 2;
			probe = s->childXids[middle];
			if (TransactionIdEquals(probe, xid))
				return true;
			else if (TransactionIdPrecedes(probe, xid))
				low = middle + 1;
			else
				high = middle - 1;
		}
	}

	return false;
}

2 写入childXids:AtSubCommit_childXids

在子事务提交时,会执行AtSubCommit_childXids:

  1. 将本层的xid添加到上层事务的childXids中。
  2. 将本层记录的childXids传递到上层事务的childXids中。
c 复制代码
static void
AtSubCommit_childXids(void)
{
	...

	new_nChildXids = s->parent->nChildXids + s->nChildXids + 1;
  ...

	s->parent->childXids[s->parent->nChildXids] = XidFromFullTransactionId(s->fullTransactionId);

	if (s->nChildXids > 0)
		memcpy(&s->parent->childXids[s->parent->nChildXids + 1],
			   s->childXids,
			   s->nChildXids * sizeof(TransactionId));

	s->parent->nChildXids = new_nChildXids;

	/* Release child's array to avoid leakage */
	if (s->childXids != NULL)
		pfree(s->childXids);
	/* We must reset these to avoid double-free if fail later in commit */
	s->childXids = NULL;
	s->nChildXids = 0;
	s->maxChildXids = 0;
}

PG子事务维护链式结构,当子事务提交时,需要把自己记录"commited childs"信息逐层上交。

例如三层子事务的场景下:

复制代码
												childXids
top-transaction     		    []
  sub-transaction-16401     []
    sub-transaction-16402   []
      sub-transaction-16403 []        <-- will commit

														childXids
top-transaction     		    		[]
  sub-transaction-16401     		[]
    sub-transaction-16402   		[]
    														^
    														|
      sub-transaction-16403 		[]				<-- committing

														childXids
top-transaction        					[]
  sub-transaction-16401     		[]
    sub-transaction-16402   		[16403]  	<-- committed

														childXids
top-transaction        					[]
  sub-transaction-16401     		[]
    sub-transaction-16402   		[16403]  	<-- will commit

														childXids
top-transaction        					[]
  sub-transaction-16401     		[16402, 16403]  <-- committed

当前事务记录的childXids在进行mvcc判断时,完全等效于自己的xid。

3 其他

AtSubAbort_childXids:子事务回滚时,需要清理childXids。

SerializeTransactionState:序列化事务状态。

其他还有初始化、清理等,不在列举。

相关推荐
广州智造3 小时前
OptiStruct实例:3D实体转子分析
数据库·人工智能·算法·机器学习·数学建模·3d·性能优化
技术宝哥6 小时前
Redis(2):Redis + Lua为什么可以实现原子性
数据库·redis·lua
学地理的小胖砸7 小时前
【Python 操作 MySQL 数据库】
数据库·python·mysql
dddaidai1238 小时前
Redis解析
数据库·redis·缓存
数据库幼崽8 小时前
MySQL 8.0 OCP 1Z0-908 121-130题
数据库·mysql·ocp
Amctwd8 小时前
【SQL】如何在 SQL 中统计结构化字符串的特征频率
数据库·sql
betazhou9 小时前
基于Linux环境实现Oracle goldengate远程抽取MySQL同步数据到MySQL
linux·数据库·mysql·oracle·ogg
lyrhhhhhhhh9 小时前
Spring 框架 JDBC 模板技术详解
java·数据库·spring
喝醉的小喵10 小时前
【mysql】并发 Insert 的死锁问题 第二弹
数据库·后端·mysql·死锁
付出不多11 小时前
Linux——mysql主从复制与读写分离
数据库·mysql