如果在锁定行的父级上尝试级联删除,PostgreSQL会发生什么?

oro*_*aki 5 sql postgresql select transaction-isolation

我有一个表foo_bar,另一个表spam_eggsfb外键指向foo_bar.spam_eggs删除相关的行时会级联spam_eggs.fb删除.

我正在使用PostgreSQL.

在我曾经用于SELECT... FOR UPDATE锁定spam_eggs行的事务中.在此事务的持续时间内,另一个事务已尝试DELETE FROM...foo_bar我的锁定行相关联.这会触发错误,还是我的锁定行会导致查询阻塞直到我的原始更新事务结束?

Cra*_*ger 13

试试看吧.打开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);
Run Code Online (Sandbox Code Playgroud)

然后打开另一个psql连接.BEGIN两者中的交易.

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

您将看到第二个语句在第一个语句上阻塞.这是因为它DELETEfoo_bar级别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 COMMITTED,SERIALIZABLE隔离级别的行为将相同.如果你真的UPDATE在你SELECT ... FOR 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""
Run Code Online (Sandbox Code Playgroud)