事务的几种情况
脏读
一个事务读取了另一个并行未提交事务写入的数据。
不可重复读
一个事务重新读取之前读取过的数据,发现该数据已经被另一个事务(在初始读之后提交)修改。
幻读
一个事务重新执行一个返回符合一个搜索条件的行集合的查询, 发现满足条件的行集合因为另一个最近提交的事务而发生了改变。
串行化异常
成功提交一组事务的结果与一次运行这些事务的所有可能顺序不一致。
事务隔离性
READ UNCOMMITTED (读取未提交)
最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
在PotgreSql中不支持,且被定义为READ COMMITTED。
READ COMMITTED(读已提交)
读已提交是PostgreSQL中的默认隔离级别。当一个事务运行使用这个隔离级别时,一个查询只能看到查询开始之前已经被提交的数据,而无法看到未提交的数据或在查询执行期间其它事务提交的数据。
REPEATABLE READ(可重复读)
可重复读隔离级别只看到在事务开始之前被提交的数据;它从来看不到未提交的数据或者并行事务在本事务执行期间提交的修改,不过能够看见本身事务中更新,即使它们还没有被提交。
这个级别与读已提交不同之处在于,一个可重复读事务中的查询看到事务中第一个非事务控制语句开始时的一个快照,而不是事务中当前语句开始时的快照。因此,在一个单一事务中的后续SELECT命令看到的是相同的数据,即它们看不到其他事务在本事务启动后提交的修改。
SERIALIZABLE(可串行化)
可串行化隔离级别提供了最严格的事务隔离。这个级别为所有已提交事务模拟序列事务执行;就好像事务被按照序列一个接着另一个被执行,而不是并行地被执行。
PG中事务隔离级别对照表

√
表示有可能
×
表示不可能
×(pg)
表示只在pg数据库中不可能的特性,其它数据库如oracle可能出现。
应用示例
查看当前事务隔离级别
postgres=# show transaction_isolation ;
transaction_isolation
-----------------------
read committed
隔离级别读已提交+不可重复读情况
事务1会话窗口
postgres=# begin;
BEGIN
postgres=# set transaction isolation level read committed ;
SET
postgres=# select * from test;
id | info
----+------
1 | text
事务2会话窗口,更新数据
postgres=# begin;
BEGIN
postgres=# update test set info='a' ;
UPDATE 1
postgres=# commit;
COMMIT
事务1会话窗口查询到更新的数据
postgres=# select * from test;
id | info
----+------
1 | a
隔离级别读已提交+幻读情况
事务2会话窗口,插入新行
postgres=# insert into test values(2,'text');
INSERT 0 1
事务1会话窗口,查询到新插入的行
postgres=# select * from test;
id | info
----+------
1 | a
2 | text
隔离级别可重复读+不会出现幻读情况
事务1会话窗口
postgres=# begin;
BEGIN
postgres=# set transaction isolation level repeatable read;
SET
postgres=# select * from test;
id | info
----+------
3 | a
4 | text
事务2会话窗口,插入新行
postgres=# insert into test values(5,'text');
INSERT 0 1
事务1会话窗口,再查询不到新插入的行
postgres=# select * from test;
id | info
----+------
3 | a
4 | text
隔离级别读已提交+串行化异常情况
事务1会话窗口,全表扫描表
drop table test;
create table test(id int,info text);
insert into test select generate_series(1,10),'text';
begin;
select * from test where id=1;
事务2会话窗口全表扫描表
begin;
select * from test where id=1;
事务1会话窗口插入数据
insert into test values(1,'1');
事务2会话窗口插入数据
insert into test values(2,'2');
此时先提交事务1成功,再提交事务2也成功。出现并行的事务提交,叫做串行化异常。
可隔离级别可串行化+不会出现串行化异常情况
事务1会话窗口,全表扫描表,扫描所有的数据块,并加上了锁SIReadLock
drop table test;
create table test(id int,info text);
insert into test select generate_series(1,10),'text';
begin isolation level serializable;
select * from test where id=1;
事务2会话窗口,全表扫描表,扫描所有的数据块,并加上了锁SIReadLock
begin isolation level serializable;
select * from test where id=1;
事务1会话窗口,插入数据
insert into test values(1,'1');
事务2会话窗口,插入数据
insert into test values(2,'2');
此时最先提交的事务能够成功提交,后提交的事务会报以下错误
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt.
HINT: The transaction might succeed if retried.
因为全表扫描时加载的预锁是表级别的预锁(内存中的一种弱冲突锁, 只在事务结束时判断是否有数据依赖性的冲突),当一个事务对表有变更操作时并且先提交,另一个事务再对这个表做变更的话就会报错,属于串行化的正常事务机制。




