• MySQL进阶(再论事务)——什么是事务 & 事务的隔离级别 & 结合MySQL案例详细分析


    在这里插入图片描述

    前言

    MySQL最为最流行的开源数据库,其重要性不言而喻,也是大多数程序员接触的第一款数据库,深入认识和理解MySQL也比较重要。

    本篇博客阐述MySQL的事务的定义和特性,原子性,一致性,隔离性,持久性;然后结合实际的案例详细分析了事务的隔离级别:序列化(SERIALIZABLE)、可重复读(REPEATABLE READ)、提交读(READ COMMITTED)、未提交读(READ UNCOMMITTED)。

    本系列文章合集如下:

    【合集】MySQL的入门进阶强化——从 普通人 到 超级赛亚人 的 华丽转身

    在这里插入图片描述

    引出


    1.事务(TRANSACTION)是一个不可分割的逻辑单元,包含了一组数据库操作命令,并且把所有的命令作为一个整体向系统提交,要么都执行、要么都不执行;

    2.隔离级别和脏读、不可重复读以及幻象读的对应关系如下:

    隔离级别脏读不可重复读幻读
    READ UNCOMMITTED允许允许允许
    READ COMMITED不允许允许允许
    REPEATABLE READ 【默认的隔离级别】不允许不允许允许
    SERIALIZABLE不允许不允许不允许

    3.在MySQL数据库中,默认的事务隔离级别是REPEATABLE READ 可重复读;

    一、什么是事务

    事务(TRANSACTION)是一个不可分割的逻辑单元,包含了一组数据库操作命令,并且把所有的命令作为一个整体向系统提交,要么都执行、要么都不执行。

    二、事务的特性

    事务必须具备以下四个属性,简称ACID 属性

    1、原子性(Atomicity)
    原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。
    比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败。

    2、一致性(Consistency)
    事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

    换一种方式理解就是:事务按照预期生效,数据的状态是预期的状态。
    比如账户A和账户B两者的余额加起来总共是5000,那么不管A和B之间如何转账,转账几次,事务结束后两个账户的钱相加起来最终还是5000。

    3、隔离性(Isolation)
    事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间,要相互隔离。
    在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同,分别是:未授权读取,授权读取,可重复读取和串行化。

    4、持久性(durability)
    一旦事务提交,那么数据的状态就会被永久的保存到数据库中。
    即使发生系统崩溃或机器宕机等故障,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束的状态。

    三、事务的使用步骤

    1、开始事务

    BEGIN ;START TRANSACTION;
    
    • 1
    • 2

    2、提交事务

    COMMIT ;
    
    • 1

    3、回滚(撤销)事务

    ROLLBACK ;
    
    • 1

    五、SQL实战

    使用事务有两种方式,分别为显式事务隐式事务

    1、显式事务

    (1)创建account表,并插入测试数据

    在这里插入图片描述

    create table account(
        id int primary key auto_increment,
        name varchar(20) unique not null,
        balance int
    );
    
    insert into account values(null,'zs',1000);
    insert into account values(null,'lisi',1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    commit之后数据才提交

    在这里插入图片描述

    事物回滚,删除一条数据进行测试

    在这里插入图片描述

    回滚

    在这里插入图片描述

    BEGIN;
    delete from account where name='zs';
    
    ROLLBACK
    
    • 1
    • 2
    • 3
    • 4

    保存点SAVEPOINT

    在事务中创建保存点,方便后续针对保存点进行回滚。一个事务中可以存在多个保存点。

    在这里插入图片描述

    回滚到指定位置

    在这里插入图片描述

    begin;
    delete from account where name='zs';
    savepoint first;
    delete from account where name='lisi';
    
    ROLLBACK TO first;
    COMMIT
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2、隐式事物

    DROP TABLE account;
    create table account(
        id int primary key auto_increment,
        name varchar(20) unique not null,
        balance int
    );
    
    insert into account values(null,'zs',1000);
    insert into account values(null,'lisi',1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (1)在MySQL中执行DML语句,会自动提交事物。

    delete from account where name='zs';
    
    • 1

    在这里插入图片描述

    (2)MySQL中有一个系统变量 autocommit, 可以查看是否开启自动提交事物。

    SHOW VARIABLES LIKE 'autocommit';
    
    • 1

    (3)把系统变量autocommit 的值设置为 OFF ,则会关闭自动提交。

    SET autocommit = OFF;
    #或
    SET autocommit = 0;
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    (4)再次执行DML语句,发现不会自动提交。

    insert into account values(null,'zs',1000);
    
    • 1

    在这里插入图片描述

    (5)此时必须显示的提交事物才能生效

    commit;
    
    • 1

    在这里插入图片描述

    六、事务的隔离级别

    1、理论

    MySQL 中事务的隔离级别一共分为四种,分别如下:

    • 序列化(SERIALIZABLE)
    • 可重复读(REPEATABLE READ)
    • 提交读(READ COMMITTED)
    • 未提交读(READ UNCOMMITTED)
    时刻事务1事务2
    T0读取商品初始库存为2
    T1扣减库存,库存为1
    T2读取商品库存为1
    T3回滚事务,库存为2
    T4扣减库存,提交事务
    T5查询库存为1

    在MySQL数据库中,默认的事务隔离级别是REPEATABLE READ 可重复读

    2、查看事务的隔离级别

    通过如下 SQL 可以查看数据库实例默认的全局隔离级别和当前 session 的隔离级别。

    (1)MySQL8 之前使用如下命令查看 MySQL 隔离级别:

    SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
    
    • 1

    (2)MySQL8 开始,通过如下命令查看 MySQL 默认隔离级别

    SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;
    
    • 1

    在这里插入图片描述

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

    SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    
    • 1

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

    在这里插入图片描述

    注意,如果只是修改了当前 session 的隔离级别,则换一个 session 之后,隔离级别又会恢复到默认的隔离级别,所以我们测试时,修改当前 session 的隔离级别即可。

    七、READ UNCOMMITTED 未提交读

    该事物隔离级别会导致出现脏读、不可重复读、幻读。

    1、准备测试数据

    DROP TABLE account;
    create table account(
        id int primary key auto_increment,
        name varchar(20) unique not null,
        balance int
    );
    
    insert into account values(null,'zs',1000);
    insert into account values(null,'lisi',1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    2、脏读

    一个事务读到另外一个事务还没有提交的数据,称之为脏读。具体操作如下

    会话A

    START TRANSACTION;
    UPDATE account set balance = balance -1 WHERE name = 'lisi'
    
    • 1
    • 2

    会话B

    SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;
    
    SELECT * FROM account
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

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

    这就是脏读问题。

    3、不可重复读

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

    在这里插入图片描述

    --  会话A
    SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    START TRANSACTION;
    SELECT * FROM account WHERE name ='lisi';
    SELECT SLEEP(10);
    SELECT * FROM account WHERE name ='lisi';
    COMMIT;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    --  会话B
    SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    
    START TRANSACTION;
    UPDATE account SET balance =  balance -500 WHERE name ='lisi';
    COMMIT;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    和脏读的区别在于,脏读是看到了其他事务未提交的数据,而不可重复读是看到了其他事务已经提交的数据

    (由于当前 SQL 也是在事务中,因此有可能并不想看到其他事务已经提交的数据)。

    4、幻读

    是指当事务不是独立执行时发生的一种现象。

    幻象读和不可重复读非常像,看名字就是产生幻觉了。

    在这里插入图片描述

    --  会话A
    SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    START TRANSACTION;
    SELECT * FROM account;
    SELECT SLEEP(10);
    SELECT * FROM account;
    COMMIT;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    --  会话B
    SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    
    START TRANSACTION;
    insert into account values(null,'wangwu',1);
    COMMIT;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    幻读

    • 幻读说的是存不存在的问题:原来不存在的,现在存在了,则是幻读
    • 不可重复读说的是变没变化的问题:原来是A,现在却变为了B,则为不可重复读

    八、 READ COMMITTED 提交读

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

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

    1、脏读测试–解决

    在这里插入图片描述

    --  会话A
    SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
    START TRANSACTION;
    UPDATE account set balance = balance -1 WHERE name = 'lisi'
    
    • 1
    • 2
    • 3
    • 4
    --  会话B
    SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
    SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;
    
    SELECT * FROM account
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、不可重复度和幻读–未解决

    九、REPEATABLE READ 可重复读【默认】

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

    1、不可重复读测试–解决

    在这里插入图片描述

    --  会话A
    SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
    START TRANSACTION;
    SELECT * FROM account WHERE name ='lisi';
    SELECT SLEEP(10);
    SELECT * FROM account WHERE name ='lisi';
    COMMIT;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    --  会话B
    SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
    SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;
    
    START TRANSACTION;
    UPDATE account SET balance =  balance +500 WHERE name ='lisi';
    COMMIT;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意,REPEATABLE READ 也是 InnoDB 引擎的默认数据库事务隔离级别

    2、幻读–未解决

    十、SERIALIZABLE

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

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


    总结

    隔离级别和脏读、不可重复读以及幻象读的对应关系如下:

    隔离级别脏读不可重复读幻读
    READ UNCOMMITTED允许允许允许
    READ COMMITED不允许允许允许
    REPEATABLE READ 【默认的隔离级别】不允许不允许允许
    SERIALIZABLE不允许不允许不允许
  • 相关阅读:
    CentOS上搭建SVN并自动同步至web目录
    高空作业安全带佩戴识别检测系统
    供应链 | 在线平台的研究与思考(一):销售渠道与模式选择
    Prompt Engineering
    力扣刷题day50|739每日温度、496下一个更大元素 I
    MATLAB编程:逐帧读取视频并转换为图片格式
    R语言探索与分析18-基于时间序列的汇率预测
    springboot 老年人健康保障管理系统毕业设计源码302303
    IIS方式部署项目发布上线
    第一章:前言
  • 原文地址:https://blog.csdn.net/Pireley/article/details/134035940