当前位置:网站首页>Postgresql源码(68)virtualxid锁的原理和应用场景
Postgresql源码(68)virtualxid锁的原理和应用场景
2022-08-09 21:55:00 【高铭杰】
相关:
《Postgresql源码(40)Latch的原理分析和应用场景》
《Postgresql源码(67)LWLock锁的内存结构与初始化》
《Postgresql源码(68)virtualxid锁的原理和应用场景》
0 总结速查
- 事务的
vxid.localTransactionId
并不会变,只在begin时申请一次,也就是一个事务共享一个localTransactionId
。事务内的fpVXIDLock
一直是true状态,直到换成LWLock。 fpVXIDLock
是fast-path锁,真正需要等锁时会把fpVXIDLock
换成LWLock
去等锁,换锁&加锁逻辑在VirtualXactLock
函数中封装好了。- 在查看锁表时,每个会话都会有申请vxid,在事务启动时申请一次。
- 在查看锁表时,如果发现vxid锁未granted,他等待的就是virtualtransaction列的vxid,或者用pg_blocking_pid也可以查询在等谁。
1 前言
我们在空载数据库上查询pg_locks:
- 第一行很明显是需要查询pg_locks视图,所以加了AccessShareLock锁。
- 第二行就是本文要讨论的重点,为什么PG每次查询都要加一个virtualxid锁,virtualxid是在哪里加的?
2 virtualxid加锁位置
回到vxid的获取位置:
#0 VirtualXactLockTableInsert (vxid=...) at lock.c:4477
#1 0x0000000000583157 in StartTransaction () at xact.c:2019
#2 0x0000000000583f43 in StartTransactionCommand () at xact.c:2870
#3 0x0000000000978c12 in start_xact_command () at postgres.c:2689
#4 0x0000000000976393 in exec_simple_query (query_string=0x2d65500 "select 1;") at postgres.c:989
#5 0x000000000097acbd in PostgresMain (a) at postgres.c:4494
#6 0x00000000008b6eb2 in BackendRun (port=0x2d86a60) at postmaster.c:4530
#7 0x00000000008b6831 in BackendStartup (port=0x2d86a60) at postmaster.c:4252
#8 0x00000000008b2ca9 in ServerLoop () at postmaster.c:1745
#9 0x00000000008b257b in PostmasterMain (argc=1, argv=0x2d5f0e0) at postmaster.c:1417
#10 0x00000000007b4df7 in main (argc=1, argv=0x2d5f0e0) at main.c:209
在函数VirtualXactLockTableInsert中:
void
VirtualXactLockTableInsert(VirtualTransactionId vxid)
{
//【1】
LWLockAcquire(&MyProc->fpInfoLock, LW_EXCLUSIVE);
//【2】
MyProc->fpVXIDLock = true;
//【3】
MyProc->fpLocalTransactionId = vxid.localTransactionId;
//【4】
LWLockRelease(&MyProc->fpInfoLock);
//【5】
}
在位置【1】查询pg_locks的结果:为空
在位置【2】、【3】、【4】查询
pg_locks
是会卡住(pg_locks
视图对应pg_lock_status()
函数,函数会遍历PGPROC拿fpInfoLock,所以会发生等锁);在位置【5】查询
pg_locks
:出现virtualxid排他锁
所以我们看到的virtualxid排他锁,其实是MyProc->fpVXIDLock = true
的结果,注意没有LWLock。
2 virtualxid的组成
typedef struct
{
BackendId backendId; /* backendId from PGPROC */
LocalTransactionId localTransactionId; /* lxid from PGPROC */
} VirtualTransactionId;
从上面分析来看,virtualxid加锁的同时也更新了MyProc->fpLocalTransactionId
的值,这个值记录的是本地事务ID:LocalTransactionId,直观上可以理解为当前会话的命令ID,随着命令执行+1递增。
LocalTransactionId的获取位置:启动任意一个会话后,后台私有内存会创建一个本地事务ID计数器:
LocalTransactionId
GetNextLocalTransactionId(void)
{
LocalTransactionId result;
/* loop to avoid returning InvalidLocalTransactionId at wraparound */
do
{
result = nextLocalTransactionId++;
} while (!LocalTransactionIdIsValid(result));
return result;
}
每次本地会话启动一个事务,都会在StartTransaction
中调用GetNextLocalTransactionId
拿到一个本地事务ID,并将nextLocalTransactionId++
。
注意:
- 事务的
vxid.localTransactionId
并不会变,只在begin时申请一次!也就是一个事务共享一个localTransactionId
。 - 事务内的
fpVXIDLock
一直是true状态。
3 virtualxid没有lwlock如何发生锁竞争?
例如创建索引场景,需要等待表上的写事务结束.
场景:
- session1删除表一半数据,
vxid={3,21}
,事务未提交。 - session2创建索引卡住,等待session事务结束。
session2等锁发生过程分析:
【1】找到需要等待的会话的vxid={3,21}
(gdb) p *lockholders
$8 = {backendId = 3, localTransactionId = 21}
【2】调用VirtualXactLock(*lockholders, true)
等待vxid={3,21}
,现在锁表中可以看到:建索引本身的vxid4/46
,建索引需要等老事务结束,所以用vxid等另外一个会话结束,可以看到最后一行在请求别人的vxid3/21
。
【3】VirtualXactLock
函数进入后,找到vxid={3,21}
的PROC,将假锁proc->fpVXIDLock
换成真锁lwlock
。(假锁 无法使用LWLock机制等锁)
if (proc->fpVXIDLock)
{
PROCLOCK *proclock;
uint32 hashcode;
LWLock *partitionLock;
hashcode = LockTagHashCode(&tag);
partitionLock = LockHashPartitionLock(hashcode);
LWLockAcquire(partitionLock, LW_EXCLUSIVE);
proclock = SetupLockInTable(LockMethods[DEFAULT_LOCKMETHOD], proc,
&tag, hashcode, ExclusiveLock);
...
GrantLock(proclock->tag.myLock, proclock, ExclusiveLock);
LWLockRelease(partitionLock);
proc->fpVXIDLock = false;
}
- 如果
proc->fpVXIDLock == true
,用vxid = {3,21}
申请tag = {locktag_field1 = 3, locktag_field2 = 21}
。 - 【SetupLockInTable】在共享内存索引哈希表(LockMethodLockHash)中查询tag是否存在,如果没有创建出来。
- 【SetupLockInTable】查到了直接用传入的ExclusiveLock把LWLock锁上。
- 释放假锁
proc->fpVXIDLock == true
,现在锁表中已经有一把tag = {locktag_field1 = 3, locktag_field2 = 21}
的LWLock了,不再需要fastpath锁fpVXIDLock。
【4】开始等3/21结束
/* Time to wait. */
(void) LockAcquire(&tag, ShareLock, false, false);
LockRelease(&tag, ShareLock, false);
4 virtualxid应用场景
目前只发现这两处用法:
- 除了上述创建索引的场景,并行创建索引的第三阶段也用到了
VirtualXactLock
来等待老快照的结束。 - 热备读写冲突时,如果默认超时30秒,会把读会话kill掉继续做日志,这个kill的机制也是用vxid来实现的。
边栏推荐
- leetcode 38. 外观数列
- 【EF】 更新条目时出错。有关详细信息,请参见内部异常。[通俗易懂]
- “稚晖君”为2022昇腾AI创新大赛打call 期待广大开发者加入
- 你的 Link Button 能让用户选择新页面打开吗?
- openGauss数据库基本操作(超详细)
- ACM MM 2022 | Cloud2Sketch: Painting with clouds in the sky, AI brush strokes
- BulkInsert方法实现批量导入
- 每日一R「02」所有权与 Move 语义
- SecureCRT background color
- 【微服务~Nacos】Nacos之配置中心
猜你喜欢
开发者必备:一文快速熟记【数据库系统】和【软件开发模型】常用知识点
五星控股汪建国:以“植物精神”深耕赛道,用“动物精神”推动成长
你真的了解乐观锁和悲观锁吗?
2.1.5 大纲显示问题
Blender程序化建模简明教程【PCG】
ACM MM 2022 | Cloud2Sketch: 长空云作画,AI笔生花
Space not freed after TRUNCATE table
xctf攻防世界 Web高手进阶区 shrine
AI Knows Everything: Building and Deploying a Sign Language Recognition System from Zero
4D Summary: 38 Knowledge Points of Distributed Systems
随机推荐
random.normal() and random.truncated_normal() in TF
SQLi-LABS Page-2 (Adv Injections)
阿里云架构师金云龙:基于云XR平台的视觉计算应用部署
为什么这么多人都想当产品经理?
级联下拉菜单的实现「建议收藏」
TF generates uniformly distributed tensor
String hashing (2014 SERC J question)
阿里云架构师金云龙:基于云XR平台的视觉计算应用部署
Converting angles to radians
Pagoda measurement - building LightPicture open source map bed system
In programming languages, the difference between remainder and modulo
How do task flow executors work?
One Pass 2074: [21CSPJ Popularization Group] Candy
abstract class or interface
发送激活邮件「建议收藏」
17-GuliMall 搭建虚拟域名访问环境
NIO Cup 2022 Nioke Summer Multi-School Training Camp 7 CFGJ
Jinshanyun earthquake, the epicenter is in bytes?
[Implementation of the interface for adding, deleting, checking, and modifying a double-linked list]
【微服务~Nacos】Nacos服务提供者和服务消费者