MySQL数据库事务的四大特性


Posted in MySQL onApril 20, 2022

1 简介

事务的4种隔离级别分别是读未提交(Read Uncommitted)、读已提交(Read Committed)、 可重复读(Repeatable Read)和串行化(Serializable)。

首先,在了解这4种隔离级别前就必须先要了解其前提,也就是事务,本文简单介绍一下关于事务。

之后,我们也要理解这4种隔离级别产生的原因和场景展现以及4种隔离级别是如何解决问题的。

2 什么是数据库事务?

事务由一个有限的数据库操作序列组成,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。

例如一个银行转账场景:

A转账B 100元,A的账号扣除100元,B的账号加上100块。假如中间出现任何异常,例如,在A的账号扣100元时,银行瘫痪,B的账号余额没有发生变化。这时候就需要事务来保证将A的钱还回去。

2.1 事务的四大特性(ACID)

  • 原子性:事务作为一个整体被执行,包含在其中的对数据库的操作要么全部都执行,要么都不执行。
  • 一致性:指在事务开始之前和事务结束以后,数据不会被破坏,假如A账户给B账户转10块钱,不管成功与否,A和B的总金额是不变的。
  • 隔离性:多个事务并发访问时,事务之间是相互隔离的,一个事务不应该被其他事务干扰,多个并发事务之间要相互隔离。
  • 持久性:表示事务完成提交后,该事务对数据库所作的操作更改,将持久地保存在数据库之中。

3 并发事务会导致的问题

  • 脏读:事务 A 读取了事务 B 更新的数据,然后 B 进行回滚操作,那么A读取的数据就是脏数据
  • 不可重复读:事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据做了更新并提交,导致事务A多次夺取同一数据时,结果不一致。
  • 幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

? 不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

3.1 本文会使用到的 SQL 语句

3.1.1 示例表结构

CREATE TABLE `account` (
  `id` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `balance` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `un_name_idx` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3.1.2 查询事务的默认隔离级别

mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+
1 row in set (0.01 sec)

3.1.3 设置当前会话的事务隔离级别

mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)

4 事务的4种隔离级别和示例演示

事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted)
不可重复读(read-committed)又叫读已提交
可重复读(repeatable-read)
串行化(serializable)

4.1 读未提交

事务A:

MySQL数据库事务的四大特性

事务B:

MySQL数据库事务的四大特性

? 读未提交是隔离级别最低的,会造成脏读。

4.2 读已提交

为了避免脏读,数据库有了比读未提交更高的隔离级别,即读已提交

对于提交:当前事务只能读取其它事务已提交的数据,未提交事务的数据读取不到。

事务A:

MySQL数据库事务的四大特性

事务B:

MySQL数据库事务的四大特性

由此可以得出结论,隔离级别设置为已提交读(READ COMMITTED)
时,已经不会出现脏读问题了,当前事务只能读取到其他事务提交的数据。但是,站在事务A的角度想想,存在其他问题吗?

提交读的隔离级别会有什么问题呢?

在同一个事务A里,相同的查询sql,读取同一条记录(id=1),读到的结果是不一样的,即不可重复读。所以,隔离级别设置为read committed的时候,还会存在不可重复读的并发问题。

4.3 可重复读

为了避免不可重复读的并发问题,我们将隔离级别设置为可重复读(REPEATABLEE READ),重复一下之前的操作。

事务A:

MySQL数据库事务的四大特性

事务B:

MySQL数据库事务的四大特性

到了这一步,可以发现事务隔离级别设置为可重复读,可以解决幻读问题。

那么可重复读真的是否已经解决了幻读问题呢?毕竟还剩个事务隔离级别呢。

RR隔离级别下,手动启动一个事务,进行select操作,他会生成一个快照,可以理解为将当前数据库的数据复制一份,在当前事务中,之后不管进行多少次select查询,都是在模板中去取数据,所以不管数据库中是否对数据进行了改变,都不会影响当前事务数据的读取,从而避免了幻读。这种普通的 select 操作,称为快照读

但是如果在当前事务中使用了下图语句进行当前读:

select * from account for update;

for update是进行当前读的操作,他会重新从数据库去加载当前的最新的数据,每执行一次加载一次,如果在此时,另外一个事务为数据库添加了一个事务,再进行查询,会发现查询的数据与之前相比多了或者少了,这也就是幻读现象。

如果你阅读到这里,去实操一下,会发现和我说的不一样,有一种上当的感觉。

其实不是的,这是因为上述都是在标准的可重复读下的情况,在innodb存储引擎中对可重复读进行了改造,为当前读加上了 Next-key Lock,也就是间隙锁和行锁的统称,行锁防止了别的事务修改或者删除,间隙锁防止了别的事务新增。也就是在进行上面的for update事务中,其他的事务不能对数据进行增删操作,执行会报错或者长时间处于等待状态。

? 注意:如果A事务如果进行了快照读,然后通过B事务对数据就行增删,然后紧接着A事务进行当前读操作,两次读取数据不一致,不能算作幻读,因为幻读定义是同一个select语句,快照读和当前读的查询语句是不一样的.

