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

要回答这个问题,我们必须在物理层面上,把“哈希表本身(容器)”和“哈希表里的结构体(数据)”彻底切分开来。

直接给出精确结论:

作为“容器”的两张全局哈希表,是 100% 同时生成的;但里面用来装数据的 LOCKPROCLOCK 结构体,存在着极其严格的“先后物理因果顺序”,绝对不是同时生成的!

我们按数据库的生命周期,分宏观和微观两个维度,把这个内存初始化的逻辑推导出来:


第一维度:宏观层面(数据库实例启动时的“天地初开”)

【物理动作】:容器的同时诞生

当你敲下 pg_ctl start(或 openGauss 的 gs_om -t start),主进程(Postmaster)在向操作系统申请一大块**共享内存(Shared Memory)**时,会执行一个底层的 C 语言函数:InitLocks()

  1. 预分配地盘:在这个函数里,内核会根据你在 postgresql.conf 里配置的 max_locks_per_transactionmax_connections 参数,计算出一个总内存大小。
  2. 画好网格:内核在共享内存里,同时划分出两片巨大的空白网格区域。
    • 一片命名为 LockMethodLockHash(资源哈希表)。
    • 一片命名为 LockMethodProcLockHash(关系/契约哈希表)。
  3. 推导结论:在数据库刚刚启动、没有任何业务 SQL 连进来的时候,这两张“表”就已经并排躺在内存里了。此时,它们是两个巨大的、绝对空层的哈希数组,里面一个 LOCKPROCLOCK 结构体都没有。

第二维度:微观层面(业务并发涌入时的“严格因果顺序”)

现在,业务进程(PGPROC_1)杀进来了,准备对表 orders 申请锁。这时候,结构体的生成就进入了极其严密的**“先造神像,再造信徒”**的物理时序。

绝对不可能同时生成!必须严格遵循以下两步推演:

【第 1 步:先找/生成 LOCK(造神像)】

  • 执行引擎拿着表 OID,首先冲向 LockMethodLockHash(资源哈希表)。
  • 如果这张表是全库第一个被访问的,内核会在这个哈希表里 malloc 分配出一个全新的 LOCK 结构体
  • 架构师物理推导:为什么必须先造它?因为在计算机科学的指针逻辑里,“实体(物)”必须先于“关系”存在。 如果实体不存在,后面生成的契约指针连内存地址都找不到,会直接引发臭名昭著的“空指针异常(Null Pointer Exception)”,导致整个数据库 Coredump 崩溃。

【第 2 步:再找/生成 PROCLOCK(造信徒)】

  • 当确认 LOCK 结构体已经在内存中稳稳落脚后。
  • 进程 紧接着(哪怕只隔了几个 CPU 时钟周期) 拿着自己的进程指针和刚刚那个 LOCK 的指针,冲向 LockMethodProcLockHash(关系哈希表)。
  • 内核在里面分配出一个全新的 PROCLOCK 结构体
  • 内核将 PROCLOCK 内部的 myLock 指针,死死绑在刚刚第一步生成的 LOCK 结构体的内存地址上。

架构级总结(防内存泄漏的终极闭环)

内核之所以把生成顺序规定得死死的,不仅是为了加锁,更是为了释放锁(垃圾回收)时的绝对安全

当事务 COMMIT,系统开始大扫除时,销毁顺序是完全反过来的:

  1. 先撕契约:先沿着进程链表,把 PROCLOCK 结构体从关系哈希表中抹除销毁。
  2. 再看神像:然后去检查那个 LOCK 结构体。如果发现它底下的 procLocks 链表已经空了(说明全库没有任何人再碰这张表了)。
  3. 销毁实体:为了节省宝贵的共享内存,内核会顺手把这个孤零零的 LOCK 结构体也从资源哈希表中抹除释放。

如果两者是同时生成、同时销毁的,在多核 CPU 的高并发争抢下,极易出现“契约还在,但物理表结构体已经被别人提前释放”的悬空指针惨剧。

所以,回答面试官的核心话术是:“哈希表容器是在实例启动时同步预分配的;但在高并发加锁时,内部条目的生成是极其严格的串行逻辑——先寻址或创建 LOCK 资源实体,再动态创建并绑定 PROCLOCK 关系契约。一切为了指针的安全与内存的不泄漏。”


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

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

资源哈希表 和 关系哈希表 是同时生成的吗??:等您坐沙发呢!

发表评论