【问题标题】:What will happen in PostgreSQL if a cascading delete is attempted on the parent of a locked row?如果在锁定行的父级上尝试级联删除,PostgreSQL 会发生什么?
【发布时间】:2012-08-15 10:26:34
【问题描述】:

我有一个表foo_bar 和另一个表spam_eggs,其中fb 外键指向foo_barspam_eggs 行在其相关的spam_eggs.fb 被删除时被级联删除。

我正在使用 PostgreSQL。

在事务中,我使用SELECT... FOR UPDATE 锁定spam_eggs 行。在此事务期间,另一个事务试图DELETE FROM... 我锁定行的相关foo_bar。这会触发错误,还是我锁定的行会导致查询阻塞,直到我的原始更新事务结束?

【问题讨论】:

  • 短版:它会阻塞。长版:当它解除阻塞时,会发生什么取决于两个事务的隔离级别是什么,第一个事务还做了什么,以及第一个事务是回滚还是提交。见答案。

标签: sql postgresql select transaction-isolation


【解决方案1】:

试试看。打开psql 并进行一些设置:

CREATE TABLE foo_bar(id integer primary key);
CREATE TABLE spam_eggs(
     foo_bar_id integer not null references foo_bar(id) on delete cascade
);
INSERT INTO foo_bar (id) VALUES (1),(2),(3),(4);
INSERT INTO spam_eggs(foo_bar_id) VALUES (1),(2),(3),(4);

然后打开另一个 psql 连接。 BEGIN 两人的交易。

  1. 在第一个(旧)会话中,运行 SELECT 1 FROM spam_eggs WHERE foo_bar_id = 4 FOR UPDATE;
  2. 在第二个(新)会话中,运行DELETE FROM foo_bar WHERE id = 4;

您会看到第二个语句阻塞在第一个语句上。这是因为foo_bar 上的DELETE 级联到spam_eggs 并尝试使用外键引用锁定该行,以便将其删除。该锁阻塞了SELECT ... FOR SHARE 持有的锁。

一般来说,尝试在所有这些情况下进行测试:

  • tx 是 BEGIN ISOLATION LEVEL READ COMMITTED 并首先发出 ROLLBACK
  • tx 是 BEGIN ISOLATION LEVEL READ COMMITTED 并首先发出 COMMIT
  • tx 是 BEGIN ISOLATION LEVEL SERIALIZABLE 并首先发出 ROLLBACK
  • tx 是 BEGIN ISOLATION LEVEL SERIALIZABLE 并首先发出 COMMIT

以确保您知道会发生什么。如果你在测试之前对你期望发生的事情进行推理,这对你的学习也有好处。

在这种情况下,READ COMMITTEDSERIALIZABLE 隔离级别的行为相同。如果您实际上在您的SELECT ... FOR UPDATE 之后执行UPDATE,然后COMMIT,那么它们的行为会有所不同; READ COMMITTED 版本将 DELETE 成功,而 SERIALIZABLE 版本将失败:

regress=# BEGIN ISOLATION LEVEL SERIALIZABLE;
regress=# DELETE FROM foo_bar WHERE id = 4;                                                                                                                                                                                                    
ERROR:  could not serialize access due to concurrent update                                                                                                                                                        
CONTEXT:  SQL statement "DELETE FROM ONLY "public"."spam_eggs" WHERE $1 OPERATOR(pg_catalog.=) "foo_bar_id""

【讨论】:

  • 非常感谢,克雷格。我很欣赏所有的细节。
猜你喜欢
  • 2018-08-31
  • 2011-09-05
  • 1970-01-01
  • 1970-01-01
  • 2021-12-02
  • 1970-01-01
  • 2012-02-29
  • 2010-09-07
  • 1970-01-01
相关资源
最近更新 更多