postgresql - 依靠 Postgres 的死锁检测来进行并发控制是否安全?

标签 postgresql deadlock database-deadlocks

我的应用程序偶尔会遇到死锁,因为两个事务需要更新相同的行但顺序不同(例如,事务 A 更新行 X 然后 Y,而事务 B 更新行 Y 然后 X)。

由于各种原因,解决避免此类死锁的传统方法(锁定或以一致的顺序更新行)不太理想。

由于我尝试执行的更新在其他方面是幂等且与顺序无关的,因此简单地在应用程序级别捕获这些偶尔出现的死锁并重试事务是否安全合理?

例如:

def process_update(update):
    attempt = 0
    while attempt < 10:
        try:
            execute("SAVEPOINT foo")
            for row in update:
                execute("UPDATE mytable SET … WHERE …", row)
            execute("RELEASE SAVEPOINT foo")
            break
        except Deadlock:
            execute("ROLLBACK TO SAVEPOINT foo")
        attempt += 1
    raise Exception("Too many retries")

这是一个合理的想法吗?或者是否存在与 Postgres 的死锁检测相关的成本,这可能会使其变得危险?

最佳答案

我针对在同一张表上运行 50 到 100 个并发进程的系统对此进行了大量研究和实验。除了基本的死锁之外,还可能发生许多事务失败。我的案例包括读取已提交和可序列化的事务。在应用程序级别处理此问题不会导致任何问题。幸运的是,Postgres 会立即失败,因此唯一的性能影响是对应用程序的影响,对数据库没有什么影响。

关键部件每type of error ,知道哪些情况需要回滚,并且有一个 exponential backoff for retries .我发现立即重试或静态休眠时间会导致进程简单地反复死锁并导致有点多米诺骨牌效应,这是有道理的。

这是我的系统处理每个并发问题所需的完整逻辑(伪代码):

begin transaction (either read committed or serializable)
while not successful and count < 5
    try 
        execute sql
        commit
    except
        if error code is '40P01' or '55P03'
            # Deadlock or lock not available
            sleep a random time (200 ms to 1 sec) * number of retries
        else if error code is '40001' or '25P02'
            # "In failed sql transaction" or serialized transaction failure
            rollback
            sleep a random time (200 ms to 1 sec) * number of retries
            begin transaction
        else if error message is 'There is no active transaction'
            sleep a random time (200 ms to 1 sec) * number of retries
            begin transaction
    increment count

关于postgresql - 依靠 Postgres 的死锁检测来进行并发控制是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42319930/

相关文章:

sql - PGError : ERROR: column "is_required" is of type boolean but expression is of type integer

sql - 仅考虑具有特定条目的项目来聚合表的有效方法

mysql - MySQL死锁: engine.日志分析

c# - 'await' 有效,但调用 task.Result 挂起/死锁

sql - 1 个单表发生死锁,2 个用户执行简单语句

mysql - 事务数据库和防止死锁

java - 在 Java 8 中使用 Hibernate 5+ 的 PostgreSQL 9.6 DATE 列映射

php - 添加所有行乘以另一个表中的另一行

mysql - 如何在不阻塞的情况下锁定mysql中的行?

mysql - Python 数据插入的 SQL 死锁