丢失更新会发生在 PostgreSQL 的读提交隔离级别吗?

Posted

技术标签:

【中文标题】丢失更新会发生在 PostgreSQL 的读提交隔离级别吗?【英文标题】:Can Lost Update happen in read committed isolation level in PostgreSQL? 【发布时间】:2013-01-09 01:07:57 【问题描述】:

我在 PostgreSQL 中有如下查询:

UPDATE 
     queue 
SET 
  queue.status   = 'PROCESSING' 
WHERE 
    queue.status   = 'WAITING' AND
    queue.id       = (SELECT id FROM queue WHERE STATUS = 'WAITING' LIMIT 1 )
RETURNING 
        queue.id

许多工作人员试图一次处理一项工作(这就是为什么我有限制为 1 的子查询)。在此更新之后,每个工作人员都会获取有关 id 的信息并处理工作,但有时他们会获取相同的工作并处理两次或更多次。隔离级别为已提交读。

我的问题是我如何保证一件作品会被处理一次?我知道那里有很多帖子,但我可以说我已经尝试了其中的大部分但没有帮助();

我尝试了 SELECT FOR UPDATE,但它导致了死锁的情况。 我试过 pg_try_advisory_xact_lock,但它导致 out of shared memory 我尝试将AND pg_try_advisory_xact_lock(queue.id) 添加到外部查询的 WHERE 子句中,但是... [?]

任何帮助将不胜感激。

【问题讨论】:

您可能希望使用 PostgreSQL 9.1 中引入的新 serializable 隔离级别:wiki.postgresql.org/wiki/Serializable 和 drkp.net/papers/ssi-vldb12.pdf 【参考方案1】:

在您描述的情况下不会发生更新丢失,但也不会正常工作。

在您上面给出的示例中会发生什么,假设(例如)10 个工作人员同时启动,所有 10 个工作人员都将执行子查询并获得相同的 ID。他们都将尝试锁定该 ID。其中一个会成功;其他人将阻止第一个锁。一旦第一个后端提交或回滚,其他 9 个后端将争夺锁。将得到它,重新检查 WHERE 子句,发现 queue.status 测试不再匹配,不修改任何行就返回。其他 8 个查询也会发生同样的情况。因此,您使用 10 个查询来完成一个查询的工作。

如果您未能明确检查 UPDATE 结果并看到零行已更新,您可能会认为您丢失了更新,但事实并非如此。您的应用程序中只是由于对执行顺序和隔离规则的误解而导致了并发错误。真正发生的事情是,您正在有效地序列化您的后端,以便一次只有一个实际取得进展。

PostgreSQL 可以避免让它们都获得相同的队列项 ID 的唯一方法是对它们进行序列化,因此在查询 #1 完成之前它不会开始执行查询 #2。如果你愿意,你可以通过LOCKing 队列表来做到这一点......但同样,你也可以只拥有一个工人。

您无法通过咨询锁解决此问题,无论如何也不容易。使用非阻塞锁定尝试迭代队列直到获得第一个可锁定项之前的黑客攻击会起作用,但会很慢而且很笨拙。

您正在尝试使用 RDBMS 实现工作队列。这不会很好地工作。它会很慢,会很痛苦,而且既正确又快速将非常非常困难。不要自己滚。相反,使用完善的、经过良好测试的系统来进行可靠的任务排队。看看 RabbitMQ、ZeroMQ、Apache ActiveMQ、Celery 等。还有PGQ from Skytools,一个基于 PostgreSQL 的解决方案。

相关:

In PostgreSQL, do multiple UPDATES to different rows in the same table having a locking conflict? Can multiple threads cause duplicate updates on constrained set? Why do we need message brokers like rabbitmq over a database like postgres?

【讨论】:

如果我在WHERE 子句中添加pg_try_advisory_xact_lock(queue.id) 会怎样?我编辑了我的问题并将其添加到我的查询中! @PMoubed 请不要编辑您的问题来更改已经存在的内容;添加到它,所以在你编辑之前做出的答案仍然有意义。使用pg_try_advistory_xact_lock 可能会起作用;我无法立即明白为什么它不会,但我还没有详细研究它。不过,您之前不是说您在尝试时遇到了“共享内存不足”错误吗? 我的问题是如何使用咨询锁来避免这个问题! @PMoubed “这个问题”:什么问题?共享内存不足?请详细而明确。 是的,这个问题我的意思是“共享内存不足”。知道如何解决吗?【参考方案2】:

SKIP LOCKED 可用于在 PostgreSql 中实现队列。 see

【讨论】:

以上是关于丢失更新会发生在 PostgreSQL 的读提交隔离级别吗?的主要内容,如果未能解决你的问题,请参考以下文章

在PostgreSQL中分隔整数值

org.postgresql.util.PSQLException:错误:由于事务之间的读/写依赖关系,无法序列化访问

目标数据库中没有列,但出现“架构更新正在终止,因为可能会发生数据丢失”

一步步学习并发:了解并发是如何发生的

AI打开时提示有更新版本的Illustrator生成,可能会发生数据丢失,打开之后啥也没有怎么解决这个问题,

隔离级别与丢失更新