小结

  • 数据库的并发问题有:脏读、不可重复读和幻读;
  • 事务隔离级别依次为:读未提交、读已提交、可重复读和串行化;
  • 在标准的RR下并没有彻底解决幻读,但是在Mysql的innodb引擎中彻底解决了;
  • innodb通过 Next-Key lock解决的幻读问题,其实也就是阻塞串行化了;
  • 不能把快照读和当前读在一个事务中进行比较是否出现幻读,两者不是同一个select,不满足幻读的官方定义。

4.4 串行化

略,这部分我懒得放图了,因为结果和上面没啥差别。

文献引用

一文彻底读懂MySQL事务的四大隔离级别 -

到此这篇关于深入理解MySQL事务的4种隔离级别的文章就介绍到这了!

MySQL 相关文章推荐
数据库的高级查询六:表连接查询:外连接(左外连接,右外连接,UNION关键字,连接中ON与WHERE的不同)
Apr 05 MySQL
MySQL kill不掉线程的原因
May 07 MySQL
mysql在项目中怎么选事务隔离级别
May 25 MySQL
MySQL5.7并行复制原理及实现
Jun 03 MySQL
MySQL 不等于的三种使用及区别
Jun 03 MySQL
mysql如何配置白名单访问
Jun 30 MySQL
MySQL连接控制插件介绍
Sep 25 MySQL
实战 快速定位MySQL的慢SQL
Mar 22 MySQL
MySQL中IO问题的深入分析与优化
Apr 02 MySQL
mysql中如何用命令创建联合唯一索引
Apr 20 MySQL
MySQL 语句执行顺序举例解析
Jun 05 MySQL
MySQL优化之慢日志查询
Jun 10 MySQL
Windows 64位 安装 mysql 8.0.28 图文教程
分析MySQL优化 index merge 后引起的死锁
解决MySQL报“too many connections“错误
Mysql查询时间区间日期列表,不会由于数据表数据影响
Apr 19 #MySQL
WINDOWS下安装mysql 8.x 的方法图文教程
CentOS MySql8 远程连接实战
Mysql排查分析慢sql之explain实战案例
Apr 19 #MySQL
You might like
php引用地址改变变量值的问题
2012/03/23 PHP
PHP得到mssql的存储过程的输出参数功能实现
2012/11/23 PHP
ThinkPHP进程计数类Process用法实例详解
2015/09/25 PHP
自定义min版smarty模板引擎MinSmarty.class.php文件及用法
2016/05/20 PHP
jQuery判断元素是否存在的可靠方法
2014/05/06 Javascript
jquery中使用循环下拉菜单示例代码
2014/09/24 Javascript
node.js中的fs.appendFile方法使用说明
2014/12/17 Javascript
jQuery中html()方法用法实例
2014/12/25 Javascript
js实现点击文本框显示日期选择器特效代码分享
2020/05/21 Javascript
使用jquery+CSS3实现仿windows10开始菜单的下拉导航菜单特效
2015/09/24 Javascript
理解JS事件循环
2016/01/07 Javascript
JS实现页面中所有img对象添加onclick事件及新窗口查看图片的方法
2016/12/27 Javascript
关于vue.extend和vue.component的区别浅析
2017/08/16 Javascript
Router解决跨模块下的页面跳转示例
2018/01/11 Javascript
Vue 项目分环境打包的方法示例
2018/08/03 Javascript
[00:34]拔城逐梦,热血永恒!2020(秋)完美世界城市挑战赛报名开启
2020/10/09 DOTA
[01:06:54]DOTA2-DPC中国联赛 正赛 RNG vs Dragon BO3 第一场 1月24日
2021/03/11 DOTA
[46:42]DOTA2-DPC中国联赛正赛 Aster vs Magma BO3 第二场 3月5日
2021/03/11 DOTA
python类继承用法实例分析
2015/05/27 Python
Python采用Django制作简易的知乎日报API
2016/08/03 Python
Python 创建新文件时避免覆盖已有的同名文件的解决方法
2018/11/16 Python
Python叠加两幅栅格图像的实现方法
2019/07/05 Python
Python创建一个元素都为0的列表实例
2019/11/28 Python
css3 box-sizing属性使用参考指南
2013/01/08 HTML / CSS
HTML5在手机端实现视频全屏展示方法
2020/11/23 HTML / CSS
UGG雪地靴德国官网:UGG德国
2016/11/19 全球购物
英国时尚首饰品牌:Missoma
2020/06/29 全球购物
在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern "C"
2014/08/09 面试题
生物技术专业研究生自荐信
2013/09/22 职场文书
社区党务公开实施方案
2014/03/18 职场文书
农村改厕实施方案
2014/03/22 职场文书
我有一个梦想演讲稿
2014/05/05 职场文书
青年志愿者先进事迹
2014/05/06 职场文书
2015年中学团委工作总结
2015/07/22 职场文书
《悬崖边的树》读后感2篇
2019/12/02 职场文书
nginx配置之并发频次限制
2022/04/18 Servers