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



我已经遇到了我的应用程序中偶尔陷入僵局,因为两个交易需要更新相同的行但在不同的订单中(ex,交易,更新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会立即失败,因此唯一的性能是对应用程序的应用,对数据库没有任何意义。

关键组件正在捕获每种错误,知道哪些情况需要回滚,并且具有指数向后进行重试。我发现立即进行检测或静态睡眠时间会导致过程简单地彼此僵硬,并引起多米诺骨牌效应,这很有意义。

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

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

最新更新