multithreading - 原子更新 .. 在 Postgres 中选择

标签 multithreading postgresql concurrency race-condition transaction-isolation

我正在构建某种排队机制。有几行数据需要处理,还有一个状态标志。我正在使用 update .. returning 子句来管理它:

UPDATE stuff
SET computed = 'working'
WHERE id = (SELECT id from STUFF WHERE computed IS NULL LIMIT 1)
RETURNING * 

嵌套的选择部分是否与更新相同的锁,或者我在这里有一个竞争条件?如果是这样,内部选择是否需要是 select for update

最佳答案

虽然 Erwin 的建议可能是获得正确行为的最简单方法(只要您在 SQLSTATE 为 40001 时重试事务),排队应用程序就其本质而言,与 SERIALIZABLE 事务的 PostgreSQL 实现相比,它们倾向于更好地处理请求阻塞以获得轮到队列的机会,后者允许更高的并发性并且对机会更“乐观”碰撞。

问题中的示例查询,就目前而言,在默认的 READ COMMITTED 事务隔离级别中,将允许两个(或更多)并发连接从队列中“声明”同一行。会发生什么:

  • T1 开始并在 UPDATE 阶段锁定行。
  • T2 在执行时间上与 T1 重叠并尝试更新该行。它阻止等待 T1 的 COMMITROLLBACK
  • T1 提交,已成功“声明”该行。
  • T2 尝试更新该行,发现 T1 已经有,寻找该行的新版本,发现它仍然满足选择条件(即 id 匹配),并且也“声明”该行。

可以对其进行修改以使其正常工作(如果您使用的 PostgreSQL 版本允许在子查询中使用 FOR UPDATE 子句)。只需将 FOR UPDATE 添加到选择 id 的子查询的末尾,就会发生这种情况:

  • T1 启动并在选择 id 之前锁定行。
  • T2 在执行时间上与 T1 重叠,并在尝试选择 id 时阻塞,等待 T1 的 COMMITROLLBACK
  • T1 提交,已成功“声明”该行。
  • 当 T2 能够读取该行以查看 id 时,它发现它已被声明,因此它会找到下一个可用的 id。

REPEATABLE READSERIALIZABLE 事务隔离级别,写冲突会抛出错误,您可以根据 SQLSTATE 捕获并确定是序列化失败,并重试。

如果您通常需要 SERIALIZABLE 事务,但又想避免在排队区重试,您可以使用 advisory lock 来实现。 .

关于multithreading - 原子更新 .. 在 Postgres 中选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11532550/

相关文章:

javascript - 将嵌套数组添加到 Sequelize/PostgreSQL 数据库

ruby-on-rails - 尝试将本地数据库推送到 Heroku 后出现 "ActionView::Template::Error (Unknown primary key for table"

c# - 多个 worker 同时触发重复工作

python - 等待线程完成使用连接。很基本

linux - 在 Linux 中创建新线程是否复制文件描述符和套接字描述符?

postgresql - 如何从单个字段中的多个值进行搜索(GTFS 中的公交车站)?

android - Android SDK 的 CircularArray 线程安全吗?

multithreading - ARM STLR 内存排序语义

java HashMap 线程可见性

java - Java中的线程概念