MySQL 事务隔离机制

  • 隔离性与隔离级别
  • 四个案例看懂 MySQL 事务隔离级别
    • 查看隔离级别
    • READ_UNCOMMITTED
      • 脏读
      • 不可重复读
      • 幻读
    • READ_COMMITTED
    • REPEATABLE_READ
    • SERIALIZABLE
  • 事务隔离的实现
  • 总结

提到事务,大家肯定不陌生。最经典的例子就是银行转账。

比如,A 账户给 B 账户转账 100。在这种交易的过程中,有几个问题值得思考:

  • 如何同时保证上述交易中,A 账户总金额减少 100,B账户总金额增加 100?
  • A 账户如果同时在和 C 账户交易(T2),如何让这两笔交易互不影响?
  • 如何在支持大量交易的同时,保证数据的合法性(没有钱凭空产生或消失) ?
  • 如果交易完成时数据库突然崩溃,如何保证交易数据成功保存在数据库中?

要保证交易正常可靠地进行,数据库就得解决上面的四个问题,这也就是事务诞生的背景,它能解决上面的四个问题。

简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在MySQL 中,事务支持是在引擎层实现的。

我们知道,MySQL 是一个支持多引擎的系统,但并不是所有的引擎都支持事务。

比如 MySQL 原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被 InnoDB 取代的重要原因之一。

下面,配合实例,我们分析 InnoDB 存储引擎在事务支持方面对于隔离性的实现。

隔离性与隔离级别

我们知道事务的四个特性:ACID(AtomicityConsistencyIsolationDurability,即原子性、一致性、隔离性、持久性)。

当数据库上有多个事务同时执行的时候,就可能会出现问题:

  • 脏读(dirty read)
  • 不可重复读(non-repeatable read)
  • 幻读(phantom read)

为了解决这些问题,就有了隔离级别的概念。

在谈隔离级别之前,我们首先要知道,隔离的越严实,效率就会越低。因此很多时候,我们都要在二者之间寻找一个平衡点。

SQL标准的事务隔离级别包括

  • 读未提交read uncommitted):一个事务还没提交时,它做的变更就能被别的事务看到;
  • 读提交read committed):一个事务提交以后,它做的变更才会被其它事务看到;
  • 可重复读repeatable read):一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其它事务也是不可见的;
  • 串行化serializable):对于同一行记录,「写」会加「写锁」,「读」会加「读锁」。当出现读写锁冲突的时候,后访问的事务必须等待前一个事务执行完成,才会继续执行。

在 MySQL 数据库中,默认的事务隔离级别是 RR。

四个案例看懂 MySQL 事务隔离级别

查看隔离级别

MySQL8 之前的查询命令是:

SELECT @@GLOBAL.tx_isolation, @@tx_isolation;

MySQL8 开始查询命令是:

SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;


根据上图,可以看到,默认的隔离级别为 REPEATABLE-READ,「全局隔离级别」和「当前会话隔离级别」是相同的。

我们可以通过如下命令修改隔离级别(建议在修改时,仅修改当前 session 隔离级别即可,不用修改全局的隔离级别):

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

上面这条 SQL 表示,仅把当前 session 的数据库隔离级别设置为 READ UNCOMMITTED,设置成功后,再次查询隔离级别,发现当前 session 的隔离级别已经变了,如图:

注意,这里只是修改了当前 session 的隔离级别,换一个 session 之后,隔离级别又会恢复到默认的隔离级别,如果使用的是 Navicat 的话,不同的查询窗口就对应了不同的 session

READ_UNCOMMITTED

READ UNCOMMITTED 是最低隔离级别,这种隔离级别中存在脏读、不可重复读以及幻读问题

我们通过这个隔离级别,搞懂这三个问题到底是怎么回事。

建表语句:

CREATE TABLE account (id INT NOT NULL AUTO_INCREMENT,name VARCHAR (50) NOT NULL,balance BIGINT NOT NULL,PRIMARY KEY (id),UNIQUE KEY idx_name (name)
) ENGINE = INNODB

预设两条数据,如下: zhangsanzhaowu 两个用户,两个人的账户各有 1000 块。

下面通过模拟这两个用户之间的一个转账操作,借此分析这三个问题到底是怎么回事。

脏读

一个事务读到另外一个事务还没有提交的数据,称之为脏读。

具体操作如下:

  1. 首先打开两个 SQL 操作窗口,假设分别为 A 和 B,在 A 窗口中输入如下几条 SQL (输入完成后不用执行):
