postgresql - 具有 read_commited 隔离的 Postgres 死锁

标签 postgresql deadlock

我们注意到 Postgresql 9.2 服务器在以下情况下很少发生死锁:

T1开始批量操作:

UPDATE BB bb SET status = 'PROCESSING', chunk_id = 0 WHERE bb.status ='PENDING' 
AND bb.bulk_id = 1 AND bb.user_id IN (SELECT user_id FROM BB WHERE bulk_id = 1 
AND chunk_id IS NULL AND status ='PENDING' LIMIT 2000)

当 T1 在几百毫秒左右(BB 有数百万行)后提交时,多个线程开始新的事务(每个线程一个事务)从 BB 读取项目,进行一些处理并以 50 或 50 的批处理更新它们所以查询:

对于选择:

SELECT *, RANK() as rno OVER(ORDER BY user_id) FROM BB WHERE status = 'PROCESSING' AND bulk_id = 1 and rno = $1

和更新:

UPDATE BB set datetime=$1, status='DONE', message_id=$2 WHERE bulk_id=1 AND user_id=$3

(user_id、bulk_id 有 UNIQUE 约束)。

由于外部情况问题,另一个事务 T2 在 T1 提交后几乎立即执行与 T1 相同的查询(项目标记为“正在处理”的初始批处理操作)。

UPDATE BB bb SET status = 'PROCESSING', chunk_id = 0 WHERE bb.status ='PENDING' 
AND bb.bulk_id = 1 AND bb.user_id IN (SELECT user_id FROM BB WHERE bulk_id = 1 
AND chunk_id IS NULL AND status ='PENDING' LIMIT 2000)

然而,尽管这些项目被标记为“正在处理”,但此查询会因工作线程中的某些更新(如我所说的那样分批完成)而死锁。据我了解,这不应该发生在我们使用的 READ_COMMITTED 隔离级别(默认)中。我确信 T1 已经提交,因为工作线程在它提交之后执行。

编辑:我应该澄清的一件事是 T2 在 T1 之后但在提交之前开始。然而,由于我们在同一行上使用 SELECT for UPDATE 获取了 write_exclusive 元组锁(不受上述任何查询的影响),它会在运行批处理之前等待 T1 提交更新查询。

最佳答案

When T1 commits after a few hundred milliseconds or so (BB has many millions of rows), multiple threads begin new Transactions (one transaction per thread) that read items from BB, do some processing and update them in batches of 50 or so with the queries:

这让我觉得这是一个并发问题。我认为最好让一个事务读取行并将它们交给工作进程,然后在它们返回时分批更新它们。您的基本问题将是这些行有效地处理不确定状态,在事务期间保留行等。您必须单独处理回滚等,因此锁定是一个真正的问题。

现在,如果该解决方案不可行,我将有一个单独的锁定表。在这种情况下,每个线程分别启动,锁定锁定表,声明一堆行,将记录插入锁定表,然后提交。这样每一个线程都声明了记录。然后他们可以处理他们的记录集,更新它们等。您可能希望有一个定期清除陈旧锁的过程。

本质上,您的问题是行从状态 A -> 处理 -> 状态 B 开始,并且可能会回滚。由于其他线程无法知道哪些行正在处理以及由哪些线程处理,因此您无法安全地分配记录。一种选择是将模型更改为:

state A -> claimed state -> processing -> state B。然而,你必须有一些方法来确保行被有效分配,并且线程知道哪些行已分配给彼此。

关于postgresql - 具有 read_commited 隔离的 Postgres 死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16861151/

相关文章:

spring - JPA findBy...IgnoreCase 和 PostgreSQL 索引

postgresql - 使用 Springboot 和 Postgres 废弃的连接池

sql - 存储在 varchar 中的除法数字

ruby-on-rails - 导入数据后 Postgresql 转储不会更正数据库

从主队列调用 dispatch_sync 时 iOS 死锁,执行的 block 保存到核心数据

multithreading - 多线程编程中是否可以恢复死锁?

postgresql - 如何在不将磁盘大小加倍的情况下向 postgresql 表添加非空列

java - 在 Eclipse 中调试 Android 上的死锁

java - Java 中使用信号量的多线程

java rmi死锁