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

Iva*_*kov 8 postgresql locking transactions postgresql-11 select-for-update

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

准备:

CREATE TABLE test(id INTEGER, flag BOOLEAN);
INSERT INTO test(id, flag) VALUES (1, true);
Run Code Online (Sandbox Code Playgroud)

假设有两个并发事务 ( Autocommit=false, READ_COMMITTED ) TX1TX2

TX1:

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

TX2:

SELECT id FROM test WHERE flag=true FOR UPDATE;
-- waits for TX1 to release lock
Run Code Online (Sandbox Code Playgroud)

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

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

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

PS:PostgreSQL版本11.1

Lau*_*lbe 10

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

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

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

对于id1,这也是如此,因此扫描会找到该行。但查询必须等到锁被释放。当这最终发生时,它会获取该行的最新提交版本并再次执行检查,以便该行也被排除。

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

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

如果您想避免此类异常,请使用REPEATABLE READ隔离级别。那么在这种情况下您将收到序列化错误,并且可以重试事务,从而避免类似的不一致。