在讲解 Mysql 的隔离级别与事务之前,先要了解一下概念:什么是 脏读、幻读、不可重复读?
假如有表 User 中数据如下
id | name | age | sex |
---|---|---|---|
1 | Jack | 25 | 男 |
2 | Rose | 23 | 女 |
脏读 不可重复读 幻读
脏读
产生原因
脏读又称无效数据的读出,是指在数据库访问中,事务 A 将某一值修改,然后事务 B 读取该值,此后事务 A 因为某种原因撤销对该值的修改,这就导致了事务 B 所读取到的数据是无效的。 一般是针对于 update 操作的。
时间顺序 | 事务A | 事务B |
---|---|---|
1 | 开始事务 | |
2 | Update user set age = 30 where id = 1 | |
3 | 开始事务 | |
4 | select age from user where id = 1 查询出年龄为 30 (脏读) | |
5 | RollBack | |
6 | 结束事务 | |
7 | 此处进行业务操作 例如将年龄加一 | |
8 | Update user set age = 31 where id = 1 | |
9 | 提交事务 |
按照正确逻辑,上述操作最终年龄应为 25+1 = 26
解决方案
知道了脏读产生的原因,如果要避免脏读,可以在操作数据的时候对数据加行锁,禁止其它事务对数据进行读取和修改即可
不可重复读
前后多次读取,数据内容不一致。针对于 update 操作。
产生原因
事务A先执行读操作,由于整个事务A比较大,前后多次读取同一条数据需要经历很长的时间 ,而在事务A两次读取数据中间的过程中,有事务B对数据执行更改操作,造成的事务A两次读取数据不一致,即不可重复读。
时间顺序 | 事务A | 事务B |
---|---|---|
1 | 开始事务 | |
2 | select age from user where id = 1 (查询出年龄为25) | |
3 | 开始事务 | |
4 | 其它操作 | |
5 | update user set age = 23 where id = 1 | |
6 | 提交事务 | |
7 | select age from user where id = 1 (查询出年龄为23) | |
8 | 其它操作 | |
9 | 结束事务 |
按照正确逻辑,事务 A 前后两次读取到的数据应该一致
解决方案
对数据加行锁,禁止其它事务修改该行数据即可。
幻读
幻读是指当事务不是独立执行时发生的一种现象。
产生原因
事务A先读取与搜索条件相匹配的若干行,由于整个事务A比较大,前后多次读取同一组数据需要经历很长的时间,而在事务A两次读取数据中间的过程中,有事务B对事务A的结果集执行新增或者删除行的操作,造成的事务A两次读取数据总量不一致,仿佛产生了幻觉一样,即幻读。一般是针对 insert 和 delete 操作。
时间顺序 | 事务A | 事务B |
---|---|---|
1 | 开始事务 | |
2 | select count(*) from user where id < 5 (查询结果为2) | |
3 | 开始事务 | |
4 | 其它操作 | |
5 | delete from user where id = 2 | |
6 | 提交事务 | |
7 | select count(*) from user where id < 5 (查询结果为1) | |
8 | 其它操作 | |
9 | 结束事务 |
按照正确逻辑,事务 A 前后两次读取到的数据总量应该一致
解决方案
使用表级锁,锁定整张表,事务A结束后才释放该锁,这个时候才允许其他事务新增或删除数据。
三者区别
不可重复读和脏读的区别是:脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
Mysql 的隔离级别与事务
关于隔离级别的理解
1.read uncommitted 读未提交
可以看到未提交的数据(脏读)。
举个例子:别人说的话你都相信了,但是可能他只是说说,并不实际做。
2.read committed 读已提交
读取提交的数据。但是,可能多次读取的数据结果不一致(不可重复读,幻读)。
用读写的观点就是:读取的行数据,可以写。
3.repeatable read 可重复读(MySQL 默认隔离级别)
可以重复读取,但有幻读。
读写观点:读取的数据行不可写,但是可以往表中新增数据。
在 MySQL innodb 中,采用锁和多版本并发控制(MVCC)机制解决了幻读问题。
4.serializable 可串行化
可读,不可写。像 Java 中的锁,写数据必须等待另一个事务结束。
各隔离级别是否会产生脏读、不可重复读、幻读
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 read uncommitted | ✅ | ✅ | ✅ |
读已提交 read committed | ❌ | ✅ | ✅ |
可重复读 repeatable read | ❌ | ❌ | ✅ |
可串行化 Serializable | ❌ | ❌ | ❌ |
事务的特性 ACID
事务在英文中是 transaction,和现实世界中的交易很类似,它有如下四个特性:
A (Atomicity) 原子性
原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功的条件是事务里的所有操作都成功,只要有一个操作失败,整个事务就失败,需要回滚。
比如银行转账,从A账户转100元至B账户,分为两个步骤:
1)从A账户取100元;
2)存入100元至B账户。
这两步要么一起完成,要么一起不完成,如果只完成第一步,第二步失败,钱会莫名其妙少了100元。
C (Consistency) 一致性
一致性也比较容易理解,也就是说数据库要一直处于一致的状态,事务的运行不会改变数据库原本的一致性约束。
例如现有完整性约束a+b=10,如果一个事务改变了a,那么必须得改变b,使得事务结束后依然满足a+b=10,否则事务失败。
I (Isolation) 独立性
所谓的独立性是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响。
比如现在有个交易是从A账户转100元至B账户,在这个交易还未完成的情况下,如果此时B查询自己的账户,是看不到新增加的100元的。
D (Durability) 持久性
持久性是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失。
查看与设置隔离级别
1.查看当前会话隔离级别
select @@tx_isolation; 或者 show variables like ‘%tx_isolation%’
2.查看系统当前隔离级别
select @@global.tx_isolation;
3.设置当前会话隔离级别
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
set session transaction isolation level serializable;
4.设置系统当前隔离级别
set global transaction isolation level repeatable read;
5.命令行,开始事务时
set autocommit=off 或者 start transaction / begin