PostgreSQL表中未删除行的xmax系统列为何不为零?
其实呀,PostgreSQL的xmax系统列可不是只用来标记行被删除的事务ID,它还有个重要职责:记录锁定该行的事务ID。所以即使行没被删除,只要满足某些场景,xmax就会是非零值,下面给你拆解几个常见情况:
行级锁被持有:如果有事务通过
SELECT ... FOR UPDATE、SELECT ... FOR SHARE这类语句锁定了某行,持有锁的事务ID就会被写入该行的xmax。比如你打开一个事务执行:BEGIN; SELECT * FROM product WHERE id=1 FOR UPDATE;这时候在另一个会话查询
product表的xmax,就能看到对应行的xmax是这个未提交事务的ID,直到该事务提交或回滚、锁释放后,xmax才会恢复为0。未提交的删除/更新事务:如果有个事务执行了删除或更新该行的操作,但还没提交,在当前事务的快照里这行依然可见,但
xmax已经被设置为那个操作事务的ID了。只有当该事务提交后,新的快照才会看不到这行;如果事务回滚,xmax会被标记为无效,后续查询里行还是正常可见的。外键约束的隐性锁:你的
product表被stock表的外键引用,当对stock表做插入、更新product_id这类操作时,PostgreSQL会自动对product表的对应行加共享锁,防止在检查外键有效性期间该行被删除,这时候xmax也会记录下加锁的事务ID。比如插入一条stock记录时,数据库要确保对应的product存在,就会临时锁住那行,锁释放后xmax才会归零。
简单来说,xmax的作用是标记行的“删除者”或“锁定者”,不是只有删除才会让它非零。你可以先检查下有没有未提交的事务,或者近期有没有执行过行锁相关的操作,大概率能找到原因~
内容的提问来源于stack exchange,提问作者Mangu Singh Rajpurohit

