我们注意到 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 左右的批次更新它们查询:
对于选择:
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 之后但在它提交之前开始。但是,由于我们在同一行上使用 a 获取 write_exclusive 元组锁SELECT for UPDATE
(不受上述任何查询的影响),它在运行批量更新查询之前等待 T1 提交。