PostgreSQL在尝试获取更新记录时遇到死锁

时间:2014-10-02 18:42:51

标签: sql database postgresql

我有一个多个进程在一个表中对帐户进行一些工作。我已经实现了一个查询,每个进程通过在锁定列中设置一个值来锁定100个随机解锁记录并返回锁定的ID。

UPDATE accounts SET locked = now() WHERE account_id in
    (SELECT account_id FROM accounts
              WHERE last_account_info_fetched IS NULL AND locked IS NULL
    LIMIT 100
    FOR UPDATE)
RETURNING account_id

从另一个事务中的第一个查询返回一堆ID的进程会更新并解锁记录。

UPDATE accounts SET last_account_info_fetched = ?, locked = NULL WHERE account_id = ?

问题是当多个进程运行第一个查询时,会导致死锁。如何解决这个问题,以便每个进程获得一组不同的ID而不会出现死锁?

ERROR: deadlock detected
  Detail: Process 3428 waits for AccessExclusiveLock on tuple (16865,68) of relation 10409452 of database 10221183; blocked by process 8628.
Process 8628 waits for ShareLock on transaction 27789140; blocked by process 5340.
Process 5340 waits for ShareLock on transaction 27789126; blocked by process 3428.

3 个答案:

答案 0 :(得分:1)

如何为每个帖子添加范围限制,以便它们不会重叠:

UPDATE accounts SET locked = now() WHERE account_id in
    (SELECT account_id FROM accounts
              WHERE last_account_info_fetched IS NULL AND locked IS NULL 
              and account_id >= 0 
              and account_id <1000 
              LIMIT 100
    FOR UPDATE)
RETURNING account_id

如果你能找到每个线程拥有自己范围的方法,那么你就不应该遇到这个问题。您可以让每个线程选择一个随机数,然后只解锁该范围内的行,但在该场景中仍会偶尔重叠一次。

答案 1 :(得分:0)

您似乎正在尝试实现工作队列/任务队列或邮件系统。

这比你发现的要难得多。

我建议使用现有的一个;查看ActiveMQ,ZeroMQ,Celery,RQ,resqueue,......许多其他消息和任务队列。

正在进行的工作应该会使PostgreSQL 9.5更容易:SKIP LOCKED补丁,它允许您选择尚未被其他进程锁定的第一个n记录。

答案 2 :(得分:0)

这是一个很好的答案:Avoiding PostgreSQL deadlocks when performing bulk update and delete operations

以下是您的查询在修复中的外观:

UPDATE accounts SET locked = now() WHERE account_id in
(SELECT account_id FROM accounts
          WHERE last_account_info_fetched IS NULL AND locked IS NULL 
          and account_id >= 0 
          and account_id <1000 
          ORDER BY account_id --that`s it
          LIMIT 100
    FOR UPDATE)
RETURNING account_id