我试图更好地理解 MVCC 并且对以下(人为的)场景感到困惑。我的印象是,要使一行对事务可见,事务的 id 必须大于元组的 xmin 值。
create table test (id int);
(session 1)
begin;
select txid_current();
┌──────────────┐
│ txid_current │
├──────────────┤
│ 704 │
└──────────────┘
(session 2)
begin;
insert into test values (1);
commit;
(session 1)
select xmin, xmax, id from test;
┌──────┬──────┬────┐
│ xmin │ xmax │ id │
├──────┼──────┼────┤
│ 705 │ 0 │ 1 │
└──────┴──────┴────┘
id 为 704 的事务如何看到 xmin 为 705 的元组?
更新:
阅读http://www.interdb.jp/pg/pgsql05.html对我来说帮助澄清了很多事情。我之前看过的一些视频似乎暗示,作为一个硬性规则,一个元组只有在其 xmin 小于当前交易的 txid 时才可见。
然而,上面的阅读为我描绘了一幅略有不同的画面。在 READ COMMITTED 隔离级别中,事务在每个查询开始时获取其他事务状态的新快照。
会话 1 中的第一个查询将获得 704:704 的快照:(您可以通过运行看到select txid_current_snapshot()
)这意味着从 txid 704 的角度来看,所有 txids < 704 都是不活动的(提交或中止)并且所有事务 > = 704 个处于活动状态(进行中或尚未开始)。
会话 1 的第二个查询将获得 704:706 的新快照:这意味着从 txid 704 的角度来看,所有 txids < 704 都处于非活动状态,并且所有 txids >= 706 都处于活动状态。然而,由于 txid 705 介于这些值之间,并且它实际上已经提交,它也是 txid 704 收到的新快照的一部分,并且是可见的。因此,一个元组只有在其 xmin 小于当前交易的 txid 时才可见并不是硬性规定。
这取决于隔离级别。postgresql 的默认级别是“Read Committed”,在该级别可能会发生这种情况。在 postgresql 中,它不会发生在“可重复读取”或“可序列化”时。
这种行为通常不是问题,较高的序列化级别会降低服务器速度,这就是它们不是默认值的原因。
有关详细说明,请参阅手册https://www.postgresql.org/docs/9.6/static/transaction-iso.html
在会话 1 中,您
select txid_current();
的已过时。在会话 2 完成插入后重复该查询,您会发现它返回一个不同的值,该值大于 705。如果要将 txid_current() 锁定到位,则必须在隔离级别设置为至少可重复读取的事务中。否则,它将在每个语句之间前进(给定数据库中的其他提交活动)。