Mysql数据库事务的脏读幻读及不可重复读详解


Posted in MySQL onMay 30, 2022

一、什么是数据库事务

数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。——百度百科

比如,你有2条sql要执行,如果放到一个事务里,要么2个sql都执行成功,要么都失败。都执行成功了就提交事务,有一个失败了就回滚,不存在一个成功一个失败。

二、事务的ACID原则

这是数据库事务的核心所在。

1. 原子性(Atomicity)

比如现在A有800元,B有200元,A给B转账200元。完成此场景有2步,可以当做在一个事务里:

1- A:800-200=600
2- B:200+200=400

那么,这2个步骤只能都成功,或者都失败。如果一个成功一个失败了,那么有一个人的钱就不对了。原子性就是表示不能只发生其中一个动作。

2. 一致性(Consistency)

针对一个事务操作前与操作后的状态一致。

比如现在A有800元,B有200元,2个人总计是1000元。那么不管这2个人之间怎么转来转去,总和一定还是1000元,钱不会凭空产生或消失。

3. 持久性(Durability)

对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。

比如现在A有800元,B有200元,此时A要给B转账200,或有2种情况:

1. 事务还没提交,这时候服务挂了或者断电,那么重启数据库后,数据状态应该为:A有800元,B有200元

2. 事务已经提交,这时候服务挂了或者断电,那么重启数据库后,数据状态应该为:A有600元,B有400元

可以看到,事务一旦提交,就会持久化到数据库里,不会因外界原因导致数据丢失。

4. 隔离性(Isolation)

事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。

比如现在有2个事务同时进行,A和C同时在给B转账:

事务一:A有800元,B有200元,A给B转账200元

事务二:C有1000元,B有200元,C给B转账100元

这2个事务不会互相影响。隔离性就是针对多用户同时操作的情况下,排除其他事务对本事务的影响。

三、隔离带来的问题

数据库的事务隔离级别有4个,强度从低到高依次为:
Read uncommitted 、Read committed 、Repeatable readSerializable,而随着隔离级别的不同,会引发一些其他的问题。

1. 脏读

一个事务读取了另外一个事务未提交的数据,就是脏读。

事务1: A给B转账500,但是事务未提交。

事务2: B查看了账户,发现A转过来500,本来只转300过来就好,发现多转了200,心里美滋滋。。。

事务1: A及时发现多转了200,修改了转300,提交事务。

最终,B再次查看账户的时候发现还是只多了300块,白高兴一场,这种就是脏读。当隔离级别设置为Read uncommitted时可能会出现该情况。
若避开脏读,可以设置隔离级别为Read committed

2. 不可重复读

一个事务先后读取同一条记录,而事务在两次读取之间该数据被其它事务所修改,则两次读取的数据不同,这种就是不可重复读。

事务1:B去买东西,卡里有500块钱,消费100,还没提交事务。

事务2:B的老婆把B的500块钱转出去了,已提交事务。

事务1:B此时提交事务,支付不了。再次读取发现卡里没钱支付。

当隔离级别设置为Read committed,可以避免脏读,但是可能会造成不可重复读。
若避开不可重复读,可以设置隔离级别为Repeatable read

3.幻读

一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为幻读。

事务1:B的老婆查看B的卡消费记录,目前共消费了500元。

事务2:B此时刚在外面请朋友吃完饭,付款了100,事务已提交。

B的老婆决定把账单打印出来,晚上跟B对账,却发现打印出来的消费为600元。她刚才明明看到是500,怎么是600,难道是幻觉?

Mysql的默认隔离级别为Repeatable read,可以避免不可重复读,但是可能出现幻读的情况。

如果要继续解决幻读,那么可以将隔离级别设置为最高级的Serializable,这时候事务都是按照顺序执行的,脏读、幻读、不可重复度都可以避免,但是性能很差。

四、手动测试下事务的过程

可以在mysql里手动去执行事务提交的过程,辅助理解。现在来模拟一个转账的过程,A给B转账500。

先创造下测试条件,造库、表、数据。

