You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL Repeatable Read隔离级别下SELECT...FOR UPDATE意外获取AccessExclusiveLock的疑问

我来一步步帮你拆解这个问题里的两个核心疑问:

一、关于意外出现的AccessExclusiveLock

首先得澄清一个关键知识点:PostgreSQL里根本不存在tuple级别的AccessExclusiveLock——这种锁模式是专门针对表级别的(对应locktype = 'relation')。所以你看到的那条奇怪记录,大概率是记录查询结果时搞混了列的顺序:比如locktype实际是relation,而tuple列本来是空值(表级锁不会填充tuple和page字段),你误把其他列的值填到这里了。

接下来排查这个表级AccessExclusiveLock的来源:

  • 先查有没有“隐形”的DDL操作:打开一个新会话,执行SELECT pid, query FROM pg_stat_activity WHERE datname = 'mydb' AND query LIKE '%Users%';,看看除了你这两个事务外,有没有其他会话在跑ALTER TABLE、VACUUM FULL、TRUNCATE这类会触发AccessExclusiveLock的命令。很多时候这种锁都是其他进程偷偷加的,自己没注意到。
  • 再检查当前事务有没有隐式触发DDL:比如你的Users表上有没有特殊的触发器、自定义函数?不过PostgreSQL里触发器里根本没法执行DDL(执行DDL会直接提交事务,和你当前的事务逻辑冲突),所以这个可能性极低。
  • 最后确认自己有没有误操作:比如在事务开始后,是不是不小心敲了LOCK TABLE "Users";?这个命令默认加的就是AccessExclusiveLock,如果没注意到的话,就会出现你看到的情况。

另外,你更新的查询结果里说被阻塞的查询是AccessExclusiveLock TRUE,这也不对劲——被阻塞的会话应该是granted = FALSE,只有持有锁的会话才会是TRUE。你可以再核对下pid对应的会话:那个持有锁的pid=199957的会话,它的实际查询是什么?如果确实只执行了你说的SELECT ... FOR UPDATE和UPDATE,那要么是你看错了结果,要么是某个旧版本PostgreSQL的bug(建议核对下你的PG版本)。

二、关于tuple锁的含义

当你执行SELECT ... FOR UPDATE时,PostgreSQL会给选中的行加行级锁,对应的locktype = 'tuple':

  • page:是这个行所在的数据页面编号(这里是0,说明是表的第一个数据页面)。
  • tuple:是这个行在页面里的偏移编号(这里是9)——PostgreSQL里页面内的元组编号是从1开始的,所以这个就是你锁定的id=1的用户对应的具体数据行位置。
  • 正常来说,这个tuple锁的模式应该是RowExclusiveLock(这是SELECT ... FOR UPDATE和UPDATE默认的行级锁模式),这和你看到的行锁记录是完全符合预期的。

内容的提问来源于stack exchange,提问作者Pablo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 09:22:36