【问题标题】:How to prevent phantom reads in Postgres?如何防止 Postgres 中的幻读?
【发布时间】:2016-04-03 18:44:58
【问题描述】:

是否可以防止幻读,或者锁定 Postgres 事务中丢失的行?例如,考虑以下命令序列:

在连接 1 上:

CREATE TABLE weather ( city varchar(80) PRIMARY KEY );
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
INSERT INTO weather VALUES ('a');

同时,在连接 2 上:

BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT * FROM weather WHERE city = 'a' FOR SHARE;
INSERT INTO weather VALUES ('b');

然后回到连接 1:

COMMIT;

再次回到连接 2:

COMMIT;
SELECT * FROM weather;
-- Shows both rows

连接 2 上的事务似乎不可能成功,因为创建“b”行的前提条件取决于“a”行的缺失。如何防止第二笔交易成功?

【问题讨论】:

  • psql (PostgreSQL) 9.3.11
  • 可序列化事务只要行为与这些事务的 some 串行顺序一致就可以成功,但这种排序与这些事务的顺序无关已启动或已提交。在这种情况下,如果您先运行事务 2,然后运行事务 1,您似乎会得到相同的结果,所以我看不出有任何冲突的原因。
  • 确实如此,尽管它并没有让我更接近问题的答案。如何在不锁定整个数据库或表的情况下进行原子读写?
  • 当您说“幻读”时,您是指SELECT * FROM weather 看到行'a' 的事实吗?如果它们不在同一个事务块中,则无法保证此 SELECT 与之前的 SELECT 之间的一致性。
  • 就像我说的,提交的实际顺序是无关紧要的。数据库“假装”#2 首先发生,并且它能够这样做是因为没有会话观察到任何与此事件序列不一致的行为。如果您在两次提交之间有第三个可序列化事务运行SELECT * FROM weather,它将看到a,而不是b;这将排除 #2 先出现的可能性,并且 #2 在提交时会失败。

标签: postgresql transactions


【解决方案1】:

不锁定整个表的一种方法是使用 Postgresql 的 Advisory lock [1] 机制:

-- tx 1
begin;
select pg_advisory_lock(1234);
insert/update....
commit;

-- tx 2
begin;
select pg_advisory_lock(1234);
SELECT * FROM weather WHERE city = 'a' FOR SHARE;
insert/update...
commit;

通过这种方式,您可以进行事务间通信,而正常的 MVCC 行为是不够的。在示例中,1234 是在应用程序级别具有含义的任意整数。另请参阅 [2] 了解更多使用建议锁的方法。

[1]http://www.postgresql.org/docs/9.5/static/explicit-locking.html#ADVISORY-LOCKS

[2]http://www.postgresql.org/docs/9.5/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS-TABLE

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-31
    • 2013-09-28
    • 1970-01-01
    • 2019-02-22
    • 1970-01-01
    相关资源
    最近更新 更多