SELECT 在并发更新期间更新了行

SELECT updated rows during concurrent updates

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

  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不正确吗?

隔离级别在 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 查询,这将看到数据库状态的更新版本。

所以为了回答你的问题

Is there any way to have the SELECT in TX2 block until TX1 has been completed, and then SELECT the new updated row?

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