本文所有的前提是MySQL的存储引擎是Innodb。
我们知道MySql的默认事务隔离级别是RR(可重复读),那么为什么大多数公司使用MySql时选择的事务隔离级别却是RC(读提交)呢?
一、事务隔离级别
数据库的事务隔离级别有读未提交、读提交、可重复读、串行化。其中在读未提交隔离级别下会存在脏读的问题,串行化执行效率低下,所以一般在读提交和可重复读两种隔离级别下选择。
读提交避免了脏读的现象,但没有解决幻读的问题;而可重复读解决了幻读的问题。
二、幻读
什么是幻读?
在一个事务中,前后两次查询同一范围(相同的查询SQL)的数据,返回的结果不一致,需要注意的是,不一致是指后一次查询的结果有前一次查询结果所没有的数据,也就是有新的数据被插入到这个范围中。而且幻读仅仅针对的是当前读,快照读是不会出现幻读。
看到上面对幻读的说明,一般都会有以下疑问:
两次查询的结果中的数据行是一样的,只是某些行的数据没修改了,比如某一行的字段被修改了,这种也算两次查询的结果不是完全一样,算不算幻读?
什么是当前读和快照读,两者有啥区别?
疑问1的情况不是幻读,是属于脏读,只有在读未提交隔离级别下才会发生。
当前读,指的是读取的是当前最新数据,并且会对读取的记录加锁,加什么样的锁跟事物隔离级别相关,主要包括如下情况:
快照读,指的是单纯的select语句。
怎么解决幻读?
MySql在可重复读的隔离级别下解决了幻读的问题。
首先,看下面这个例子:
1. 首先创建一个t_demo表,并插入几条数据。
2. RC下事务执行的情况
如果在RC隔离级别下,Session B在执行update时会被阻塞直到t4时刻Session A事务提交,而在t3时刻的Session C执行insert时不会被阻塞,导致Session A在t1和t4时刻两次相同的查询条件下查出来的结果是不一样的,这就是在RC隔离级别下出现幻读的情况。
这个例子可以看出RC是可以解决脏读的问题,无法避免幻读的现象,主要原因是在RC隔离级别下,对需要加锁的数据加的是行锁,只能对已经存在的记录行进行加锁,而插入的是一个没有存在的新的记录行。
3. RR下解决幻读
为了解决幻读的问题,引入了间隙锁(Gap Lock)和临键锁(Next-key Lock)。
间隙锁,锁定索引记录之间的间隙,用开区间来表示。比如上面插入的记录就有间隙,(- ∞,1),(1,5),(5,10),(10,15),(15, ∞)。
临键锁,间隙锁和行锁组成临键锁,前开后闭区间来表示。
一般而言,当前读能命中具体的记录则会加行锁,否则会根据间隙情况,加间隙锁,防止出现幻读。具体加锁情况要分为走索引的,不走索引的两大类,具体的加锁情况在这里不展开讨论,
三、为什么很多公司选择mysql的事务隔离级别是RC?
从以下两个方面来看,
1、使用RR事务隔离级别,能避免幻读,但是由于引入间隙锁导致加锁的范围可能扩大,从而会影响并发,还容易造成死锁,因为间隙锁和间隙锁是不冲突的;
2、在大多数业务场景下,事务隔离级别RC基本上能满足业务需求,幻读出现的机率较少;
从够用的角度来看,选择RC隔离级别是可以的。