总结
- PG的子事务回滚是真回滚(直接回滚了,不管顶层事务提交还是回滚)。
- PG的子事务提交是假提交(子事务提交后会把决定权交给顶层事务,随顶层事务提交、回滚)。
子事务提交后,将xid记录到父事务的childXids,父事务的childXids就表示下面已经提交的子事务,这些子事务xid在后续mvcc计算中,会完全等效与当前的事务xid。
childXids
在Postgresql的事务状态中,存在childXids数组,本篇分析该结构的用途和原理。
代码语言:javascript复制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是有序的,二分法即可。
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:
- 将本层的xid添加到上层事务的childXids中。
- 将本层记录的childXids传递到上层事务的childXids中。
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”信息逐层上交。
例如三层子事务的场景下:
代码语言:javascript复制 childXids
top-transaction []
sub-transaction-16401 []
sub-transaction-16402 []
sub-transaction-16403 [] <-- will commit
代码语言:javascript复制 childXids
top-transaction []
sub-transaction-16401 []
sub-transaction-16402 []
^
|
sub-transaction-16403 [] <-- committing
代码语言:javascript复制 childXids
top-transaction []
sub-transaction-16401 []
sub-transaction-16402 [16403] <-- committed
代码语言:javascript复制 childXids
top-transaction []
sub-transaction-16401 []
sub-transaction-16402 [16403] <-- will commit
代码语言:javascript复制 childXids
top-transaction []
sub-transaction-16401 [16402, 16403] <-- committed
当前事务记录的childXids在进行mvcc判断时,完全等效于自己的xid。
3 其他
AtSubAbort_childXids:子事务回滚时,需要清理childXids。
SerializeTransactionState:序列化事务状态。
其他还有初始化、清理等,不在列举。