ISO 和 ANIS SQL 标准制定了四种事务隔离级别的标准,包括了一些具体的规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。隔离级别越低,一般支持更高的并发处理,并且拥有更低的系统开销,也就是意味着事务请求的锁越少或保持锁的时间越短。
SQL 标准定义的四个隔离级别:
- READ UNCOMMITTED(读取未提交内容)
- READ COMMITTED(读取已提交内容)
- REPEATABLE READ(可重复读)
- SERIALIZABLE (串行化)
READ UNCOMMITTED(读取未提交内容)
在 READ UNCOMMITTED 隔离级,所有的事务都可以“看到”未提交事务的执行结果。
在这种隔离级别上,可能会产生比如脏读(Dirty Read)、幻读(Phantom Read)以及不可重复读(None-Repeatable)。
除非有我们知道自己在做什么,并且有能力预估及处理由此产生的问题。
该隔离级别在实际应用中很少,因为其性能并不比其他隔离级别好多少,而别的级别也有很多其他的优势,比如能避免脏读或者幻读等问题。
READ COMMITTED(读取已提交内容)
READ COMMITTED 隔离级表示从事务开始到提交前,所做的任务数据改变都是不可见的,也就是说只能看见已提交事务所做的改变,解决了“脏读”的问题。
但是这种隔离级别依然会有“不可重复读”的可能性,也就是说在事务执行间隙,如果用户运行同一SQL语句两次,看到的结果有可能是不同的。
REPEATABLE READ(可重复读)
REPEATABLE READ 隔离级别解决了 READ COMMITTED 隔离级别导致的问题,确保了同一事务的多个实例在并发读取数据时,会得到相同的结果,从而解决了“不可重复读”的可能性。
但是依旧会出现“幻读”的问题。
SERIALIZABLE (串行化)
SERIALIZABLE 是最高级别的隔离级了,它通过强制事务排序,使并发事务不可能相互冲突,从而解决了“幻读”问题。也就是说, SERIALIZABLE 会在每个读的数据行上加锁,不过这可能会导致大量超时和锁竞争的现象。
下表为 ANSI SQL 隔离级相关的问题
脏读、不可重复读、幻读
了解完事务隔离级别,在不同的级别中会产生不同的问题,比如脏读、不可重复读以及幻读。
所以我们需要再明确下他们各自的定义是什么?
脏读
什么是脏读?
我们读取的数据是处于事务过程中产生的更新数据,而这个更新数据并未提交。
比如,我们正在执行一个事务,并且对数据进行了修改,而执行提交之前,另一个事务也访问了这个数据,并且使用了这个数据。
这里就会产生两种情况,一种是之前事务正常提交,一切相安无事;另一种是之前事务由于某种原因提交失败,事务回滚,而另一个事务正常使用了这个数据,从而产生的结果会出现问题。
不可重复读
不可重复读是出现在两个事务之间的冲突。
事务 T1 读取一个字段,然后事务 T2 更新了这个字段,事务 T1 再次读取这个字段的时候,前后两次的值不同。
一般来说,不可重复读的问题是可以接受的,因为最终读到的是已经提交的数据,本身并不会带来行大的问题。
幻读
幻读是指当用户在读取某一范围的数据行时,另一个事务又在该范围内插入了新行或者删除了其中某行,当用户再读取该范围内的数据行时,会发现新的数据行有所变化,比如数据行数有所不同,新增的行有可能会出现数据不一致等等,就好像出现“幻觉”一样。
在这里,我们需要注意一下“不可重复读”和“幻读”还是有所区别。虽
然都是出现在两个事务数据冲突导致的问题,但是两个还是有所区别的,“不可重复读”重点在于修改,也就是说我们两次读取的内容是不所有同的,而“幻读”重点在于新增或者删除,两次读取出来的数据行数有所不同。
但是,我们再说一个但是,在MySQL 官方文档中将不可重复读和幻读的界限模糊了,将不可重复读的问题定义为幻读。MySQL InnoDB存储引擎默认的事务隔离级别是 READ REPEATABLE,采用了 Next-Key Lock 算法,从而避免了不可重复读和幻读的现象。
第一类更新丢失
A/B 两个事务都对同一数据进行更新,A由于某种原因需要进行事务回滚,把已经提交的事务B的更新数据覆盖了。
这种情况发生的时机是两个事务读取同一数据,一个执行成功一个执行失败,失败回滚将执行成功的事务覆盖为初始值。
第二类更新丢失
类似第一类更新丢失,同样是A/B两个事务对同一数据进行更新,事务A将已提交的事务B的更新数据覆盖了。
这种情况发生的时机是两个事务读取同一数据,都修改成功,最后执行的事务将前一个事务执行成功的数据覆盖。
最后
虽然制定了四种事务隔离级别标准,但是很多数据库厂商是不遵守的,比如 Oracle 数据库就不支持 READ UNCOMMITTED 和 REPEATABLE READ。
另外,绝大部分数据库至少设置成了 READ COMMITTED 级别,比如 Microsoft SQL Server、Oracle。
而MySQL InnoDB 存储引擎默认的事务隔离级别为 READ REPEATABLE。