如果在执行事务A的过程中,并发事务B添加了符合事务A的搜索条件并提交的行,那么当事务A提交时会发生什么?它会包含新行,还是交易失败?
假定它们在Postgresql中以“可重复读取”隔离级别运行。
答案 0 :(得分:2)
交易 A 不会失败。它不会看到事务 B 插入的数据。
演示:
create table accounts (id bigserial primary key, balance bigint);
insert into accounts(balance) values (50), (100), (200);
交易 A :
begin transaction isolation level repeatable read;
select * from accounts where balance >= 100;
id | balance
----+---------
2 | 100
3 | 200
(2 rows)
交易 B :
begin;
insert into accounts(balance) values (300);
INSERT 0 1
commit;
交易 A :
select * from accounts where balance >= 100;
id | balance
----+---------
2 | 100
3 | 200
(2 rows)
没有(4, 300)
行。
(在PostgreSQL 11.2上测试)
请注意, PostgreSQL在REPEATABLE READ
隔离级别上具有更强的保证。它可以防止幻像读取。
“可重复读”隔离级别仅查看事务开始之前提交的数据;它从不会看到未提交的数据或并发事务在事务执行期间提交的更改。 (但是,该查询的确会看到以前的更新在其自己的事务中执行的效果,即使它们尚未提交。)对于此隔离级别,这比SQL标准要求的保证要强
来自Table 13.1. Transaction Isolation Levels:
在REPEATABLE READ
隔离级别允许允许进行幻像读取,但
另请参见:
更新
如果声明
update accounts set balance = balance + 30 where balance >= 100;
是最后一笔交易声明 A 发出的,因为从交易 A 的角度来看,只有两行满足{{ 1}}谓词:
balance >= 100
提交后:
update accounts set balance = balance + 30 where balance >= 100;
UPDATE 2
仅此行返回的行
commit;
COMMIT
select * from accounts;
id | balance
----+---------
1 | 50
4 | 300
2 | 130
3 | 230
(4 rows)
语句已更新(不包括事务 B 插入的行)
请注意,如果事务 A 会尝试select * from accounts where balance >= 100;
被另一个已提交的并发事务更改的行,则将失败:
A
update
B
begin transaction isolation level repeatable read;
BEGIN
select * from accounts where id = 1;
id | balance
----+---------
1 | 50
(1 row)
A :
begin;
BEGIN
update accounts set balance = balance + 10;
UPDATE 3
commit;
该错误是预期的。从文档中:
如果第一个更新程序回滚,则其效果将被否定,并且可重复的读取事务可以继续更新原始找到的行。 但是,如果第一个更新程序提交(并且实际上更新或删除了该行,而不仅仅是锁定了该行),则可重复读取的事务将回滚,并显示消息
-- By some logic based on the fact that the balance is 50 we decided to update it to 60 -- balance is updated by committed concurrent transaction update accounts set balance = 60 where id = 1; ERROR: could not serialize access due to concurrent update
因为可重复读取事务开始后,可重复读取事务无法修改或锁定其他事务更改的行。
预计应用程序将重试失败的事务:
应用程序收到此错误消息时,应中止当前事务并从头开始重试整个事务。