Postgresql源码(74)两阶段事务PrepareTransaction事务如何与会话解绑(下)
2023-06-13 09:11:01 时间
相关 《Postgresql源码(69)常规锁简单分析》 《Postgresql源码(73)两阶段事务PrepareTransaction事务如何与会话解绑(上)》 《Postgresql源码(74)两阶段事务PrepareTransaction事务如何与会话解绑(下)》
1 两阶段事务使用的特殊PGPROC
InitProcGlobal时,申请的所有PGPROC如下图所示:
- PGPROC数组分三段使用,数组入口在ProcGlobal->allProcs,数组申请在InitProcGlobal
- 第一段:正常的后端服务进程
- 第二段:5个,bgwriter、checkpointer、walwriter或startup、walsender、walreceiver。
- 第三段:Dummy PROC两阶段事务专用,一阶段提交后,事务锁与第一段中的PGPROC解锁,与第三段中的PGPROC关联。
注意:
- 在TwoPhaseShmemInit中,已经把PGPROC和GlobalTransactionData建立了一一对应关系。
- 使用PGPROC是,只需要用
GlobalTransactionData->pgprocno
在allProcs
里面找即可:proc = &ProcGlobal->allProcs[gxact->pgprocno];
。 - 这个对应关系是一一对应的,初始化之后就不会改变了。
2 一阶段提交解锁流程
第一步:通过GlobalTransactionData拿到PGPROC
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;
- locallock->lockOwners是一个数组,初始8个值,每个值记录了一个Owner,数组记录了这把锁被多少个owner获取过。owner有值代表事务申请的锁、owner没值表示会话申请的锁。
- 如果是sessionlock,表示在LockAcquire时的第三个参数为false,表示这把锁和会话关联,不和事务关联。
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);
}
第三步:开始扫主锁表
遍历数据结构如下图:
- 当前进程的PGPROC中有一个16个链表头组成的锁链数组,每一个位置上是一个PROCLOCK链表头。
- 数组16个位置对应主锁表的16个分组,遍历全部主锁表
等价与
遍历这个数组上每个位置的链表。
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();
}
相关文章
- Postgresql源码(79)plpgsql中多层调用时参数传递关键点分析(pl参数)
- Postgresql源码(66)insert on conflict语法介绍与内核执行流程解析
- Postgresql源码(68)virtualxid锁的原理和应用场景
- Postgresql简单insert时modify节点的构造
- Postgresql源码(100)Portal与事务的关系(顶层事务与子事务)
- python-Python与PostgreSQL数据库-PostgreSQL数据库的基本知识(一)
- 教你PostgreSQL解析URL的正确方法
- PostgreSQL 如何禁用全表扫描的方法介绍
- Debian中PostgreSQL数据库安装配置实例
- PostgreSQL 42P11: invalid_cursor_definition 报错 故障修复 远程处理
- 管理精通Postgresql权限管理,实现数据安全(postgresql权限)
- 对比对比深度:PostgreSQL与Oracle之间的差异(postgresql和oracle)
- PostgreSQL编码协议:改变数据库架构(postgresql协议)
- PostgreSQL:实现强大优势的数据库(postgresql优势)
- 数据库使用PostgreSQL实现分布式数据库构建(postgresql分布式)
- 的性能优势Postgresql在性能方面的优势——德哥的深度解读(德哥postgresql)
- 中跳出跳出PostgreSQL循环的实践方法(postgresql循环)
- PostgreSQL中的函数使用技巧(postgresql函数)
- PostgreSQL:未来数据库发展的光明前景(postgresql前景)
- PostgreSQL:轻松入门的开源数据库(postgresql介绍)
- PostgreSQL的弊端:不可忽视的缺陷(postgresql缺点)
- 優化PostgreSQL 查詢最佳化之道(postgresql查询)
- 利用 PostgreSQL 脚本加速数据库操作!(postgresql脚本)
- 如何简单快速地安装PostgreSQL在Linux系统上(linux安装postgresql)
- 简易教程:Linux下如何快速安装PostgreSQL数据库(linux安装postgresql)
- PostgreSQL与Microsoft SQL Server之间的数据库比较(pg mssql 数据库)
- PostgreSQL 掌握精妙的数据类型(postgresql类型)
- 官方文档PostgreSQL:正式中文文档(postgresql中文)
- PostgreSQL查看数据库,索引,表,表空间大小的示例代码