SELECT FOR UPDATE 锁定 PostgreSQL 中的其他表

Jul*_*ien 8 postgresql locking postgresql-9.6

PostgreSQL 9.6 的情况:

  • 带有整数主键的表 A
  • 表 B 的主键上有外键约束,引用表 A 的主键

SELECT id FROM A FOR UPDATE;阻塞UPDATE B SET x=y;直到 A 上的锁被释放。

我认为这是因为引用的值可能会改变。我怎样才能在不删除外键约束的情况下避免第一个语句阻塞第二个语句的执行?

如果我放弃外键约束,我应该期待什么坏事?在出现这个问题的实际数据库中,如果表 B 从 A 删除后还有剩余行,这不会是一个重大问题。我们也从不更新主键。

用代码编辑

-- Setup
create table a (id int primary key);
create table b (id int primary key references a (id) on update cascade on delete cascade, x varchar);
insert into a values (1), (2);
insert into b values (1, null), (2, null);

-- Left
begin;
select 1 from a for update;
/* Keep the transaction open */

-- Right
begin;
update b set x = 'abc' where id = 1;
update b set x = 'xyz'; /* It blocks here /*
Run Code Online (Sandbox Code Playgroud)

ype*_*eᵀᴹ 10

我们也永远不会更新主键。

在这种情况下,我认为您可以使用FOR NO KEY UPDATE代替FOR UPDATE. 这是一个较弱的锁,如 Postgres docs about Explicit Locking 中所述

FOR NO KEY UPDATE

行为与 类似FOR UPDATE,不同之处在于获取的锁较弱:此锁不会阻止SELECT FOR KEY SHARE尝试获取相同行上的锁的命令。这种锁定模式也被任何UPDATE没有获得FOR UPDATE锁定的人获得。

测试:

-- Setup
create table a (id int primary key, 
                x varchar);
create table b (id int primary key 
                    references a (id) on update cascade on delete cascade, 
                x varchar);
insert into a values (1), (2);
insert into b values (1, null), (2, null);

-- Left
begin;
select 1 from a for no key update;
/* Keep the transaction open */

            -- Right
            begin;
            update b set x = 'abc' where id = 1;
            update b set x = 'xyz';    -- doesn't block

-- Left
update a set x = 'left' where id = 1;
commit ;

            -- Right
            commit ;
Run Code Online (Sandbox Code Playgroud)