我在CentOSLinux上使用PostgreSQL 9.3.12。
我有两个进程连接到同一个数据库,使用默认事务隔离级别“读提交”。根据postgres文档,事务中的一个进程在提交之前不应“看到”事务中另一个进程所做的更改。
我看到的一个序列是:
现在,表T应该在两个事务开始之前填充,并且进程B的查询应该出现一行。如果这些进程不并发运行,它会这样做。
我的理解是,进程B应该看到表T中所需行的旧副本,进行更改,这些更改应该被进程A删除和重新填充表T所破坏。我不明白为什么进程B是空的。
除了我自己对这些先决条件的完全误解之外,还有谁能想到我看到这种行为的另一个原因吗?
不要担心糟糕的架构,它正在消失。我只是想理解为什么这种情况似乎违反了我所理解的“读取已提交”事务隔离。
谢谢。
根据postgres文档,事务中的一个进程不应“看到”事务中另一个进程所做的更改,直到它们被提交。
是和否-像往常一样,这取决于情况。留档严格规定:
读取已提交是PostgreSQL中的默认隔离级别。
当事务使用此隔离级别时,SELECT查询(没有FOR UPDATE/SHARE子句)只能看到在查询开始之前提交的数据;它永远不会看到未提交的数据或在并发事务执行查询期间提交的更改。实际上,SELECT查询会在查询开始运行的那一刻看到数据库的快照。但是,SELECT确实会看到在其自己的事务中执行的先前更新的影响,即使它们尚未提交。还要注意,如果其他事务在第一个SELECT启动之后和第二个SELECT启动之前提交更改,则连续的两个SELECT命令可以看到不同的数据,即使它们在单个事务中。
换句话说,简单地说,SELECT不同于SELECT FOR UPDATE/DELETE/UPDATE。
您可以创建简单的测试用例来观察该行为:
会话1
test=> START TRANSACTION;
START TRANSACTION
test=> SELECT * FROM test;
x
----
1
2
3
4
5
6
7
8
9
10
(10 rows)
test=> DELETE FROM test;
DELETE 10
test=>
现在登录另一个会话2:
test=> START TRANSACTION;
START TRANSACTION
test=> SELECT * FROM test;
x
----
1
2
3
4
5
6
7
8
9
10
(10 rows)
test=> SELECT * FROM test WHERE x = 5 FOR UPDATE;
在最后一个命令SELECT… FOR UPDATE
会话1“挂起”并等待某事……
回到第一阶段
test=> insert into test select * from generate_series(1,10);
INSERT 0 10
test=> commit;
COMMIT
现在,当您返回会话2时,您将看到以下内容:
test=> SELECT * FROM test WHERE x = 5 FOR UPDATE;
x
---
(0 rows)
test=> select * from test;
x
----
1
2
3
4
5
6
7
8
9
10
(10 rows)
也就是说-简单的SELECT
仍然看不到任何更改,而SELECT… FOR UPDATE
确实看到行已被删除。但是它没有看到会话1插入的新行
事实上,你看到的序列是: