一、含义
数据库管理系统中,事务需要满足ACID四个特性。
Atomicity,即原子性,表示一个事务内的所有操作要么全做,要么全不做。
Consistency,即一致性,表示一个事务只能使数据库从一个一致的状态跳转到另一个的一致性的状态,不能破坏诸如主键唯一或者某列的所有值之和为固定值(转账不能影响存款总额)之类的约束。
Isolation,即隔离性,表示两个事务之间互相不影响,就如同两个事务串行一般。
durability,即持久性,表示如果一个事务如何成功了,它的影响是持久的,不会丢失。持久性通过将数据持久化到磁盘上来保证,当然如果磁盘坏掉了数据还是会丢失的,多副本可以极大地避免数据丢失,使可靠性达到更多个9的级别。
数据库经常需要并发同时处理多个事务,让所有相关的事务串行,可以完整地保证ACID特性,但这样TPS会比较低,数据库系统可以说是基本不可用了。因此,为了提高并发度,我们需要降低隔离性要求。根据业务具体需要,我们需要在并发度和隔离级别之间做trade off,通用的DBMS一般都会提供多个隔离级别供用户选用。
二、异常现象
2.1 脏写(dirty write)
有两个事务T1和T2,T1更改了某列x,在T1提交之前,T2随之也更改了x,这就是脏写,因为T1还没提交,T2更改的是T1的中间状态。假如现在T2提交了,T1要回滚,如果回滚到T1开始前的状态,已经提交的T2的操作就全部丢失了,假如不回滚到T1开始前的状态,已经roll back的T1的影响就还存在数据库中。允许这种现象的数据库基本是不可用的,因为其已经不能完成事务roll back了。
2.2 脏读(dirty read)
有两个事务T1和T2,T1更改将列x从0更改为5,在T1提交之前,T2读x进行读取操作,读到T1的中间状态x = 5,假设最终T1 rollback了,而T2又依据T1的中间状态x = 5做一些操作,最终就会出现一些不合理的结果。
2.3 不可重复读(fuzzy read)
有两个事务T1和T2,T1先读了x,然后T2更改了x,然后提交,这时如果T1再次读x,则会出现在一个事务内两次读的x的结果是不一样的。
2.4 幻读(phantom read)
有两个事务T1和T2,T1根据一个condition从table test select满足条件的行,随后T2往table test里insert满足Condition的行或者update不满足条件使其满足条件然后提交,这时如果T1再次通过condition select的话,则会出现一个事务内两次按条件select的结果是不一样的。
2.5 丢失更新(loss of update)
有两个事务T1和T2,T1先读x,读到x = 0,然后T2读x,读到x = 0,接着T1讲x加3,然后提交,接着T1将x加4,然后提交。这样,x的值最终为4,T1的更新丢失了,如果T1和T2串行的话,最终结果会为7。
2.6 读偏斜(read skew)
假如需要满足约束x + y = 10,初始时x = 5, y = 5,事务T1先读到x = 5,然后事务T2将x改为4,y改为6,然后提交,事务T1接着读到y = 6,此时事务T1看到x + y = 11不满足约束x + y = 10。
2.7 写偏斜(write skew)
假如需要满足约束x + y >= 0,初始时x = -3, y = 5,事务T1先读x和y,然后事务T2读x和y,接着事务T2将y改为3,然后提交,接着事务T1将x改为-5,然后提交。最终,x = -5,y = 3不满足约束x + y >= 0。
三、基于锁的隔离级别
锁按照范围、模式、持续时间来分门别类。按照范围可以分为谓词锁(predicate)和行锁(item),谓词锁会将满足一个condition的所有行锁住(包括满足条件的不存在的行),行锁只会锁住特定的一行。按照模式可以分为读锁(read)和写锁(write),读锁和读锁可以共存,读锁和写锁互斥,写锁和写锁互斥(读锁和写锁的优先级,写锁能否抢占读锁)。按照持续时间可以分为短锁(short)和长锁(long),短锁表示当要访问数据时上锁,读写完后马上解锁,长锁表示当要访问数据时加锁,直到事务结束才解锁。两阶段锁(tow-phase lock)表示在事务刚开始时加上需要的读锁和写锁,直到事务结束时才将加上的所有锁一次性释放。两阶段锁可以实现可串行化的隔离级别。基于锁的隔离级别如下表所示:
- Degree 0允许dirty write
- Read Uncommitted允许dirty read
- Read Commited规避了dirty read/write,允许fuzzy read、loss of update、read skew以及write skew
- Cursor Stability规避了loss of update
- Repeatable Read规避了dirty read/write、fuzzy read、loss of update、read skew以及write skew,但会出现幻读
- Serailizable规避了所有异常现象
四、Snapshot Isolation
MVCC维护了多个版本的数据,事务开启的时候会给这个事务指定一个版本号StartTimeStamp,该事务的读事务会读到版本号不大于StartTimeStamp数据,读操作不会阻塞,这样的好处是读和写互不影响可以并发执行。当一个事务要提交时,数据库会给这个事务指定一个CommitTimeStamp,假如有其他事务Tother的CommitTimeStampother在范围[StartTimeStamp, CommitTimeStamp]并且Tother事务跟要提交的事务修改了同一行数据,那么这个事务会abort掉,这称作first-commiter-win原则(即乐观锁)。first-commiter-win原则保证了不会出现dirty write以及loss of update,一个事务内读到的数据是同个版本的数据,保证了不会有fuzzy read、幻读和read skew等现象,但其允许write skew。可以看出Snapshot Isolation跟Repeatable Read的严格程度是没法比较的。
参考资料:
《A Critique of ANSI SQL Isolation Levels》