我必须遗漏一些关于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
您将获得旧版本的记录。
答案 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
准备的事务在提交或回滚之前仍然处于打开状态。