我有一个多个进程在一个表中对帐户进行一些工作。我已经实现了一个查询,每个进程通过在锁定列中设置一个值来锁定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.
答案 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