当前位置: 首页 > postgresql, 死锁 > 正文

全局的 LOCK 结构体里有一个 grantMask,它记录了“当前这张表被所有人加上了哪些锁”。

既然全局已经有了状态指示灯,为什么在 PROCLOCK(人与物的契约)结构体内部,还要单独设计一个 holdMask(持有掩码) 字段?

我们继续用**“假设没有它会发生什么灾难”**的底层逻辑推演法,分 4 步把它在 C 语言级别的绝对物理作用推导出来!


第一步绝境:单进程多重加锁(锁升级的物理现实)

【工业级场景】

在真实的复杂业务中,一个业务进程(PGPROC_A)在一个长事务里,对同一张表往往不止加一次锁。

例如:

  1. 进程 A 先执行了一句 SELECT * FROM orders。此时,它向 orders 表申请了 1 级锁(AccessShareLock)
  2. 紧接着在同一个事务里,进程 A 又执行了一句 UPDATE orders SET ...。此时,它又向 orders 表申请了 3 级锁(RowExclusiveLock)

【物理推演】

在内存中,内核绝对不会为进程 A 和 orders 表生成两个 PROCLOCK 契约!

为了节省极其宝贵的共享内存,无论进程 A 对这张表加多少种不同级别的锁,它们之间永远只能共享那唯一的一个 PROCLOCK_A 结构体


第二步绝境:释放时的“物理失忆”(全局掩码的盲区)

【假设反推:如果没有 holdMask

假设全局的 LOCK 结构体的 grantMask 现在的二进制位是 00000101(表示这张表目前同时存在 1 级锁和 3 级锁)。

现在,进程 A 的事务结束了,它调用 COMMIT 准备释放资源。

内核顺着指针找到了 PROCLOCK_A,准备去修改全局的 grantMask

灾难发生了:

内核看着 PROCLOCK_A,脑子里一片空白。内核不知道:“进程 A 当初到底是只加了 1 级锁,还是只加了 3 级锁,还是两个都加了?”

如果不知道进程 A 到底持有什么锁,内核就绝对不敢去贸然扣减全局的 grantMask。如果扣错了,别的进程持有的锁状态就会被物理破坏,整个并发控制系统瞬间崩溃!


第三步破局:holdMask 的诞生(精准的私有账本)

为了解决这种“物理失忆”,架构师在 PROCLOCK 结构体内部,强制加入了一个极其轻量级的整数型字段:holdMask(当前进程对当前资源的私有持有掩码)

它的物理运转逻辑(位运算)如下:

  1. 第一次加锁:进程 A 申请 1 级锁成功。
    • CPU 动作:PROCLOCK_A -> holdMask |= 00000001(第 1 位标为 1)。
  2. 第二次加锁(锁升级):进程 A 申请 3 级锁成功。
    • CPU 动作:PROCLOCK_A -> holdMask |= 00000100(第 3 位也标为 1)。
    • 此时 holdMask 的物理状态00000101。这本“私有账本”极其精准地记录了:进程 A 当前在这个契约上,同时挂着 1 级和 3 级两把锁。

第四步终极推演:$O(1)$ 的安全释放与契约销毁

当事务 COMMIT,或者执行 ROLLBACK TO SAVEPOINT(回滚到某个保存点,需要释放部分锁)时,holdMask 展现出了它作为架构枢纽的绝对统治力。

  1. 精准扣减:内核读取 PROCLOCK_A -> holdMask,瞬间知道进程 A 手里攥着 1 级和 3 级锁。内核可以极其安全地去重新计算全局 LOCK 上的 grantMask,绝不会误伤其他并发进程。
  2. 契约的物理生命周期判定:如果进程 A 只是释放了 3 级锁,但还要保留 1 级锁。
    • CPU 动作:利用异或或按位与非运算,将 holdMask 里的第 3 位抹零(变回 00000001)。
    • 内核决断:只要 holdMask 的值不等于 0,说明进程 A 还有锁在这个表上,这个 PROCLOCK 结构体的内存就绝对不能 free()
    • 只有当 holdMask 被彻底扣减为 00000000 时,内核才确认:双方彻底银货两讫。此时,内核才会安全地执行 unlink,将 PROCLOCK_A 从双向链表中摘除,并物理销毁。

架构师闭环总结

在面试海量数据的内核级排障岗位时,你可以这样进行终极输出:

“在 PostgreSQL 的锁管理器中,全局 LOCK->grantMask所有人的汇总状态,用来进行 $O(1)$ 的全局冲突判定;

PROCLOCK->holdMask单个进程的私有台账,它记录了当前进程对当前资源累积持有的所有锁级别。它的核心作用,是在事务执行‘锁升级’、‘部分释放(Savepoint)’以及‘最终提交’时,提供绝对精准的本地状态追溯,从而指导内核安全地重算全局掩码,并决定当前 PROCLOCK 结构体是否应该被物理销毁。”

这种从“变量丢失会导致什么内存灾难”反向推导出来的底层逻辑,不仅严密,而且充满了工程美感。


了解 www.876873.xyz 的更多信息

订阅后即可通过电子邮件收到最新文章。

逻辑推导出 holdMask 字段 的作用:等您坐沙发呢!

发表评论