事务的隔离级别
困难只是暂时的,放弃才是永久的。
—— 罗伯特·舒勒
当多个事务并发执行时可能会遇到 脏读、不可重复读、幻读 的现象,这些现象会对事务的一致性产生不同程度的影响。
- 脏读:读到其它事务未提交的数据
- 不可重复读:前后读取的数据不一致
- 幻读:前后读取的记录数量不一致
这三个现象的严重性排序:
脏读 > 不可重复读 > 幻读
SQL标准提出了四种隔离级别来规避这些现象,隔离级别越高,性能效率就越低:
读未提交(read uncommitted):指一个事务还没提交时,它做的变更就能被其它事务看到。
读提交(read committed):指一个事务提交之后,它做的变更才能被其它事务看到。
可重复读(repeatable read): 指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB引擎的默认隔离级别
串行化(serializable):会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
按隔离水平高低排序如下:
串行化 > 可重复读 > 读已提交 > 读未提交
针对不同的隔离级别,并发事务时可能发生的现象也会不同

| 隔离级别 | 脏读(Dirty Read) | 不可重复读(Non-Repeatable Read) | 幻读(Phantom Read) | 典型应用场景 | 
|---|---|---|---|---|
| READ UNCOMMITTED | ✅ 可能 | ✅ 可能 | ✅ 可能 | 极少使用(数据准确性要求低)。 | 
| READ COMMITTED | ❌ 避免 | ✅ 可能 | ✅ 可能 | Oracle 默认级别,允许不可重复读。 | 
| REPEATABLE READ | ❌ 避免 | ❌ 避免 | ✅ 可能(InnoDB 通过 MVCC 部分避免) | MySQL 默认级别,平衡性能与一致性。 | 
| SERIALIZABLE | ❌ 避免 | ❌ 避免 | ❌ 避免 | 严格一致性需求(如金融交易)。 | 
- InnoDB 在 REPEATABLE READ下通过 MVCC + Next-Key Lock 避免大部分幻读问题。
- 隔离级别越高,并发性能越低(锁竞争增加)。
所以要解决脏读现象,就要升级到 读提交 以上的隔离级别;要解决不可重复读现象,就要升级到 可重复读 的隔离级别,要解决幻读现象不建议将隔离级别升级到 串行化
不同的数据库厂商对SQL标准中规定的4种隔离级别的支持不一样,有的数据库只实现了其中几种隔离级别,我们讨论的MySQL虽然支持4种隔离级别,但是与SQL标准中规定的各级隔离级别允许发生的现象有些出入
MySQL在 可重复读 隔离级别下,可以很大程度上避免幻读现象的发生(注意是很大程度避免,并不是彻底避免),所以MySQL并不会使用 串行化 隔离级别来避免幻读现象的发生,因为使用 串行化 隔离级别会影响性能。
MySQL InnoDB引擎的默认隔离级别虽然是 可重复读 ,但是他很大程度上避免幻读现象(并不是完全解决),解决的方案有两种:
- 针对快照读(普通select 语句),是通过MVCC方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其它事务插入了一条数据,是查询不出来这条数据的,所以就很好避免幻读问题。
- 针对当前读(select ... for update等语句),是通过 next-key lock(记录锁 + 间隙锁)方式解决了幻读,因为当执行 select ... for update 语句的时候,会加上 next-key lock,如果有其它事务在next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好的避免幻读问题。
四种隔离级别具体是如何实现的呢
对于 读未提交 隔离级别的事务来说,因为可以读到未提交事务修改的数据,所以直接读取最新的数据就好了;
对于 串行化 隔离级别的事务来说,通过加读写锁的方式来避免并行访问;
对于 读提交 和 可重复读 隔离级别的事务来说,它们是通过 Read View,它们的区别在于创建Read View的时机不同,读提交 隔离级别是在每个语句执行前都会重新生成一个Read View,而 可重复读 隔离级别是 启动事务 时生成一个 Read View,然后整个事务期间都在用这个 Read View
注意,执行 开始事务 命令,并不意味着启动了事务。在MySQL有两种开启事务的命令,分别是:
- 第一种:begin / start transaction 命令
- 第二种:start transaction with consistent snapshot 命令
这两种开启事务的命令,事务的启动时机是不同的:
- 执行了 begin / start transaction 命令后,并不代表事务启动了。只有在执行这个命令后,执行了第一天select 语句,才是事务真正启动的时机;
- 执行了start transaction with consistent snapshot 命令,就会马上启动事务。
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号