【问题标题】:SELECT updated rows during concurrent updates在并发更新期间选择更新的行
【发布时间】:2020-06-06 05:59:18
【问题描述】:

假设我有一组代码创建一个事务,选择、删除和重新创建一个资源,并提交,以便更新它:

  1. BEGIN
  2. SELECT * FROM "product" WHERE (code = 'A') FOR UPDATE
  3. DELETE FROM "product" WHERE "product"."id" = 1
  4. INSERT INTO "product" ("id","code","price") VALUES (1,'A',3000) // Updated price
  5. COMMIT

现在,假设这段代码同时运行两次,并试图修改同一行:

TX1 - BEGIN
TX1 - SELECT product
TX1 - DELETE product

TX2 - BEGIN
TX2 - SELECT product - This blocks until TX1 has been committed

TX1 - INSERT updated product
TX1 - COMMIT

TX2 - SELECT product - Error occurs here...

如果我使用事务级别 ReadCommitted,我会返回 0 行。请注意,TX2 是在 TX1 中删除产品之后创建的。

我无法使用 SerializableRepeatableRead,因为行已更改,我得到 pq: could not serialize access due to concurrent update

有没有办法让TX2 中的SELECT 阻塞直到TX1 完成,然后SELECT 新更新的行我是否错误地使用了SELECT...FOR UPDATE

【问题讨论】:

  • 为什么不更新而不是删除和插入?

标签: sql postgresql concurrency transactions isolation-level


【解决方案1】:

隔离级别如何在 PostgreSQL 中工作的行为和原因在他们的文档中得到了很好的总结: https://www.postgresql.org/docs/9.5/transaction-iso.html#XACT-READ-COMMITTED)。与

在我看来,重要的是 PostgreSQL 使用乐观锁定进行事务处理。当事务开始时,会拍摄一个快照,并且可以使用它。 手动使用SELECT ... FOR UPDATE意味着对行使用悲观锁。

现在,当您使用RepeatableRead 时,数据库承诺保证事务隔离。

TX1 悲观地锁定行时,TX2 等待下一步。如果TX1 回滚,那么TX2 可能会继续。但是随着TX1 的提交,TX2 除了回滚并抛出异常以满足隔离级别的承诺之外没有其他可能性。

您可以从这里重新运行 SQL 查询,该查询将看到数据库状态的更新版本。

所以对于你的问题的回应

有没有办法让 TX2 块中的 SELECT 直到 TX1 完成,然后选择新更新的行?

是的,使用RepeatableRead 并且当TX2 的查询失败时再次重新运行您的查询。

【讨论】:

    猜你喜欢
    • 2013-09-25
    • 2014-03-30
    • 1970-01-01
    • 1970-01-01
    • 2013-10-15
    • 1970-01-01
    • 2021-01-04
    • 2019-12-19
    • 1970-01-01
    相关资源
    最近更新 更多