START TRANSACTION;
UPDATE account set balance=balance+100 where name='zhangsan';
UPDATE account set balance=balance-100 where name='zhaowu';
COMMIT;
  1. 在 B 窗口执行如下 SQL,修改默认的事务隔离级别为 READ UNCOMMITTED,如下:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
  1. 接下来在 B 窗口中输入如下 SQL,输入完成后,首先执行第一行开启事务(注意只需要执行一行即可):
START TRANSACTION;
SELECT * FROM ACCOUNT;
COMMIT;
  1. 接下来执行 A 窗口中的前两条 SQL,即开启事务,给 zhangsan 这个账户添加 100 元。
  2. 进入到 B 窗口,执行 B 窗口的第二条查询 SQL(SELECT * FROM ACCOUNT;),结果如下:

可以看到,A 窗口中的事务,虽然还未提交,但是 B 窗口中已经可以查询到数据的相关变化了。

这就是脏读问题。

不可重复读

不可重复读是指一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。

具体操作步骤如下(操作之前先将两个账户的钱都恢复为1000):

  1. 首先打开两个查询窗口 A 和 B ,并且将 B 的数据库事务隔离级别设置为 READ UNCOMMITTED
  2. 在 B 窗口中输入如下 SQL,然后只执行前两条 SQL 开启事务并查询 zhangsan 的账户:
START TRANSACTION;
SELECT * FROM ACCOUNT WHERE NAME='zhangsan';
COMMIT;

  1. 在 A 窗口中执行如下 SQL,给 zhangsan 这个账户添加 100 块钱,如下:
START TRANSACTION;
update account set balance=balance+100 where name='zhangsan';
COMMIT;
  1. 再次回到 B 窗口,执行 B 窗口的第二条 SQL 查看 zhangsan 的账户,结果如下:

zhangsan 的账户已经发生了变化,即前后两次查看 zhangsan 账户,结果不一致,这就是不可重复读

不可重复读和脏读的区别在于脏读是看到了其它事务未提交的数据,而不可重复读是看到了其它事务已经提交的数据(由于当前 SQL 也是在事务中,因此有可能并不想看到其它事务已经提交的数据)。

幻读

幻读和不可重复读非常像。幻读指的,一个事务里面,后一个请求看到的比之前相同请求看到的,多了记录出来。 幻读仅专指「新插入的行」。

我来举一个简单例子。

在 A 窗口中输入如下 SQL:

START TRANSACTION;
insert into account(name,balance) values('wangliu',1000);
COMMIT;

然后在 B 窗口输入如下 SQL:

START TRANSACTION;
SELECT * from account;
delete from account where name='wangliu';
COMMIT;

执行步骤如下:

  1. 首先打开两个查询窗口 A 和 B ,并且将 B 的数据库事务隔离级别设置为 READ UNCOMMITTED
  2. 执行 B 窗口的前两行,开启一个事务,同时查询数据库中的数据,此时查询到的数据只有 zhangsanzhaowu
  3. 执行 A 窗口的前两行,向数据库中添加一个名为 wangliu 的用户,注意不用提交事务。
  4. 执行 B 窗口的第二行,由于脏读问题,此时可以查询到 wangliu 这个用户。
  5. 执行 B 窗口的第三行,去删除 name 为 wangliu 的记录,这个时候删除就会出问题,虽然在 B 窗口中可以查询到 wangliu,但是这条记录还没有提交,是因为脏读的原因才看到了,所以是没法删除的。此时就产生了幻觉,明明有个 wangliu,却无法删除。

这就是幻读

看了上面的案例,大家应该明白了脏读不可重复读以及幻读各自的含义了。

READ_COMMITTED

READ UNCOMMITTED 相比,READ COMMITTED 主要解决了脏读的问题,对于不可重复读和幻读则未解决

将事务的隔离级别改为 READ COMMITTED 之后,重复上面关于脏读案例的测试,发现已经不存在脏读问题了;重复上面关于不可重复读案例的测试,发现不可重复读问题依然存在。

上面那个案例不适用于幻读的测试,我们换一个幻读的测试案例。

继续两个窗口 A 和 B,将 B 窗口的隔离级别改为 READ COMMITTED,然后在 A 窗口输入如下测试 SQL:

START TRANSACTION;
insert into account(name,balance) values('wangliu',1000);
COMMIT;

在 B 窗口输入如下测试 SQL:

