PREPARE TRANSACTION释放锁?

时间:2013-06-13 16:41:22

标签: postgresql transactions msdtc 2phase-commit

我必须遗漏一些关于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

您将获得旧版本的记录。

1 个答案:

答案 0 :(得分:1)

我无法在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。因此,在您执行最终COMMIT PREPARED之前,PostgreSQL无法显示已更改的行,否则如果您读取行然后执行了ROLLBACK PREPARED,则会出现脏读异常。

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

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