隔离级别
下面介绍四种因为事务的并发产生的问题:
脏写
假设银行账户中有100元,
有事务A,B
A事务把余额改为200,但还未提交
这时,B事务把余额改为300,提交,
如果A发生了回滚,那么账户余额回到100元,事务B无效。
脏写就是已经提交的事务的写操作
因为另一个还未提交的事务的回滚而失去效果,请注意这个失效的事务应该是嵌在另一个事务的中间。
脏读
脏读与脏写十分类似,
脏读是因为另一个未完成的事务,发生了回滚,导致已提交的事务读出
的数据无效。
继续银行账户的例子,
如果银行账务中有100元,
有事务AB,
事务A首先开启,修改了账户余额为200,这时,
事务B开启,读到账户余额为200,事务B结束。
然后事务A发生了回滚,200这个数字失效,也就是B读到的是一个无效的数字,我们称之为脏读。
(脏读,脏写都是因为可以看到未提交的数据
,这些未提交的数据后来消失了)
不可重复读
继续之前银行账户的例子,
有事务AB,银行账户中有100元
事务A开启,读出100元,
事务B开启,将100元修改为200元,
这时A又一次读取余额,发现余额竟然是200元。
在一个事务之内,读到了不一致的值
,我们称之为不可重复读。
不可重复读是因为在一个事务两次读操作的中间
有另一个事务进行了写操作。
幻读
与不可重复读十分类似,
但是这次要换一个例子,假设有一个班级的成绩单,其中有10人及格,
开启事务A,读到10人及格,
这时开启事务B,又向成绩表中写入了10条及格的数据,
事务A再次查询及格记录,发现竟然有20人及格。
一个事务两次读取的数据量
不一致,我们称之为幻读。
好像出现了幻觉。
隔离性其实比想象的要复杂。在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。每种存储引擎实现的隔离级别不尽相同。如果熟悉其他的数据库产品,可能会发现某些特性和你期望的会有些不一样(但本节不打算讨论更详细的内容)。读者可以根据所选择的存储引擎,查阅相关的手册。
下面简单地介绍一下四种隔离级别。
READ UNCOMMITTED(未提交读)
有回滚的风险
在READ UNCOMMITTED 级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)
(万一这个未提交的数据后面发生了回滚
,那么这次读的数据就是无效的,即脏数据
)。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED 不会比其他的级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。
READ COMMITTED(提交读)
没有考虑事务发生的先后顺序
大多数数据库系统的默认隔离级别都是READ COMMITTED (但MySQL不是)。READ COMMITTED 满足前面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读(nonrepeatable read)
,(可以看到已经提交的修改,意味着在一个事务的执行过程中,第一次读的数据和第二次读的数据之间可能有另一个事务对这个数据进行了修改并提交,第二次读到的数据就是已经被修改提交的数据)因为两次执行同样的查询,可能会得到不一样的结果。
REPEATABLE READ(可重复读)
REPEATABLE READ 解决了脏读的问题。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读(Phantom Read)的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(Phantom Row)。InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读的问题。本章稍后会做进一步的讨论。
可重复读
是MySQL的默认事务隔离级别。
SERIALIZABLE(可串行化)
SERIALIZABLE 是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说,SERIALIZABLE 会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。