START TRANSACTION;
SELECT * from account;
insert into account(name,balance) values('wangliu',1000);
COMMIT;

执行步骤如下:

  1. 首先执行 B 窗口的前两行 SQL,开启事务并查询数据,此时查到的只有 zhangsan 和 zhaowu 两个用户。
  2. 执行 A 窗口的前两行 SQL,插入一条记录,但是并不提交事务。
  3. 执行 B 窗口的第二行 SQL,由于现在已经没有了脏读问题,所以此时查不到 A 窗口中添加的数据。
  4. 执行 B 窗口的第三行 SQL,由于 name 字段唯一,因此这里会无法插入。此时就产生幻觉了,明明没有 wangliu 这个用户,却无法插入 wangliu。

REPEATABLE_READ

READ COMMITTED 相比,REPEATABLE READ 进一步解决了不可重复读的问题,但是幻读则未解决。

REPEATABLE READ 中关于幻读的测试和上一小节基本一致,不同的是第二步中执行完插入 SQL 后记得提交事务。

由于 REPEATABLE READ 已经解决了不可重复读,因此第二步即使提交了事务,第三步也查不到已经提交的数据,第四步继续插入就会出错

SERIALIZABLE

SERIALIZABLE 提供了事务之间最大限度的隔离,在这种隔离级别中,事务一个接一个顺序的执行,不会发生脏读、不可重复读以及幻读问题,最安全。

如果设置当前事务隔离级别为 SERIALIZABLE,那么此时开启其它事务时,就会发生阻塞,必须等当前事务提交了,其它事务才能开启成功,因此前面的脏读、不可重复读以及幻读问题这里都不会发生。

事务隔离的实现

在 InnoDB 中事务隔离性是由「锁」来实现的

首先说 READ UNCOMMITTED,它是性能最好,也可以说它是最野蛮的方式,但是它压根儿就不加锁,所以根本谈不上什么隔离效果,可以理解为没有隔离。

再来说 SERIALIZABLE。读的时候加共享锁,也就是其它事务可以并发读,但是不能写。写的时候加排它锁,其它事务不能并发写也不能并发读。

最后说 READ COMMITTEDREPEATABLE READ。这两种隔离级别是比较复杂的,既要允许一定的并发,又想要兼顾的解决问题。

为了实现可重复读,MySQL 采用了 MVCC (多版本并发控制) 的方式。

有关锁和MVCC的分析,我们在后面文章进行详细说明。

这里我们只需要知道,MVCC 只在 READ COMMITTEDREPEATABLE READ 这两个隔离级别下工作。

最后,「幻读」InnoDB 通过引入间隙锁的方式解决。

总结

​本文我们分析了并发访问引发的三个问题:

  • 脏读:脏读指的是读到了其它事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了不一定最终存在的数据,这就是脏读。
  • 不可重复读:它对比可重复读,不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其它事务的影响,比如其它事务改了这批数据并提交了。通常针对数据更新(UPDATE)操作
  • 幻读:幻读则是针对数据插入(INSERT)操作来说的

针对这三个问题,SQL 标准定义了四种隔离级别:

  • 读未提交(READ UNCOMMITTED)
  • 读提交 (READ COMMITTED)
  • 可重复读 (REPEATABLE READ)
  • 串行化 (SERIALIZABLE)

从上往下,隔离强度逐渐增强,性能逐渐变差。采用哪种隔离级别要根据系统需求权衡决定,其中,可重复读是 MySQL 的默认级别。

事务隔离其实就是为了解决上面提到的脏读、不可重复读、幻读这几个问题,下面展示了 4 种隔离级别对这三个问题的解决程度。


最后我们简单介绍了下事务隔离的实现。

事务隔离性主要是由「锁」来实现的,为了解决可重复读,MySQL 采用了 MVCC (多版本并发控制) 的方式。

好了,有关事务隔离机制的就先介绍到这了,我们下篇见。

