概念
MySQL事务隔离级别是为了解决并发事务互相干扰的问题。MySQL有四种隔离级别:
- 未提交读 READ UNCOMMITTED
- 已提交读 READ COMMITTED
- 可重复读 REPEATABLE READ
- 序列化 SERIALIZABLE
四种隔离级别
未提交读
事务能够读到其他事务未提交的数据,未提交的数据可能会回滚,这个级别读到的数据是脏数据,这个问题成为脏读。
已提交读
事物能读取到已经提交事物的数据,不会有脏读问题。但是在同一个事务内,多次读取同一条数据,由于其他事务的修改,导致前后读取的结果不一致,叫做不可重复读。
可重复读
MySQL的默认隔离级别,可以解决不可重复读问题。但是不能解决幻读问题,幻读是两次查到的数据数量不一致,不可重复读是同一条数据内容不同,幻读是返回的数据添加或删除了数量。
MySQL的InnoDB默认使用多版本并发控制(MVCC,Multi-Version Concurrency Control)实现可重复读。
- 数据库为每个事务维护一个数据快照。
- 事务读取的是事务开始时数据的快照版本。
- 即使其他事务修改了数据,当前事务看到的仍然是它开始的一致视图
- 因为只是对已经存在的数据进行版本快照,新插入的无法提供历史版本,所以导致了幻读。
- MySQL的InnoDB引擎在可重复读隔离级别下通过间隙锁(Gap Lock)和next-key锁额外解决幻读问题。这与SQL规范不同
序列化
事务最高隔离级别,强制事务排序,使其不会发生冲突,解决了脏读、不可重复读和幻读问题,但是执行效率低,真正使用场景不多。
并发事务中的问题
脏读
一个事务读取到另一个事务未提交的数据。
不可重复读
一个事务读取同一条记录两次,得到结果不一致(重点在update和delete),其他事务对数据进行了修改。
幻读
一个事务读取两次,得到的记录条数不一致(重点在insert)。其他事务插入了数据。
关系总结
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读 | 可能 | 可能 | 可能 |
已提交读 | 不可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 可能 |
序列化 | 不可能 | 不可能 | 不可能 |
*注:MySQL InnoDB在REPEATABLE READ下实际防止了幻读
具体使用
查看当前隔离级别
-- 查看全局隔离级别
SELECT @@global.transaction_isolation;
-- 查看当前会话隔离级别
SELECT @@transaction_isolation;
更新隔离级别
-- 更新全局
SET GLOBAL TRANSACTION ISOLATION LEVEL level;
-- 更新当前会话
SET SESSION TRANSACTION ISOLATION LEVEL level;
可用的隔离级别有
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
演示脏读
步骤 | 事务1 | 事务2 | 备注 |
---|---|---|---|
1 | set session transaction isolation level read uncommitted; |
将隔离级别设置成未提交读 | |
2 | start transaction; |
start transaction; |
|
3 | select * from students; |
此时无数据 | |
4 | insert into students values (null,'xieyanjun'); |
||
5 | select * from students; |
事务1可读取到事务2插入的数据 | |
6 | rollback; |
事务2将数据回滚 | |
7 | select * from students; |
无数据 |
演示不可重复读
步骤 | 事务1 | 事务2 | 备注 |
---|---|---|---|
1 | set session transaction isolation level read committed; |
将隔离级别设置成已提交读 | |
2 | insert into students values (null,'xieyanjun'); |
初始化数据 | |
3 | start transaction; |
||
4 | select * from students where id = 1; |
xieyanjun | |
5 | start transaction; |
||
6 | update students set name = 'xieyanjun1' where id = 1; |
||
7 | commit; |
事务2将数据更新并提交 | |
8 | select * from students where id = 1; |
xieyanjun1 |
演示幻读
MySQL的InnoDB引擎在REPEATABLE READ
隔离级别上通过间隙锁(Gap Lock)额外防止了幻读。所以这里无法演示,理论上在标准SQL下,事务一查询出一条数据,当事务二插入数据并提交后,事务一再次查询时会有两条数据,两次查询出结果数量不一致,出现幻读。