-- 创建库
CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci;
-- 使用库
USE shop;
-- 创建表
CREATE TABLE `account`(
	`id` INT(3) NOT NULL AUTO_INCREMENT,
	`name` VARCHAR(30) NOT NULL,
	`money` DECIMAL(9,2) NOT NULL,
	PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
-- 插入数据
INSERT INTO account(`name`,`money`)
VALUES ('A', 2000.00),('B', 10000.00)

上述sql都执行完即可,现在有2条测试数据。

Mysql数据库事务的脏读幻读及不可重复读详解

接下来手动执行事务提交的过程。

  • 关闭自动提交
SET autocommit = 0; -- 关闭自动提交,默认是打开

执行sql。

  • 开启一个事务
START TRANSACTION -- 开启一个事务

执行sql。

  • 定义事务里的sql
    开启事务后的sql,就是定义在一个事务里了。
UPDATE account SET money=money - 500 WHERE `name` = 'A' -- A减去500
UPDATE account SET money=money - 500 WHERE `name` = 'B' -- B增加500

执行后,数据变更。A加了500,B少了500。

Mysql数据库事务的脏读幻读及不可重复读详解

现在我不去提交,进行回滚。

ROLLBACK; -- 回滚

数据变回最开始的样子。

Mysql数据库事务的脏读幻读及不可重复读详解

重新执行2条sql,并且提交事务。

UPDATE `shop`.`account` SET `money`=`money` - 500 WHERE `name` = 'A'; -- A减去500
UPDATE `shop`.`account` SET `money`=`money` + 500 WHERE `name` = 'B'; -- B增加500
COMMIT; -- 提交事务

数据修改成功,此时再次执行回滚,数据已经不可逆了。

Mysql数据库事务的脏读幻读及不可重复读详解

以上就是Mysql数据库事务的脏读幻读及不可重复读详解的详细内容!


Tags in this post...

MySQL 相关文章推荐
MySQL 角色(role)功能介绍
Apr 24 MySQL
如何用Navicat操作MySQL
May 12 MySQL
Mysql中 unique列插入重复值该怎么解决呢
May 26 MySQL
如何自己动手写SQL执行引擎
Jun 02 MySQL
低版本Druid连接池+MySQL驱动8.0导致线程阻塞、性能受限
Jul 01 MySQL
MySQL 1130异常,无法远程登录解决方案详解
Aug 23 MySQL
浅谈MySQL表空间回收的正确姿势
Oct 05 MySQL
教你使用VS Code的MySQL扩展管理数据库的方法
Jan 22 MySQL
MySQL优化常用的19种有效方法(推荐!)
Mar 17 MySQL
MySQL中rank() over、dense_rank() over、row_number() over用法介绍
Mar 23 MySQL
MySQL创建表操作命令分享
Mar 25 MySQL
MySQL中优化SQL语句的方法(show status、explain分析服务器状态信息)
Apr 09 MySQL
mysql字段为NULL索引是否会失效实例详解
May 30 #MySQL
MYSQL如何查看操作日志详解
sql查询语句之平均分、最高最低分及排序语句
May 30 #MySQL
mysql5.5中文乱码问题解决的有用方法
深入理解MySQL中MVCC与BufferPool缓存机制
MYSQL事务的隔离级别与MVCC
详解Mysql数据库平滑扩容解决高并发和大数据量问题
You might like
PHP 5.3.1 安装包 VC9 VC6不同版本的区别是什么
2010/07/04 PHP
PHP序列号生成函数和字符串替换函数代码
2012/06/07 PHP
PHP中单引号与双引号的区别分析
2014/08/19 PHP
PHP超级全局变量【$GLOBALS,$_SERVER,$_REQUEST等】用法实例分析
2019/12/11 PHP
php设计模式之抽象工厂模式分析【星际争霸游戏案例】
2020/01/23 PHP
[原创]站长必须要知道的javascript广告代码
2007/05/30 Javascript
语义化 H1 标签
2008/01/14 Javascript
jQuery EasyUI API 中文文档 - ProgressBar 进度条
2011/09/29 Javascript
基于socket.io和node.js搭建即时通信系统
2014/07/30 Javascript
Bootstrap创建可折叠的组件
2016/02/23 Javascript
js实现截图保存图片功能的代码示例
2017/02/16 Javascript
AngularJS中$http使用的简单介绍
2017/03/17 Javascript
bootstrap-table实现服务器分页的示例 (spring 后台)
2017/09/01 Javascript
微信小程序如何调用json数据接口并解析
2019/06/29 Javascript
JavaScript实现的滚动公告特效【基于jQuery】
2019/07/10 jQuery
微信小程序自定义顶部组件customHeader的示例代码
2020/06/03 Javascript
Vue利用localStorage本地缓存使页面刷新验证码不清零功能的实现
2020/09/04 Javascript
JavaScript中展开运算符及应用的实例代码
2021/01/14 Javascript
对Python字符串中的换行符和制表符介绍
2018/05/03 Python
python docx 中文字体设置的操作方法
2018/05/08 Python
python获取微信小程序手机号并绑定遇到的坑
2018/11/19 Python
pandas删除指定行详解
2019/04/04 Python
pytorch之ImageFolder使用详解
2020/01/06 Python
用纯CSS3实现网页中常见的小箭头
2017/10/16 HTML / CSS
eDreams澳大利亚:预订机票、酒店和度假产品
2017/04/19 全球购物
电影T恤、80年代T恤和80年代服装:TV Store Online
2020/01/05 全球购物
医学院毕业生自荐信范文
2014/03/06 职场文书
大一新生学期自我评价
2014/04/09 职场文书
学雷锋树新风演讲稿
2014/05/10 职场文书
户籍证明书标准模板
2014/09/10 职场文书
土木工程毕业答辩开场白
2015/05/29 职场文书
惊涛骇浪观后感
2015/06/05 职场文书
军训结束新闻稿
2015/07/17 职场文书
Windows安装Anaconda3的方法及使用过程详解
2021/06/11 Python
Python使用OpenCV实现虚拟缩放效果
2022/02/28 Python
java版 联机五子棋游戏
2022/05/04 Java/Android