【问题标题】:Lock and transaction in postgres that should block a querypostgres 中的锁定和事务应该阻止查询
【发布时间】:2014-04-08 18:50:33
【问题描述】:

让我们假设在 SQL 窗口 1 中我这样做:

-- query 1 
BEGIN TRANSACTION;
UPDATE post SET title = 'edited' WHERE id = 1;
-- note that there is no explicit commit

然后从另一个窗口(窗口 2)我做:

-- query 2
SELECT * FROM post WHERE id = 1;

我明白了:

1 | original title

这很好,因为默认隔离级别是 READ COMMITTED 并且因为查询 1 从未提交,所以在我从窗口 1 明确提交之前,它执行的更改是不可读的。

事实上,如果我在窗口 1 中这样做:

COMMIT TRANSACTION;

如果我重新运行查询 2,我可以看到更改。

1 | edited

我的问题是:

为什么查询 2 在我第一次运行时返回正常?我期待它会阻塞,因为窗口 1 中的事务尚未提交,并且使用id = 1 放置在行上的锁是(应该是)一个未释放的独占锁,它应该像在窗口 2 中执行的那样阻止读取。所有的rest 对我来说很有意义,但我希望 SELECT 会卡住,直到执行窗口 1 中的显式提交。

【问题讨论】:

  • PostgreSQL 使用 MVCC(多版本并发控制)而不是锁。你得到旧值而不是阻塞。如您所见,这极大地提高了并发性,但可能有点难以理解。
  • @DanielLyons:说“而不是锁”会很远......有锁,写通常不会阻止阅读,反之亦然。
  • @ErwinBrandstetter 我认为你让你的知识深度(这是巨大的)不必要地为一个基于常见误解的直截了当的问题着色。多亏了 MVCC,OP 所期望的那种锁定并不存在。确实,某些操作会锁定,并且无疑某些内部使用锁定,问题中的任何内容都不会导致预期的锁定行为。
  • @DanielLyons:这是锁定和阻塞之间的区别。这里的“特殊”成分是非阻塞锁。

标签: postgresql concurrency locking mvcc


【解决方案1】:

您描述的行为在任何事务性关系数据库中都是正常的和预期的。

如果 PostgreSQL 向您显示第一个 SELECT 的值 edited,那么这样做是错误的 - 这称为“脏读”,在数据库中是个坏消息。

PostgreSQL 将被允许在SELECT 等待,直到您提交或回滚,但 SQL 标准没有要求,您没有告诉它您要等待,它没有等待任何技术原因,因此它会立即返回您要求的数据。毕竟,在它被提交之前,update 只是一种存在——它仍然可能发生,也可能不会发生。

如果 PostgreSQL 总是在这里等待,那么您很快就会遇到这样一种情况,即一次只有一个连接可以对数据库执行任何操作。在性能方面并不漂亮,而且在绝大多数情况下完全没有必要。

如果您想等待并发的UPDATE(或DELETE),您可以使用SELECT ... FOR SHARE。 (但请注意,这不适用于 INSERT)。


详情:

没有FOR UPDATEFOR SHARE 子句的SELECT 不采用任何行级锁。因此,它可以看到当前提交的行,并且不受任何可能正在修改该行的正在进行的事务的影响。 MVCC section of the docs 中解释了这些概念。一般的想法是 PostgreSQL 是写时复制的,其版本控制允许它根据事务或语句在它开始时“看到”的内容返回正确的副本 - PostgreSQL 称之为“快照”。

在默认的READ COMMITTED 隔离快照中是在语句级别拍摄的,所以如果您SELECT 一行,COMMIT 从另一个事务对其进行更改,然后再次SELECT 它,您将看到不同的值即使在一次交易中。如果您不希望在事务开始后看到提交的更改,您可以使用SNAPSHOTisolation,或者使用SERIALIZABLEisolation 来针对某些类型的事务相互依赖添加进一步的保护。

the transaction isolation chapter in the documentation

如果您希望SELECT 等待正在进行的事务提交或回滚对所选行的更改,则必须使用SELECT ... FOR SHARE。这将阻塞UPDATEDELETE 获取的锁,直到获取锁的事务回滚或提交。

INSERT 是不同的,但是 - 元组在提交之前不存在于其他事务中。等待并发INSERTs 的唯一方法是获取EXCLUSIVE 表级锁,这样您就知道在您读取表时没有其他人在更改表。通常需要这样做意味着您在应用程序中存在设计问题 - 如果仍有未提交的inserts 仍在运行,您的应用程序不应该关心

the explicit locking chapter of the documentation

【讨论】:

    【解决方案2】:

    PostgreSQL's MVCC实现中,原则是读不阻塞写,反之亦然。 The manual:

    使用并发控制的MVCC模型的主要优势 而不是锁定是在 MVCC 中为查询而获取的锁 (读取)数据与写入数据获取的锁不冲突, 所以阅读永远不会阻碍写作,写作永远不会阻碍阅读。 即使在提供最严格的 通过使用创新的事务隔离级别 Serializable Snapshot Isolation (SSI) 级别。

    每个事务只看到(大部分)事务开始之前已提交的内容。

    意味着没有锁定。一点也不。对于许多操作,需要获取各种锁。并应用各种策略来解决可能的冲突。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-19
      相关资源
      最近更新 更多