根据 PSQL docs 关于显式锁定:
SELECT FOR UPDATE 将等待在同一行上运行任何这些命令的并发事务,然后锁定并返回更新的行(如果该行被删除,则不返回任何行)。
但是,当我更新该行中的外键并且在 select 语句中与其他表使用连接时,它似乎没有返回更新的行。对于其他列,它按预期工作。看起来,当在并发事务中更新外键列时,更新的行不可见,并且其行为就像该行已被删除。
下面是我的简短示例。我希望两个(并发)事务都会更新该行。然而,只有其中一个这样做,第二个没有返回任何内容。
但是,当第一个事务中的更新语句更改为更新非 FK 列时,两个事务都可以选择该行,正如文档所预期的那样。
请注意,
select
已连接到foo
表,即使该表未更新,但我实际上需要foo
中的一些列,这只是一个最小的示例。如果没有 join,即使更新 fk,它也会按预期工作。
因此,此行为有两个重要条件 - 1. 更新 FK 列,2. 在 select 语句中使用 JOIN。
create table foo(pk int primary key);
create table bar(pk int, fk int references foo(pk), value int);
insert into foo values (1), (2), (3);
insert into bar values (1, 1, 1);
---- First transaction
begin;
select * from bar join foo on foo.pk = bar.fk where bar.pk = 1 for update of bar;
-- Now run select in the second transaction concurrently, see code below and then get back here.
update bar set fk = 2;
-- Update value column instead of fk and the second transaction will return row as expected.
-- update bar set value = 10;
commit;
---- Second (concurrent) transaction
begin;
select * from bar join foo on foo.pk = bar.fk where bar.pk = 1 for update of bar;
-- Continue in the first transaction.
-- Returns nothing after first transaction is commited; The row is not locked so we can't update it.
commit;
我在文档中错过了什么?对这种行为有什么解释吗?最好有参考资料。
它应该可以在干净的数据库中重现,配置中没有更改,读取已提交。
您可以重写您的选择
create table foo(pk int primary key);
create table bar(pk int, fk int references foo(pk), value int);
insert into foo values (1), (2), (3);
insert into bar values (1, 1, 1);
---- First transaction
begin;
select * from bar where bar.pk = 1 AND EXISTS( SELECT 1 FROM foo WHERE foo.pk = bar.fk) for update of bar;
-- Now run select in the second transaction concurrently, see code below and then get back here.
update bar set fk = 2;
-- Update value column instead of fk and the second transaction will return row as expected.
-- update bar set value = 10;
commit;
---- Second (concurrent) transaction
begin;
select * from bar join foo on foo.pk = bar.fk where bar.pk = 1 for update of bar;
-- Continue in the first transaction.
-- Returns nothing after first transaction is commited; The row is not locked so we can't update it.
commit;
CREATE TABLE
CREATE TABLE
INSERT 0 3
INSERT 0 1
BEGIN
PK | fk | 价值 |
---|---|---|
1 | 1 | 1 |
SELECT 1
UPDATE 1
COMMIT
BEGIN
PK | fk | 价值 | PK |
---|---|---|---|
1 | 2 | 1 | 2 |
SELECT 1
COMMIT