【问题标题】:PREPARE TRANSACTION releasing locks?PREPARE TRANSACTION 释放锁?
【发布时间】:2013-06-13 16:41:21
【问题描述】:

我一定遗漏了一些关于 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 位)

EDIT 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

您将获得旧版本的记录。

【问题讨论】:

  • PostgreSQL 版本?你有没有机会使用一些旧版本?
  • @Graig-Ringer :我已经在 Windows 上更新了我的问题 => 9.2.2。

标签: postgresql transactions msdtc 2phase-commit


【解决方案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,那么它仍然可以。可串行化要求存在一个有效的顺序,其中并发事务可以连续发生以产生相同的结果。在这里,很明显:SELECT 先出现,然后是 UPDATE。当SELECT 发生时,这些事务仍然彼此同时发生,因为使用PREPARE TRANSACTION 准备的事务在提交或回滚之前仍处于打开状态。

【讨论】:

  • @Graig Ringer :更新块但不更新 SELECT。
  • @omatrot 请在您的问题中显示完整的测试用例,因为这里的测试结果都很好。更新后在这里评论,我会看看。
  • 我已经添加了完整的测试用例。我也理解了为什么这种行为是这样的:PostgreSQL 使用的 MVCC 锁定模型。我以前主要使用 MS Sql Server,所以这对我来说是新的。
  • @Graig-Ringer :你说得对,这与准备好的交易无关。当我说锁不见了时,这是不正确的。它们不再出现在锁服务器状态窗口中。
猜你喜欢
  • 1970-01-01
  • 2012-11-13
  • 2018-12-19
  • 2016-07-22
  • 2013-02-05
  • 1970-01-01
  • 1970-01-01
  • 2011-03-17
  • 1970-01-01
相关资源
最近更新 更多