一文读懂 MySQL 事务隔离机制相关推荐

  1. 一文读懂MySQL事务锁、事务级别

    锁 性能分:乐观(比如使用version字段比对,无需等待).悲观(需要等待其他事务) 乐观锁,如它的名字那样,总是认为别人不会去修改,只有在提交更新的时候去检查数据的状态.通常是给数据增加一个字段来 ...

  2. mysql 默认事务隔离级别_一文读懂MySQL的事务隔离级别及MVCC机制

    回顾前文: <一文学会MySQL的explain工具> <一文读懂MySQL的索引结构及查询优化> (同时再次强调,这几篇关于MySQL的探究都是基于5.7版本,相关总结与结论 ...

  3. Mysql事务隔离机制

    SQL隔离机制: 所谓隔离机制,指的是读与写之间的隔离,指的是在多事务并行的时候,A事务的读与B事务的写之间的隔离,也就是说B事务的写对A事务的可见性. 多事务并发运行的时候,同时读写一个数据,可能会 ...

  4. 一文读懂 MySQL Explain 执行计划

    一.前言 上周老周的一个好朋友让我出一篇教你读懂 SQL 执行计划,和我另一位读者反馈的面试题如何排查慢 SQL 的强相关,索性先出一篇一文读懂 MySQL Explain 执行计划.Explain ...

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

    前言 之前分析一个死锁问题,发现自己对数据库隔离级别理解还不够深入,所以趁着这几天假期,整理一下MySQL事务的四大隔离级别相关知识,希望对大家有帮助~ 事务 什么是事务? 事务,由一个有限的数据库操 ...

  6. 一文读懂mysql主从复制机制

    作为一个关系型数据库,MySQL内建地提供数据复制机制,这使得在使用时,可以基于其复制机制实现高可用架构等高级特性,从而使得MySQL无需借助额外的插件或其他工具就具备适用于生产环境.这是MySQL得 ...

  7. 一文读懂三种并发控制机制(封锁、时间戳、有效性确认,大量例子+证明)

    文章目录 并发控制 概述 事务特性 定义 并发控制机制 串行调度和可串行调度 调度 串行调度 可串行化调度 事务和调度的记法 冲突可串行化 冲突 优先图 证明 使用锁的可串行化实现 锁 封锁调度器 两 ...

  8. 上个厕所的功夫,搞懂MySQL事务隔离级别,Java学习视频百度云盘

    | 14 | 朱志鹏 | 男 | 25 | 技术1部 | 5000 | 看小说 | | 19 | 李昂 | 男 | 27 | 技术1部 | 7000 | 看片儿 | ±-±----------±--- ...

  9. 什么是MVCC,一文搞懂MySQL的MVCC机制

    MVCC是什么 MVCC,即Multi-Version Concurrency Control (多版本并发控制).它是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中 ...

  10. 从实际蹲坑中涨姿势之——彻底搞懂Mysql事务隔离级别

    一.业务背景 最近在做一个类似于任务分发的平台,分为任务管理平台和作业机,任务管理平台负责接收作业机的请求,并为作业机分配任务,所有的任务都存在表t_task_info,其使用一个字段task_sta ...

最新文章

  1. SAP WM 业务部门Unplanned工单消耗导致WM层面单据异常问题之分析
  2. 【Leetcode】刷题的开始
  3. 多种IP网络技术的原理和特点
  4. JavaScript---事件详解
  5. 黄聪:走进wordpress 详细说说template-loader.php
  6. Kali Linux与Ubuntu的ssh服务
  7. 20165309 实验三 敏捷开发与XP实践
  8. HttpClient实现通过url下载文件
  9. MongoDB 之聚合函数查询统计
  10. nest.js 使用express需要提供多个静态目录的操作
  11. linux下文件打包、压缩详解
  12. 详细介绍如何在ubuntu20.04中安装ROS系统,超快完成安装(最新版教程)
  13. word中批量修改上角标、下角标
  14. 重温经典,续写传奇,迈巴赫S600改铱银色加铁灰色双拼喷漆
  15. 万里汇WorldFirst人民币提现,1天内到账,太快了!
  16. 冷血格斗场和热血格斗场
  17. 云计算技术基础【12】
  18. 8.千峰教育os与窗口控制与内存修改与语言----自制随堂笔记
  19. python少儿编程008:海龟绘图画出奥运五连环!
  20. 爬虫之scrapy框架的数据持久化存储/保存为scv,json文件

热门文章

  1. 在浏览器中清除缓存和 Cookies
  2. Monero GUI Wallet发送交易源码分析
  3. mysql localhost可以连接,输入ip地址连接访问被拒绝
  4. VS2012工具箱控件
  5. Error in Summary.factor ‘min’ not meaningful for factors
  6. php 教育类,php教育培训网站是哪个
  7. 决策树模型,XGBoost,LightGBM和CatBoost模型可视化
  8. ACM779-兰州烧饼
  9. NYOJ779 兰州烧饼
  10. 普罗米修斯监控mysql与邮件告警