MYSQL的四种事务隔离级别

前言

有师兄说MySQL挺重要的
所以来谈谈我从网上所学习到的MYSQL的事务隔离级别
希望各位能学到东西

事务

事务的定义

事务是由一步或几步数据库操作序列组成逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行。

我举个例子:比如现在A要给B转账100,从数据库的角度看,步骤可以简化成这样的

  1. UPDATE 账户表名 SET A的钱=A的钱-100 WHERE ID=A;
  2. INSERT INTO 转账记录表(转账人,收款人) VALUES('A','B');
  3. UPDATE 账户表名 SET B的钱=B的钱+100 WHERE ID=B;

以上的语句都是抽象出来的
事务的意思就是说
假如发生了一次转账,就会有以上三条SQL语句的执行
但是假如在执行完第一条语句的时候,很不幸断电了
这时候A的账户并不会少100块
因为这是一个事务
要么完整地执行完三条语句
要么就是都失败(也就是所有在这个事务中修改的状态都会返回到开启这个事务前的状态)

事务的特性

事务具有四个特性:

  • 原子性(Atomicity)

    原子性是指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生。

第一点很容易理解,对应事务的定义

  • 一致性(Consistency)

    一致性是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。

事务开始之前和事务结束以后,数据库的完整性约束没有被破坏
这句话很像原子性,但是原子性和一致性的侧重点是不同的
原子性: 关注状态,要么全部成功,要么全部失败,不存在部分成功的状态。
一致性: 关注数据的可见性,中间状态的数据对外部不可见,只有最初状态和最终状态的数据对外可见
以上面那个转账为例,当A给B转账100,事务要做的就是A的钱减少100,新增一条转账记录,B的钱增加100。
对于别的事务,要么看到A给B转账100成功,要么看到A给B转账100失败,并不会看到事务中A的钱减少100这个中间的状态

  • 隔离性(Isolation)

    隔离性是指并发的事务是相互隔离的。即一个事务内部的操作及正在操作的数据必须封锁起来,不被企图进行修改的事务看到 。

还是以A给B转账100为例子,假设此时A有200
当A给B转账100的事务执行到第2条语句的时候(也就是A的钱在此事务中已经减少100,剩下100)
这时候C给A转账100的事务开始执行第一条语句的时候,得到A原始的钱应该是200,而不是100
即在C给A转账100的事务中读取不到A给B转账100的事务的数据(这里说的是这两个事务处于并行的状态)

  • 持续性(Durability)

    持久性是指在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。 即使出现了任何事故比如断电等,事务一旦提交,则持久化保存在数据库中。

这四个特性也简称ACID性。

并发环境下的几种状态

脏读

事务A读取到了事务B未提交的数据

这个怎么理解呢
我们可以列一个表格来表示
假设我们现在有一个表(user)如下所示

初始数据:

id name
1 lwf

并且存在事务A和事务B操作这张表
那么

事务A 事务B 说明
BEGIN BEGIN 开启事务
- INSERT INTO user(id,name) VALUES(2,'lwx') 事务B往user表添加一行
SELECT * FROM user - 事务A查询该表的数据

注意:此时两个事务都没有提交
但是这时候事务A读出来的数据就出现了事务B未提交的数据
结果也就是

id name
1 lwf
2 lwx

这种情况就代表了脏读

我们可以实战代码演示下
为了模拟两个事务,我们开了两个cmd来连接mysql
我们修改当前连接的事务隔离级别(这里的内容是后面要讲的,我们的目的是重现这个场景)
首先两个窗口都执行
set session transaction isolation level read uncommitted; // 设置当前连接为读未提交
set autocommit=0; // 关闭自动commit

先查询下有没有表的数据

右边的cmd插入数据,左边查询,发现查询出了右边没有提交的数据

不可重复读

事务A读取到了事务B已提交的数据

还是这个user表
初始数据:

id name
1 lwf

还是两个事务操作这张表

事务A 事务B 说明
BEGIN BEGIN 开启事务
SELECT * FROM user - 事务A查询该表的数据
- UPDATE user SET name='lwx' WHERE id=1 事务B修改id为1的行的name列为lwx
- COMMIT 事务B提交
SELECT * FROM user - 事务A查询该表的数据

事务A两次查询该表的数据不一样
第一次:

id name
1 lwf

第二次:

id name
1 lwx

这就是不可重复读

我们演示下
初始的数据

我们先查询下数据,看看正不正确

左右两边同时BEGIN,右边的更新数据并提交

左边查询数据,发现数据改变了

幻读

事务A读取到事务B新增的符合事务A查询条件的数据

初始数据:

id name
1 lwf

两个事务操作这张表

事务A 事务B 说明
BEGIN BEGIN 开启事务
SELECT * FROM user WHERE id IN(1,2) - 事务A查询该表id为1或者2的数据
- INSERT INTO user(id,name) VALUES(2,'lwx') 事务B添加了一条数据
- COMMIT 事务B提交
SELECT * FROM user WHERE id IN(1,2) - 事务A查询该表id为1或者2的数据

事务A两次查询该表的数据数量不一样
第一次:

id name
1 lwf

第二次:

id name
1 lwf
2 lwx

这就是所谓的幻读

我们也来演示下

先查询下数据,看是不是正确的

左右两边开启事务,左边先查询,然后右边插入一条数据并提交

左边查询,发现出现右边提交的数据

不可重复读和幻读的区别

有人可能会觉得不可重复读和幻读是是一个道理

不可重复读侧重的是对同一条数据的改变(UPDATE操作)
也就是两次读取到同一条数据的内容不一样

而幻读侧重查询数量的改变(INSERT操作)
也就是两次读取的数据数量不一样

事务隔离级别

mysql有四种事务的隔离级别,默认为可重复读(REPEATABLE READ)

未提交读(READ UNCOMMITTED)

一个事务可以读到一个事务未提交的数据

已提交读(READ COMMITTED)

一个事务可以读到一个事务已提交的数据。可以避免脏读

可重复读(REPEATABLE READ)

一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他事务对已有记录的更新。可以避免脏读,不可重复读。

可串行化(SERIALIZABLE)

事务之间不存在并发,一个事务的运行,会阻塞其他事务的运行。可以避免脏读,不可重复读,幻读。

这四个隔离级别从上往下的并发性越来越差,可串行化意味着丢掉并发性的优势
现实开发中基本用不到最后一个,基本都是使用Mysql默认的就行

后记

这篇文章主要是了解MySQL的四种隔离级别。