一卓的博客

怕什么真理无穷,
进一寸有一寸的欢喜。

0%

MySQL 的隔离级别与事务

在讲解 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

请作者喝杯咖啡吧