事务隔离级别
总结
介绍事务的四种隔离级别。
简介
在文章 并发事务引入的问题:幻读、不可重复读、脏读 中,已经指出:多个事务并发执行的时候,会引发脏读、不可重复读、幻读这些问题。为了避免这些问题,SQL 标准提出了四种隔离级别:
- 分别是读未提交、读已提交、可重复读、串行化,从左往右隔离级别顺序递增,隔离级别越高,也意味着性能越差。
- 隔离级别的主要目的是平衡数据的一致性和系统的并发性。
InnoDB 引擎的默认隔离级别是可重复读。
不同的数据库厂商对隔离级别的支持不一样,有的数据库只实现了其中几种隔离级别;MySQL 支持 4 种隔离级别,但是与 SQL 标准中规定的各级隔离级别允许发生的现象却有些出入。
概念
四种事务隔离级别
- READ-UNCOMMITTED(读取未提交):最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、不可重复读、幻读。
- READ-COMMITTED(读取已提交) :允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
- REPEATABLE-READ(可重复读) :对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- SERIALIZABLE(可串行化):最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,事务之间完全不可能产生干扰,该级别可以防止脏读、不可重复读以及幻读。但是可能导致大量的超时和锁争用,实际应用中很少用到这个隔离级别。
表格对比
隔离级别 | 是否允许脏读 | 是否允许不可重复读 | 是否允许幻读 |
---|---|---|---|
读未提交(Read Uncommitted) | ✅ | ✅ | ✅ |
读已提交(Read Committed) | ❌ | ✅ | ✅ |
可重复读(Repeatable Read) | ❌ | ❌ | ❌ / ✅(取决于数据库实现) |
串行化(Serializable) | ❌ | ❌ | ❌ |
MySQL 的 InnoDB 引擎在“可重复读”级别通过间隙锁(Gap Lock)解决了幻读问题。
举例说明
假设有两个并发的事务,事务 M 修改值,事务 N 进行查询,下面是按照时间顺序执行两个事务的行为:
事务 M | 事务 N |
---|---|
A 本来是 1 | |
begin; | begin; |
update A=2; | |
select A;第 1 次读 A | |
commit; | |
select A;第 2 次读 A | |
commit; | |
select A;第 3 次读 A |
在不同隔离级别下,事务N 执行过程中查询到的值可能会不同,记三次查询的值为 V1~V3:
- 读未提交隔离级别:V1=2,余额 V2、V3 也是 2 ;
- 读已提交隔离级别下:V1=1,V2、V3 都是 2;
- 可重复读隔离级别下:V1、V2 都是 1, V3 =2;
- 串行化隔离级别下:事务 M 提交后,事务 N 才可以执行, V1、V2、V3= 2。
相关语句
- 查看当前会话隔离级别
select @@tx_isolation;
-- MySQL 8.0中只能用以下语句
SELECT @@transaction_isolation;
- 查看系统隔离级别
select @@global.tx_isolation;
- 设置当前会话隔离级别(新的隔离级别会在下一个事务开始的时候生效)
set session transaction isolatin level repeatable read;
- 设置系统隔离级别(新的隔离级别会在下一个事务开始的时候生效)
set global transaction isolation level repeatable read;