Postgres 9.5:作为外键引用的行的非零 xmax

Postgres 9.5: Non-zero xmax of a row refenced as a foreign key

根据http://www.postgresql.org/docs/current/static/ddl-system-columns.html

xmax - The identity (transaction ID) of the deleting transaction, or zero for an undeleted row version. It is possible for this column to be nonzero in a visible row version. That usually indicates that the deleting transaction hasn't committed yet, or that an attempted deletion was rolled back.

但是如果一行被另一行作为外键引用,它也有一个非零的xmax值:

drop table if exists demo;
create table demo
(
    id bigint primary key not null,
    pid bigint,
    constraint demo_pid_fk
        foreign key (pid)
    references demo (id)
);

insert into demo(id, pid) values(1, NULL); 
insert into demo(id, pid) values(2, 1); 
insert into demo(id, pid) values(3, 3); 
select xmin, xmax, * from demo;
 xmin | xmax | id | pid 
------+------+----+-----
 1074 | 1075 |  1 |    
 1075 |    0 |  2 |   1
 1076 | 1076 |  3 |   3
(3 rows)

insert into demo(id, pid) values(4, 1); 
select xmin, xmax, * from demo;
 xmin | xmax | id | pid 
------+------+----+-----
 1074 | 1077 |  1 |    
 1075 |    0 |  2 |   1
 1076 | 1076 |  3 |   3
 1077 |    0 |  4 |   1
(4 rows)

在上面的示例中,xmax 似乎设置为引用该行作为外键的最后一个事务的 id。为什么会这样? Postgres 如何知道该行仍然存在?

MVCC, unveiling xmin and xmax work (slides 上有 Bruce Momkian 的演讲,搜索 "Row Locks Using Xmax")。

长话短说:xmax 也用于行锁(文档中未详细介绍),在这种特定情况下,xmax 用于确保其他事务不会修改引用的元组(因此 CASCADE 将等待,例如),除非 txid_current() 大于插入的 xmax。实际上,table 中的那些非零 xmax 没有用,并被 VACUUM FULL demo 清除。