相关 《Postgresql源码(69)常规锁简单分析》 《Postgresql源码(73)两阶段事务PrepareTransaction事务如何与会话解绑(上)》 《Postgresql源码(74)两阶段事务PrepareTransaction事务如何与会话解绑(下)》
InitProcGlobal时,申请的所有PGPROC如下图所示:
注意:
GlobalTransactionData->pgprocno
在allProcs
里面找即可:proc = &ProcGlobal->allProcs[gxact->pgprocno];
。void
PostPrepare_Locks(TransactionId xid)
{
PGPROC *newproc = TwoPhaseGetDummyProc(xid, false);
HASH_SEQ_STATUS status;
LOCALLOCK *locallock;
LOCK *lock;
PROCLOCK *proclock;
PROCLOCKTAG proclocktag;
int partition;
START_CRIT_SECTION();
hash_seq_init(&status, LockMethodLocalHash);
while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
{
LOCALLOCKOWNER *lockOwners = locallock->lockOwners;
bool haveSessionLock;
bool haveXactLock;
int i;
VXID锁无需记录,一阶段提交完了会话就没了。
if (locallock->tag.lock.locktag_type == LOCKTAG_VIRTUALTRANSACTION)
continue;
haveSessionLock = haveXactLock = false;
for (i = locallock->numLockOwners - 1; i >= 0; i--)
{
if (lockOwners[i].owner == NULL)
haveSessionLock = true;
else
haveXactLock = true;
}
只留下事务锁,会话锁不是一阶段提交需要关心的。
if (!haveXactLock)
continue;
/* Mark the proclock to show we need to release this lockmode */
if (locallock->nLocks > 0)
locallock->proclock->releaseMask |= LOCKBIT_ON(locallock->tag.mode);
清理本地锁表,一阶段提交后,本地锁表不应该在保存事务锁了。
RemoveLocalLock(locallock);
}
遍历数据结构如下图:
等价与
遍历这个数组上每个位置的链表。 for (partition = 0; partition < NUM_LOCK_PARTITIONS; partition++)
{
LWLock *partitionLock;
SHM_QUEUE *procLocks = &(MyProc->myProcLocks[partition]);
PROCLOCK *nextplock;
partitionLock = LockHashPartitionLockByIndex(partition);
// 优化:判断成功不用加锁
if (SHMQueueNext(procLocks, procLocks,
offsetof(PROCLOCK, procLink)) == NULL)
continue; /* needn't examine this partition */
LWLockAcquire(partitionLock, LW_EXCLUSIVE);
遍历每个分区的procLocks链表,拿到所有PROCLOCK。
for (proclock = (PROCLOCK *) SHMQueueNext(procLocks, procLocks,
offsetof(PROCLOCK, procLink));
proclock;
proclock = nextplock)
{
/* Get link first, since we may unlink/relink this proclock */
nextplock = (PROCLOCK *)
SHMQueueNext(procLocks, &proclock->procLink,
offsetof(PROCLOCK, procLink));
lock = proclock->tag.myLock;
if (lock->tag.locktag_type == LOCKTAG_VIRTUALTRANSACTION)
continue;
/* Ignore it if nothing to release (must be a session lock) */
if (proclock->releaseMask == 0)
continue;
从procLink链表上删除这个PROCLOCK。
SHMQueueDelete(&proclock->procLink);
lock没变,myProc换成两阶段事务专用dummyproc。
proclocktag.myLock = lock;
proclocktag.myProc = newproc;
tag变了,用hash_update_hash_key更新主锁表LockMethodProcLockHash哈希表中的记录。
(注意主锁表LockMethodLockHash不需要改,因为tag和value都没变)
if (!hash_update_hash_key(LockMethodProcLockHash,
(void *) proclock,
(void *) &proclocktag))
elog(PANIC, "duplicate entry found while reassigning a prepared transaction's locks");
重新插入procLink链表。
/* Re-link into the new proc's proclock list */
SHMQueueInsertBefore(&(newproc->myProcLocks[partition]),
&proclock->procLink);
PROCLOCK_PRINT("PostPrepare_Locks: updated", proclock);
} /* loop over PROCLOCKs within this partition */
LWLockRelease(partitionLock);
} /* loop over partitions */
END_CRIT_SECTION();
}