如果事务处于Postgresql中的“可重复读取”隔离级别,事务会看到来自另一个并发事务的插入吗?

时间:2019-03-24 10:49:43

标签: sql postgresql

如果在执行事务A的过程中,并发事务B添加了符合事务A的搜索条件并提交的行,那么当事务A提交时会发生什么?它会包含新行,还是交易失败?

假定它们在Postgresql中以“可重复读取”隔离级别运行。

1 个答案:

答案 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隔离级别上具有更强的保证。它可以防止幻像读取

来自documentation

  

“可重复读”隔离级别仅查看事务开始之前提交的数据;它从不会看到未提交的数据或并发事务在事务执行期间提交的更改。 (但是,该查询的确会看到以前的更新在其自己的事务中执行的效果,即使它们尚未提交。)对于此隔离级别,这比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   因为可重复读取事务开始后,可重复读取事务无法修改或锁定其他事务更改的行。

预计应用程序将重试失败的事务:

  

应用程序收到此错误消息时,应中止当前事务并从头开始重试整个事务。