简单的 UPDATE 和 DELETE 语句可以在 PostgreSQL 中触发死锁和回滚吗?

Posted

技术标签:

【中文标题】简单的 UPDATE 和 DELETE 语句可以在 PostgreSQL 中触发死锁和回滚吗?【英文标题】:Can simple UPDATE and DELETE statements trigger deadlocks and rollback in PostgreSQL? 【发布时间】:2014-03-28 17:14:24 【问题描述】:

当在同一张表上同时执行其他更复杂的语句时,触发器中执行的简单 UPDATEDELETE 语句是否会导致死锁或回滚?

UPDATE "s_mat"
    SET "req_st" = my_st, "l_upd" = retr
    WHERE "req_id" = my_id;

DELETE FROM "mat" WHERE "req_id" = my_id;

我是否应该预期这些语句可能出现 deadlock_detectedtransaction_rollback 异常?

【问题讨论】:

【参考方案1】:

基本上是的。如果两个触发器同时在两个单独的事务中运行,我们称它们为 t1 和 t2:

t1                t2
update row x
                 update row y
delete row y
                 delete row x

这会陷入僵局。 Postgres 自动检测条件并中止除一个竞争事务之外的所有事务。 Details in the manual.

如果您的所有代码都以相同(确定的)顺序处理行,则不会发生这种情况。但有时这无法保证。

如果您使用排他锁(按规范顺序)锁定要处理的所有行manually,则可以显着减少死锁的机会。

绝对确定,使用SERIALIZABLE 事务隔离运行。您必须为序列化失败准备代码并在这种情况下重试。

可序列化模式下的事务较慢,仅应在必要时使用。如果您没有面临繁重的并发负载或代码中没有关键的语句组合,则使用默认(更快)READ COMMITTED 隔离级别可能会很好。

优化代码的性能并最​​大限度地减少竞争条件的机会窗口可能会长期。除了始终以相同的顺序处理行之外,您还可以使用data-modifying CTEsUPDATEDELETE 组合在一个语句中。不排除竞态条件,但最大限度地减少死锁的时间范围。详细示例:

Delete parent if it's not referenced by any other child

【讨论】:

好的,我想 SERIALIZABLE 是适合我的方式。在这种情况下,我需要仅检查“serialization_failure”还是检查所有 40 类 - 事务回滚异常? @JVerstry:其他一切都保持不变。回滚只发生在异常之后(除非手动触发)并且永远不会发生。为任何异常准备代码不会有什么坏处。但是你应该只在有意义的地方重试(比如序列化失败)。注意不要在这里触发无限循环。我在答案中添加了更多内容。

以上是关于简单的 UPDATE 和 DELETE 语句可以在 PostgreSQL 中触发死锁和回滚吗?的主要内容,如果未能解决你的问题,请参考以下文章

ruby 从New Relic获取一个查询语句的粘贴并解释所有这些语句;现在还可以转换UPDATE和DELETE

SQL注入之初窥insert,update,delete注入

在不提交的情况下测试 (JUnit) SQL DELETE 和 UPDATE 语句

spring data jpa执行update和delete语句时报错处理

MySQL之DML语句(insert update delete)

SQL Server Profiler 2008 怎么追踪特定种类语句如 insert,update,delete,能指定某个数据追踪吗