MySQL-基础篇 ( 事务:相关 + 操作 + ACID + 并发问题 + 隔离级别 )

发布时间 2023-08-17 19:34:42作者: 朱呀朱~

MySQL-基础篇 ( 事务 )

事务简介

  • 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败
  • 典型案例:银行转账,一方转账减钱时,若是中途出了问题,此方的钱数不会减少,而是回滚到转帐前的状态
  • 默认 MySQL 的事务是自动提交的,也就是说,当执行一条 DML 语句,MySQL 会立即隐式的提交事务
    • 即中途出了问题,可能会导致一方钱数减少了,但另一方钱数没有增加

事务操作

方式一

  • 查看事务提交方式

    • 直接输入语句:SELECT @@autocommit;

    image-20230723175854868

    • 1 表示当前事务是自动提交
  • 设置事务提交方式

    • 设置为手动提交:SET @@autocommit = 0;
  • 提交事务

    • 设置为手动提交后,SQL 语句并不会对数据库进行更改,想要提交到数据库需要在操作的最后执行:COMMIT;
    • 即,如果设置为了手动提交,则往后的所有对数据库的操作都要在执行后再 COMMIT; 才行
  • 回滚事务

    • 设置为手动提交后,当事务中途出现错误,就不要再执行 COMMIT 提交了,而是执行:ROLLBACK; 进行回滚事务
    • 因为在报错前的语句还是都执行完成了,所以要回滚让执行过的恢复到初始状态
  • 代码模拟:

    -- 查看事务提交方式
    SELECT @@AUTOCOMMIT;
    -- 设置事务提交方式,1为自动提交,0为手动提交,该设置只对当前会话有效
    SET @@AUTOCOMMIT = 0;
    
    -- 设置完手动提交后:
    select * from account where name = '张三';
    update account set money = money - 1000 where name = '张三';
    update account set money = money + 1000 where name = '李四';
    commit;
    -- 按照需要回滚事务:ROLLBACK;
    

方式二

  • 开启事务

    • 在语句执行之前先执行:START TRANSACTION;BEGIN;
  • 提交事务

    • 同方式一,成功了就继续执行:COMMIT;
  • 回滚事务

    • 同方式一,失败出错了,就不执行 COMMIT;,而是执行:ROLLBACK;
  • 代码模拟:

    start transaction;
    select * from account where name = '张三';
    update account set money = money - 1000 where name = '张三';
    update account set money = money + 1000 where name = '李四';
    commit;
    -- 按照需要回滚事务:ROLLBACK;
    

事务四大特性 ( ACID )

  • 原子性 ( Atomicity ):事务是一组操作,是不可分割的最小操作单元,要么全部成功,要么全部失败
  • 一致性 ( Consistency ):事务完成时,必须使所有数据都保持一致状态
  • 隔离性 ( Isolation ):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
  • 持久性 ( Durability ):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的
  • 即 ( 以上述转账为例 ):
    • 原子性 —— 转账过程其中一步失败了,整个操作就失败了
    • 一致性 —— 不管事务成与不成,最后总余额数一致
    • 隔离性 —— 若干个并发事务,事务 A 和事务 B 两者执行互不干扰,都是独立环境下进行的
    • 持久性 —— 不管转账成与不成,最后的对数据库的改变是永久的 ( 数据库数据最终是保存、持久化到磁盘中的 —— 各信息都是在 MySQL 文件夹下以某种格式被保存着 )

并发事务问题

  • 多个并发事务在执行的过程中所出现的并发事务问题:
问题 描述
脏读 一个事务读到另一个事务还没提交 ( 操作没完成 ) 的数据
不可重复读 一个事务先后读取同一条记录,但两次读取的数据不同 ( 其他操作在两次读取的中途对记录进行了更改 )
幻读 一个事务按照条件查询数据时,没有对应的数据行,但是在执行插入数据时,又发现这行数据已经存在 ( 数据被其他事务操作了 ),好像出现了 “ 幻影 ”

事务隔离级别

  • 为了解决在多个并发事务执行的过程中所出现的各种并发事务问题
隔离级别 脏读 不可重复读 幻读
Read uncommitted —— 读未提交 Y Y Y
Read committed —— 读已提交 N Y Y
Repeatable Read ( 默认 ) —— 可重复读 N N Y
Serializable —— 串行化 N N N
  • MySQL 默认的隔离级别是 Repeatable Read,而 Orcale 默认的就是 Read committed 了

    • Y 表示会出现,N 表示该问题不会出现
      • 从上到下隔离级别越高,但性能也就越差
        • 即 Read uncommitted 是效率最高的,但安全性是最差的
  • 查看事务隔离级别:SELECT @@TRANSACTION_ISOLATION;

    • MySQL 下:

      image-20230723215126281

  • 设置事务隔离级别:SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE};

    • SESSION 是会话级别,表示只针对当前客户端窗口有效,GLOBAL 表示对所有客户端会话窗口有效
    • 大括号内的就是自行设置的隔离级别,不区分大小写
  • 模拟脏读情况:

    • 可打开两个 cmd 命令行窗口模拟两个客户端,两个并发事务

    • 两个窗口,登录一样的用户,同样执行:

      • mysql> user 数据库;

        set ... level read uncommitted;

        start transaction;

    • A 窗口读两次一个数据,B 窗口在 A 读两次之间进行对此数据的更改,并在 A 读完再 commit;

      • 就会发现 B 还没提交,A 两次的读取就不一样了
    • 紧接着将 A 级别进行更改为 read committed 后,重复上述操作,就会发现 A 两次的读取一样了,只有 B 提交后 A 第三次读取才会查到更改后的数据

  • 而上述情况的 A 的第一、二次读取与第三次读取结果不一致,这就是 " 不可重复读 " 的情况了,只有再升一个隔离等级后,就会发现第一、二、三次结构都一致,只有 A commit; 提交后再第四次查询才会发现有了更改 ( B 提交了的更改 )

  • 最后一种幻读情况就不再演示,即 A 查空 id,B 插此空 id 并提交,就会使得 A 无法再插入却还读不到的 " 幻读 " 问题

    • 提高等级后解决方法就是,A 开启事务查后,B 无法执行任何语句,只有等 A 插、查操作等都完成了,commit; 提交了,B 才能继续执行语句 —— 规避了 " 幻读 "