Postgres:SELECT FOR UPDATE在锁释放后看不到新行

时间:2019-02-08 18:19:05

标签: postgresql transactions locking postgresql-11 select-for-update

尝试在我的应用程序中支持PostgreSQL DB时,发现了这种奇怪的行为。

准备工作

CREATE TABLE test(id INTEGER, flag BOOLEAN);
INSERT INTO test(id, flag) VALUES (1, true);

假定两个并发事务( Autocommit = false,READ_COMMITTED TX1 TX2

TX1:

UPDATE test SET flag = FALSE WHERE id = 1;
INSERT INTO test(id, flag) VALUES (2, TRUE);
-- (wait, no COMMIT yet)

TX2:

SELECT id FROM test WHERE flag=true FOR UPDATE;
-- waits for TX1 to release lock

现在,如果我 TX1 中的 COMMIT ,TX2中的SELECT将返回空光标。

对我来说很奇怪,因为在Oracle和MariaDB中进行的相同实验导致选择了新创建的行(id = 2)。

我在PG文档中找不到有关此行为的任何信息。 我想念什么吗? 有什么方法可以强制PG服务器在获取锁定后“刷新”语句可见性?

PS:PostgreSQL版本11.1

1 个答案:

答案 0 :(得分:1)

TX2 扫描表并尝试锁定结果。

该扫描从查询开始就可以看到数据库的快照,因此它无法看到在之后开始的并发修改所插入(或通过其他方式使其符合条件)的任何行。已拍摄快照。

这就是为什么您看不到带有id 2的行的原因。

对于id 1,这也是正确的,因此扫描会找到该行。但是查询必须等待直到锁被释放。最终发生这种情况时,它将获取该行的最新提交版本,然后再次执行检查,因此该行也将被排除。

此“ EvalPlanQual”重新检查(以使用PostgreSQL术语)仅针对在扫描期间发现但已锁定的行执行。在扫描过程中甚至找不到第二行,因此在那里没有进行此类处理。

这有点奇怪,承认。但这不是错误,而只是PostgreSQL运作的方式。

如果要避免此类异常,请使用REPEATABLE READ隔离级别。然后,在这种情况下,您会收到一个,并可以重试该事务,从而避免了这种不一致的情况。