在 PostgreSQL 中的 CREATE TABLE 上检测到死锁

kho*_*vat 4 postgresql deadlock transaction postgresql-9.3

当检测到死锁时,我正在异步创建多个表。两张表(表 A 和 B)都与一张表(表 C)有 FK,并且我在与表 C 的关系上遇到了死锁。

脚本

CREATE TABLE "TableA"
(
  "Id" uuid NOT NULL,
  "TableCId" uuid NOT NULL,
  CONSTRAINT "PK_TableA_Id" PRIMARY KEY ("Id"),
  CONSTRAINT "FK_TableA_TableC" FOREIGN KEY ("TableCId")
      REFERENCES "TableC" ("Id") MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
  OIDS=FALSE
);

CREATE TABLE "TableB"
(
  "Id" uuid NOT NULL,
  "TableCId" uuid NOT NULL,
  CONSTRAINT "PK_TableB_Id" PRIMARY KEY ("Id"),
  CONSTRAINT "FK_TableB_TableC" FOREIGN KEY ("TableCId")
      REFERENCES "TableC" ("Id") MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
  OIDS=FALSE
);
Run Code Online (Sandbox Code Playgroud)

该表在上述脚本执行时存在

CREATE TABLE "TableC"
(
  "Id" uuid NOT NULL,
  CONSTRAINT "PK_TableC_Id" PRIMARY KEY ("Id"),
)
WITH (
  OIDS=FALSE
);
Run Code Online (Sandbox Code Playgroud)

因此,当我查看日志时,出现以下错误,关系 87066是 TableC。请注意,TableA 和 TableB 是使用 READ COMMITTED 隔离在单独的事务中创建的。

进程464等待数据库86965的关系87066上的AccessExclusiveLock;被进程 1180 阻止。进程 1180 等待数据库 86965 的关系 87066 上的 AccessExclusiveLock;被进程 464 阻止。

当我查看 PostgreSQL 文档时,它说应该以相同的顺序执行创建表以避免死锁,并且它们以相同的顺序执行,因此不应发生死锁。

有什么原因导致这种情况以僵局结束吗?我认为如果 464 是第一个执行的进程,则进程 464 应该等待 1180?

编辑

另一项注意事项是,我在创建表脚本之前在同一事务中运行一个插入。

INSERT INTO public."TableD"("Id", "TableCId", ....)
VALUES (:p0, :p1, :p2, :p3, :p4, :p5, :p6, :p7, :p8, :p9)
Run Code Online (Sandbox Code Playgroud)

PostgreSQL 9.3

Dan*_*ité 6

假设 INSERT 的目标表(“TableD”)有一列带有“TableC”的外键。对此表的 INSERT 必须获取“TableC”上的共享锁,并且将保持该锁直到事务结束。

假设两个并发事务 T1 和 T2 以这样的 INSERT 开始。每个都在“TableC”上获取共享锁。

稍后,为了执行涉及“TableC”外键的 CREATE TABLE,T1 必须获取“TableC”上的访问独占锁。

由于 T2 已经拥有“TableC”上的共享锁,因此 T1 将等待,直到 T2 提交或回滚。

此时,如果 T2 尝试创建一个还涉及“TableC”外键的表(这又意味着获取“TableC”上的访问独占锁),它必须等待 T1 提交或回滚,因为 T1 持有共享锁定“TableC”。

所以现在 T1 正在等待 T2 完成,T2 正在等待 T1 完成:这就是死锁。

为了避免这种情况,可以在事务开始时,或者至少在不能由并发事务安全地并行运行的指令序列的开始处采取显式排他锁。