2

我一定遗漏了一些关于 PostgreSQL 和 PREPARE TRANSACTION 的两阶段提交的信息。

以下 SQL :

BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE person.tcities set ctyname='L ABERGEMENT CLEMENCIAT TRANSACT' WHERE ctyid = 1

给出以下锁:

4092    Private 329373  acc     15/53295    RowExclusiveLock    Oui 2013-06-13 18:15:55+02  UPDATE person.tcities set ctyname='L ABERGEMENT CLEMENCIAT TRANSACT' WHERE ctyid = 1    
4092    Private 329369  acc     15/53295    RowExclusiveLock    Oui 2013-06-13 18:15:55+02  UPDATE person.tcities set ctyname='L ABERGEMENT CLEMENCIAT TRANSACT' WHERE ctyid = 1    
4092    Private 328704  acc     15/53295    RowExclusiveLock    Oui 2013-06-13 18:15:55+02  UPDATE person.tcities set ctyname='L ABERGEMENT CLEMENCIAT TRANSACT' WHERE ctyid = 1    
4092    Private 327169  acc     15/53295    RowExclusiveLock    Oui 2013-06-13 18:15:55+02  UPDATE person.tcities set ctyname='L ABERGEMENT CLEMENCIAT TRANSACT' WHERE ctyid = 1    
4092            acc 15/53295    15/53295    ExclusiveLock   Oui 2013-06-13 18:15:55+02  UPDATE person.tcities set ctyname='L ABERGEMENT CLEMENCIAT TRANSACT' WHERE ctyid = 1    
4092    Private 329377  acc     15/53295    RowExclusiveLock    Oui 2013-06-13 18:15:55+02  UPDATE person.tcities set ctyname='L ABERGEMENT CLEMENCIAT TRANSACT' WHERE ctyid = 1    
4092            acc     15/53295    ExclusiveLock   Oui 2013-06-13 18:15:55+02  UPDATE person.tcities set ctyname='L ABERGEMENT CLEMENCIAT TRANSACT' WHERE ctyid = 1

一旦交易准备好:

PREPARE TRANSACTION 'TEST'

锁不见了。

因为在 PREPARE 和 COMMIT 之间发生了小的延迟,另一个查询可能会获得旧版本的记录。

是否有配置设置来避免这种行为,还是设计使然?

提前致谢。

编辑:我在 Windows x64 上使用 PostgreSQL 9.2.2(PostgreSQL 9.2.2,由 Visual C++ build 1600 编译,64 位)

编辑 2:以下是完整的测试用例:

在新会话中发出以下内容:

BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE person.tcities set ctyname='L ABERGEMENT CLEMENCIAT TRANSACT' WHERE ctyid = 1
PREPARE TRANSACTION 'TEST';

然后在另一个新会话中:

SELECT * FROM person.tcities

您将获得旧版本的记录。

4

1 回答 1

2

我无法在 PostgreSQL 9.2 上重现所描述的行为:

 CREATE TABLE prep_test AS SELECT generate_series(1,10) AS x;

 BEGIN;
 UPDATE prep_test SET x = x*10;
 PREPARE TRANSACTION 'test';

然后在第二个会话中:

 UPDATE prep_test SET x = x*20;

块,正如预期的那样,直到我COMMIT PREPARED 'test'或者ROLLBACK PREPARED 'test'

在测试可序列化隔离时,我也得到了预期的结果:

A# BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
B# BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
A# INSERT INTO prep_test(x) VALUES (42);
B# INSERT INTO prep_test(x) VALUES (43);
A# SELECT count(x) FROM prep_test;
B# SELECT count(x) FROM prep_test;
A# PREPARE TRANSACTION 'test';
B# COMMIT;

B完全按照预期失败并出现可序列化错误。如果 B 也尝试这样做,PREPARE TRANSACTION也会发生同样的情况。

用测试用例更新问题后

您的测试用例对我来说看起来不错,即它的行为应该与描述的完全一样,并且执行时不会出错。

PREPARE TRANSACTION不是承诺。你还可以ROLLBACK PREPARED。所以 PostgreSQL 在你执行 final 之前无法显示更改的行COMMIT PREPARED,否则如果你读取行然后执行了ROLLBACK PREPARED.

如果您在运行第二个命令之前没有PREPARE TRANSACTION在第一个会话中进行测试,那么您将从测试案例中获得相同的结果。它与准备好的交易无关。您只是没有看到未提交的事务更改了行,这是完全正常的。

如果两个交易都是,SERIALIZABLE那么它仍然可以。可串行化要求有一个有效的顺序,其中并发事务可以连续发生以产生相同的结果。在这里,这很明显:SELECT先出现,然后是UPDATE. 当事务发生时,事务仍然彼此同时发生,SELECT因为准备好的事务PREPARE TRANSACTION在提交或回滚之前仍然处于打开状态。

于 2013-06-14T00:11:36.840 回答