前言
本文的主要内容来源于《A Critique of ANSI SQL Isolation Levels》,文中图片同样来自此篇论文。
ANSI SQL92标准规定了四种隔离级别,read uncommitted、read committed、repeatable read、serializable isolation。随着数据库的发展,这已经越来越跟不上数据库的发展,不同厂家的数据库在相同的隔离级别下,在细微处可能表现出不同的行为,比如同为read committed,oracle、DB2和sybase的表现行为就不同。
为了便于用户甄别不同厂家的数据库定义的级别及其行为模式之间的联系和区别,《A Critique of ANSI SQL Isolation Levels》这篇论文提出希望修改标准以正确区分不同厂家数据库的行为。很可惜最后没有成功。
ANSI标准
下图是ANSI SQL标准规定的事务隔离级别和有害现象的关系。
ANSI标准规定了三种有害现象,脏读、模糊读(不可重复读)、幻读。并通过这三种有害现象区分出了不同的隔离级别。
脏读: 读到未提交事务修改的数据;
模糊读(不可重复读):事务在执行过程中两次读取同一部分数据,得到的结果不一致。
幻读:事务在执行过程中,两次执行同一条件的查询语句,发现第二次读的数据比第一次要多。
模糊读(不可重复读)和幻读最大的区别就是模糊读(不可重复读)只需要锁住已经读过的数据就可以了,而幻读需要对还不存在的数据(或者是尚未满足条件的数据)做出预防。
多提一句,只有serializable isolation是完全符合ACID标准的,其他三种情况,都不能完全避免不一致的情况。
所有隔离级别
不同厂家的数据库产品支持的隔离级别名称略有不同;
SYBASE支持的隔离级别:degree 0(read uncommitted)、degree 1(read committed)、degree 2(repeatable read)、degree 3(serializable isolation);
ORACLE支持的隔离级别:read committed(consistent read)、serializable(snapshot isolation);
DB2支持的隔离级别:read uncommitted、cursor stability、read stability、repeatable read;
Postgresql支持的隔离级别:read committed(consistent read)、repeatable read(snapshot isolation)、serializable isolation(Serialaizable Snapshot Isolation);
SQL Server支持的隔离级别:read uncommitted、read committed snapshot 、read committed 、repeatable read、snapshot isolation、serializable isolation;
MySQL支持的隔离级别:read uncommitted、read committed(consistent read)、repeatable read(snapshot isolation)、serializable isolation;
上面的列表中,前面是数据库产品中隔离级别的名字,括号内的是实际对应的隔离级别。隔离级别的细微差别主要是MVCC机制和锁机制实现不同导致的,其中,
SQL Server既支持锁机制,又支持MVCC机制,因此事实上实现了六种隔离级别;
MySQL既支持MVCC机制,又使用锁机制实现了基于锁机制的serializable isolation;
Postgresql使用SSI实现了serializable isolation,这个实现和传统的通过锁机制实现的serializable isolation是不同的,其主要区别在于传统锁机制是first-lock-win,没有拿到锁的事务会等待,而PG的SSI机制是first-commit-win,后提交的不符合串行化要求的串行化事务会报错回滚,而且SSI机制会导致一定的误回滚,不过SSI机制是无阻塞的。
隔离级别之间的关系
1,四个基本的事务隔离级别就不多说了,和之前是一样的。
2,Consistent Read和read committed之间的区别主要在cursor,Consisten Read的cursor无论数据如何修改,读到的数据是不会改变的,即使修改数据的事务已经提交,而Read Committed级别的事务会读到提交事务修改后的数据,DB2的Cursor Stability通过对cursor扫过的数据加锁,实现了同样的功能,但因为会产生阻塞,所以性能不如Consistent Read。
Consistent Read还有一个重要的优点,就是如果存在一个修改数据的事务在一个长查询(select)内启动并提交,那么处于Read Committed和Cursor Stability级别的长查询可能既读到事务修改之前的数据,又读到事务修改之后的数据,这对很多聚集函数有很严重的伤害。Consistent Read通过快照(snapshot)可以严格保证仅仅读到语句开始前提交的事务修改过的数据。
3,Snapshot Isolation屏蔽了脏读,模糊读(不可重复读),幻读,但是仍然不是串行化隔离级别。如下
Tx1:
update test1 set id=id+1;
select * from test2;
Tx2
update test2 set id=id+1;
select * from test1;
这两个事务如果均在snapshot isolation下,并且并行执行,因为R(tx1)<W(tx2),R(tx2)<W(tx1),所以无论如何不能等等价成一个串行的执行顺序,这在对数据一致性要求严格的场景下可能会造成很大问题,比如银行。
4,Repeatable Read不会发生如上(3)的不一致情况,但无法屏蔽幻读,它和Snapshot Isolation各有处理不了的情况,通常而言,从ANSI SQL92的标准来看,还是Snapshot Isolation更通用一些。
附表:
identifier | query | phenomena |
P0 | w1[x]...w2[x]...((c1 or a1) and (c2 or a2) in any order) | Dirty Write |
P1 | w1[x]...r2[x]...((c1 or a1) and (c2 or a2) in any order) | Dirty Read |
P2 | r1[x]...w2[x]...((c1 or a1) and (c2 or a2) any order) | Fuzzy / Non-Repeatable Read |
P3 | r1[P]...w2[y in P]...((c1 or a1) and (c2 or a2) any order) | Phantom |
P4 | r1[x]...w2[x]...w1[x]...c1 | Lost Update |
P4C | rc1[x]...w2[x]...w1[x]...c1 | Lost Update |
A3 | r1[P]...w2[y in P]...c2....r1[P]...c1 | Phantom |
A5A | r1[x]...w2[x]...w2[y]...c2...r1[y]...(c1 or a1) | Read Skew |
A5B | r1[x]...r2[y]...w1[y]...w2[x]...(c1 and c2 occur) | Write Skew |