ACID
,指的是数据库事务正确执行的四个基本要素:原子性(Atomicity
)、一致性(Consistency
)、隔离性(Isolation
)、持久性(Durability
)。
其中对于我们这些使用数据库而不是开发数据库的人来说,最有必要深入研究的估计就是”隔离性“了。
到了MySQL
等具体的数据库软件,事务的隔离性要求被分为多个等级,下面我们依次深入讲解,各等级要解决和防范的问题,以及它又会带来哪些新的麻烦。
1.写法约定
我们用T1
和T2
来表示下面将要出现的两个事务,它们要执行的动作写法如下:
r1[x]
–T1
读取变量x
的值w2[y=1]
–T2
写入变量y
,值为1
c1
–T1
提交事务a2
–T2
丢弃事务
2.脏写
T1
对变量进行了修改,但在它提交或者丢弃该事务之前,T2
又对这个变量进行了修改。
- {db in state A}
- w1[x=1]
- {db in state B}
- w2[x=2]
- {db in state C}
- a1
假如x
初始值为0
,T1
写入了x=1
,T2
写入了x=2
,此时x
的临时结果为2
,T1
回滚,那么x
回复为初始值0
,那么当T2
提交时x
仍为0
。
又假如说,我们的数据库存在一致性限制x=y
,考虑下面出现的情形:
- w1[x=1]
- w2[x=2]
- w2[y=2]
- c2
- w1[y=1]
- c1
T1
的操作是x=1,y=1
;而T2
的操作是x=2,y=2
,而上面的结果为x=2,y=1
,破坏了数据的一致性限制x=y
。
显然,脏写会直接破坏了事务的原子性和一致性,对于绝大部分关系型数据库来说,即使是最低隔离等级也要保证绝不能发生。
3.脏读(与是否回滚无关)
读取了被其他并行事务修改过但还未提交的变量(跟前面一样,未提交数据统一称为“脏”数据)。
直观上来说,事务2
读取了一个“有问题”的数据,它随着事务1
的回滚将不复存在,如果事务2
基于它做了其他一系列的动作,将有很大可能破坏数据的一致性。
这里再强调一下,即使不发生事务的回滚操作,脏读依然无处不在。
比如说,我们的数据库存在一致性限制x=y
,考虑下面出现的情形:
- w1[x += 100]
- w2[x *= 2]
- w2[y *= 2]
- w1[y += 100]
事务1
想给x
和y
都加100
,事务2
想给x
和y
都乘2
。假如一开始x
和y
的值都等于100
,那么最后x=400
,y=300
。x=y
的一致性限制再次被破坏。
4.不可重复读和读偏斜
不可重复读指的是,某个事务两次读取同一变量,但前后得到的值并不相等,是因为在这两次读取发生的时间内,存在另一个事务提交了对该变量的修改。
它与脏读的重要区别在于,第二次读取的变量是否已经被另一个事务所提交。如果还没提交就是脏读,如果已经提交就是不可重复读。
读偏斜比不可重复读要更宽泛些,比如我们的数据库存在一致性限制a=b
,但出现了下面的情况:
虽然我们的事务1
没有两次读取同一变量,事务1
读取到的a
和b
的值并不相等,违反了一致性限制。因此,我们可以认为不可重复读是读偏斜的一种特殊情况。
5.幻读
某一事务两次执行同一个select
操作,但返回的行集合却不一样了。原理有点像刚才的不可重复读,但这里强调的是select
操作是比单纯地读精确的某一行更加复杂的条件匹配操作,很可能返回多个数据行。
6.MySQL的事务隔离级别
MySQL
提供了4
种事务隔离级别,其中最常用的InnoDB
存储引擎的默认隔离级别为可重复读。
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
已提交读(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |