根据 postgres 文档,事务中的一个进程应该
不会“看到”事务中另一个进程所做的更改,直到它们
已承诺。
是和否——像往常一样,这取决于情况。文档 https://www.postgresql.org/docs/current/static/transaction-iso.html严格地说:
已提交读是 PostgreSQL 中的默认隔离级别。
当事务使用此隔离级别时,SELECT 查询 (没有
FOR UPDATE/SHARE 子句) 只看到查询之前提交的数据
开始;它永远不会看到未提交的数据或已提交的更改
在并发事务执行查询期间。实际上,一个 SELECT
查询会看到查询时数据库的快照
开始运行。然而,SELECT 确实看到了之前的效果
更新在其自己的事务中执行,即使它们不是
但已承诺。另请注意,两个连续的 SELECT 命令可以看到
不同的数据,即使它们位于单个事务中,如果
其他事务在第一个 SELECT 开始后提交更改,并且
在第二个 SELECT 开始之前。
更新、删除、选择更新、和 SELECT FOR SHARE 命令
在搜索目标行方面与 SELECT 的行为相同:它们
将仅查找自命令开始时提交的目标行
时间。然而,这样的目标行可能已经被更新(或者
被另一个并发事务删除或锁定)
成立。在这种情况下,潜在的更新程序将等待第一个
更新事务以提交或回滚(如果它仍在
进步)。如果第一个更新程序回滚,那么其影响是
被否定,第二个更新程序可以继续更新
原来找到行。如果第一个更新者提交,第二个更新者提交
如果第一个更新者删除了该行,则将忽略该行,否则会
尝试将其操作应用于该行的更新版本。这
命令的搜索条件(WHERE 子句)被重新评估为
查看该行的更新版本是否仍然与搜索匹配
健康)状况。如果是,则第二个更新器使用以下命令继续其操作
该行的更新版本。对于 SELECT FOR UPDATE 和
SELECT FOR SHARE,这意味着它是该行的更新版本
被锁定并返回给客户端。
换句话说,简单的 SELECT 与 SELECT FOR UPDATE/DELETE/UPDATE 不同。
您可以创建简单的测试用例来观察该行为:
第一节
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“挂起”并正在等待某些内容......
回到第 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 插入的新行
事实上,您看到的序列是:
- 进程A开始它的事务
- 进程 A 删除表 T 中的所有内容
- 进程B开始它的事务
- 进程 B 尝试对表 T 中的一行进行选择更新
-
进程 B“挂起”并等待会话 A 进行提交或回滚
- 进程 A 从传入数据重新填充表 T
- 进程A提交其事务
- 进程 B 出现空(会话 A 提交后 0 行)并调用回滚