我一定错过了一些关于 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:以下是完整的测试用例:
在新 session 中发出以下内容:
BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE person.tcities set ctyname='L ABERGEMENT CLEMENCIAT TRANSACT' WHERE ctyid = 1
PREPARE TRANSACTION 'TEST';
然后在另一个新 session 中:
SELECT * FROM person.tcities
您将获得旧版本的记录。
最佳答案
我无法在 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';
然后在第二个 session 中:
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
,则会出现脏读异常>.
如果您在运行第二个命令之前没有在第一个 session 中PREPARE TRANSACTION
,您将从测试用例中得到相同的结果。它与准备好的交易无关。您只是没有看到未提交事务更改的行,这是完全正常的。
如果两个事务都是SERIALIZABLE
,那么它仍然没问题。可串行性要求存在有效的顺序,其中并发事务可以连续发生以产生相同的结果。在这里,这是显而易见的:SELECT
首先出现,然后是 UPDATE
。当 SELECT
发生时,事务仍然彼此并发发生,因为使用 PREPARE TRANSACTION
准备的事务在提交或回滚之前仍然处于打开状态.
关于postgresql - PREPARE TRANSACTION 释放锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17092561/