【发布时间】: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