这是一次深入到 PostgreSQL/openGauss 内核最深处的探险!
我们要彻底抛弃应用层“加锁成功/失败”这种模糊的宏观概念。现在,我们把时间的流速放慢到 CPU 时钟周期级别(纳秒级),用“慢动作逐帧解析”的方式,盯死这个 LOCK 结构体里的 7 个部件,看看当一句简单的 UPDATE table_A SET ...(申请第 3 级行排他锁)冲进共享内存时,这台极其精密的“物理锁引擎”是如何进行咬合运转的!
第一帧:雷达锁定与寻址(tag 的绝对指引)
【时间点】:SQL 刚被解析完,执行引擎准备下潜到磁盘。
【参战部件】:LOCKTAG tag
- 引擎动作:执行引擎绝对不会去遍历全库几万张表。它直接提取
table_A的物理 OID(例如16384),以及所在数据库的 OID,组合拼装成一个几十字节的二进制坐标(tag)。 - 哈希直达:CPU 将这个坐标扔进哈希函数,瞬间计算出内存偏移量,直接降落到共享内存
LockMethodLockHash哈希表中的某一个具体槽位。 - 核对身份:CPU 扒开槽位里的
LOCK结构体,比对里面的tag字段。如果完全吻合,目标锁定完毕。时间复杂度 $O(1)$。
第二帧:生命线挂载(nRequested 的防回收机制)
【时间点】:刚找到 LOCK 结构体,还没开始做冲突判定。
【参战部件】:int nRequested(申请引用计数)
- 引擎动作:在进行任何实质性操作前,CPU 强制将该
LOCK结构体内的nRequested计数器加 1。 - 配合原理:为什么要先做这一步?因为内存是没有自动垃圾回收的。如果在这纳秒级的间隙里,刚好持有锁的另一个人释放了锁,内核清理程序如果看到这张表没人碰,可能会直接把这个
LOCK结构体从内存里pfree()销毁掉! - 物理意义:
nRequested + 1就相当于在这张表上挂了一根“安全绳”。它向整个操作系统宣告:“我现在正在处理这张表的逻辑,谁也不准把这个内存结构体回收掉!”
第三帧:纳秒级的生死判决(waitMask 与 grantMask 的防线)
【时间点】:安全绳挂好,开始申请第 3 级锁。
【参战部件】:LOCKMASK waitMask、LOCKMASK grantMask
这是整个运转中最核心的 CPU 位运算防线!判决分两步走:
- 第一关:防饿死探测(看
waitMask)- CPU 先看一眼门外的预警雷达
waitMask。如果此时门外刚好有个 DBA 在排队等 8 级强锁(waitMask的第 8 位是1)。 - 配合动作:哪怕当前的
UPDATE(3 级)跟表里正在运行的读请求完全兼容,系统也会强制亮红灯!因为强锁具有更高优先级,内核绝对不许 3 级锁“插队”,直接强制UPDATE去排队。
- CPU 先看一眼门外的预警雷达
- 第二关:绝对冲突比对(看
grantMask)- 假设门外没强锁排队,雷达安全。CPU 提取当前结构体的
grantMask(假设当前有人在做 1 级SELECT纯读,所以第 1 位是1,即00000001)。 - CPU 取出 3 级锁的冲突规则掩码,与
grantMask做按位与运算(&)。 - 物理计算:由于 1 级和 3 级在底层逻辑上互不干扰,位运算结果为
0。绿灯放行!
- 假设门外没强锁排队,雷达安全。CPU 提取当前结构体的
第四帧(绿灯分支):缔结契约与放行(procLocks 与 nGranted)
【时间点】:判决通过,准备修改物理状态。
【参战部件】:LOCKMASK grantMask、int nGranted、SHM_QUEUE procLocks
- 状态叠加:CPU 将
grantMask再次进行按位或运算(|),把自己的第 3 位给拨成1。此时grantMask变成了00000101(向全宇宙宣告:这张表现在既有人读,也有人写)。 - 登记造册(极其关键):
- 内存管理器瞬间
malloc出一个代表当前进程与该表关系的PROCLOCK契约结构体。 - 将这个契约,强行挂入当前
LOCK结构体底下的procLocks双向链表中。
- 内存管理器瞬间
- 授权计数:CPU 将
nGranted计数器加 1。 - 长驱直入:执行引擎彻底脱离锁管理器的拦截,通电下潜,直奔底层的物理数据块去修改那一行数据的
xmax。
第四帧(红灯分支):无情镇压与休眠(waitProcs)
【时间点】:如果第三帧的判决没通过(比如表上正在做 5 级 CREATE INDEX,位运算冲突)。
【参战部件】:LOCKMASK waitMask、PROC_QUEUE waitProcs
- 拉响警报:CPU 立刻修改预警雷达
waitMask,把第 3 个比特位拨成1(警告后面的人:现在有个写操作正在门外排队)。 - 打入冷宫:
- 同样会生成一个
PROCLOCK契约挂进procLocks,但状态标记为“未授权(等待中)”。 - 终极镇压:CPU 提取当前
UPDATE业务的进程实体指针(PGPROC),将其直接塞进LOCK结构体内部最底层的waitProcs休眠队列尾部。
- 同样会生成一个
- 物理断电:调用系统级
SemSleep信号量,该 CPU 核心算力被剥夺,业务挂起,开始计算死锁超时(deadlock_timeout)。
架构级复盘:部件之间的完美齿轮咬合
把这套慢动作连起来看,你会发现这 7 个部件根本不是孤立的变量,它们是一条流水线上环环相扣的物理齿轮:
- 防丢失齿轮:
nRequested在最外围挡着,保证整套设备在运转时不会被操作系统当垃圾回收掉。 - 极速判定齿轮:
waitMask防止高优先级被饿死,grantMask负责用纳秒级位运算拦截冲突请求。 - 排障与审计齿轮:所有的请求(无论成败)最终都必须在
procLocks链表里留下契约痕迹,这是供 DBA 排查阻塞和内核检测死锁的绝对证据。 - 系统级调度齿轮:真正起冲突时,
waitProcs负责把活生生的进程指针送进操作系统的休眠系统。 - 生命终结齿轮:当最后一个事务执行释放动作,
nRequested和nGranted同时清零,这台齿轮机器瞬间解体,释放宝贵的共享内存。
这就是工业级数据库锁引擎的“物理机械图”。每一行 C 语言代码,每一个位运算,都是在为压榨多核 CPU 算力和节省极度昂贵的共享内存而服务。
了解 www.876873.xyz 的更多信息
订阅后即可通过电子邮件收到最新文章。
慢动作拆解: 一个sql语句在获取表A的锁,这个过程中 lock结构体各个部件在干嘛?? 各个部件之间是如何配合工作的??:等您坐沙发呢!