热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

在表上添加触发器时,PSQLException和锁定问题

如何解决《在表上添加触发器时,PSQLException和锁定问题》经验,您有什么比较好的解决方法?

更新:我从问题中消除了休眠。我完全重新编写了对问题的描述,以尽可能地简化它。

我有master带noop触发器的detail表和带masterdetail表之间的两个关系的表:

create table detail (
  id bigint not null,
  code varchar(255) not null,
  primary key (id)
);

create table master (
  id bigint not null,
  name varchar(255),
  detail_id bigint, -- "preferred" detail is one-to-one relation
  primary key (id),
  unique (detail_id),
  foreign key (detail_id) references detail(id)
);

create table detail_candidate ( -- "candidate" details = many-to-many relation modeled as join table
  master_id bigint not null,
  detail_id bigint not null,
  primary key (master_id, detail_id),
  foreign key (detail_id) references detail(id),
  foreign key (master_id) references master(id)
);

create or replace function trgf() returns trigger as $$
begin
  return NEW;
end;
$$ language 'plpgsql';

create trigger trg
  before insert or update
  on master
  for each row execute procedure trgf();

insert into master (id, name) values (1000, 'x'); -- this is part of database setup
insert into detail (code, id) values ('a', 1);    -- this is part of database setup

在这种设置中,我使用打开两个终端窗口,psql然后执行以下步骤:

    在第一个终端中,更改主服务器(将事务保持打开状态)

begin;
update master set detail_id=null, name='y' where id=1000;

    在第二终端中,添加详细信息候选者以自己进行交易

begin;
set statement_timeout = 4000;
insert into detail_candidate (master_id, detail_id) values (1000, 1);

第二个终端超时中的最后一条命令,带有消息

ERROR:  canceling statement due to statement timeout
CONTEXT:  while locking tuple (0,1) in relation "master"
SQL statement "SELECT 1 FROM ONLY "public"."master" x WHERE "id" OPERATOR(pg_catalog.=) $1 FOR KEY SHARE OF x"

我的观察和问题(更改是独立的):

当数据库设置为无触发时,即drop trigger trg on master;在初始设置后调用时,一切正常。为什么存在noop触发器会产生这种影响?我不明白

当数据库被设置为没有唯一约束时master.detail_id(即alter table master drop constraint master_detail_id_key;在初始设置后被调用),一切也都可以正常工作。为什么?

当我detail=null在第一个终端的update语句中省略显式分配时(因为无论如何安装程序中都存在null值),一切也都正常。为什么?

在Postgres 9.6.12(嵌入式),9.6.15(在Docker中),11.5(在Docker中)中试用。

问题tomaszalusky/trig-example可以在DockerHub上可用的Docker映像中重现,也可以从此Dockerfile(内部指令)构建。


更新2:我发现上面三个观察的常见行为。我在第二个事务中select * from pgrowlocks('master')从pgrowlocks扩展中生成了查询。该行级锁更新行的masterFOR UPDATE在失败的情况下,但FOR NO KEY UPDATE在所有三个工作情况。这是与文档中的模式匹配表完全一致的,因为FOR UPDATEmode是更强大的模式,而insert语句请求的mode是FOR KEY SHARE(从错误消息中可以明显看出,也调用该select ... for key share命令具有与command相同的效果insert)。

FOR UPDATE模式文档说明:

FOR UPDATE锁定模式还可以通过(...)UPDATE来修改,该UPDATE会修改某些列上的值。当前,在UPDATE情况下考虑的那组列是可以在外键(...)中使用的唯一索引。

master.detail_id列是正确的。但是,仍然不清楚为什么FOR UPDATE没有根据触发条件单独选择模式,以及为什么触发条件导致了它。


推荐阅读
author-avatar
qq2304944703
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有