2013-05-31 31 views
1

我们注意到下列情况一个PostgreSQL 9.2服务器上的僵局的一种罕见的发生:Postgres的僵局read_commited隔离

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个左右的批次:

对于选择:

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唯一性约束)。

由于状况问题的外部原因,另一个事务T2几乎在T1已经提交之后执行与T1相同的查询(初始批处理操作,其中项目被标记为'PROCESSING')。

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提交。

回答

0

当T1几百毫秒左右后提交(BB有几百万行),多线程开始从BB阅读项目新交易(每线程一个交易),做一些处理,并分批进行更新大约有50左右的查询:

这让我想起了一个并发问题。我认为你最好有一个事务读取行并将它们交给工作进程,然后在他们回来时分批更新它们。你的根本问题在于,这些行在不确定的状态下有效地工作,在交易期间持有行等。你必须分别处理回滚等,因此锁定是一个真正的问题。

现在,如果这种解决方案是不可能的,我会有一个单独的锁定表。在这种情况下,每个线程分别旋转,锁定锁定表,声明一堆行,将记录插入锁定表并提交。这样每个线程都要求记录。然后他们可以在他们的记录集上工作,更新它们等。您可能想要一个定期清除旧锁的进程。

实质上,你的问题是行从状态A→处理→状态B,并可能回滚。由于其他线程无法知道正在处理的行和哪些线程,因此无法安全地分配记录。一种选择是将模型更改为:

状态A - >声明状态 - >处理 - >状态B.但是你必须有一些方法来确保行被有效地分配,并且线程知道哪些行已经被分配给彼此。