• 十一、MySql的事务(上)


    一、引入

    (一)CURD不加控制,会有什么问题?

    在这里插入图片描述

    (二)CURD满足什么属性,能解决上述问题?

    1. 买票的过程得是原子的吧
    2. 买票互相应该不能影响吧
    3. 买完票应该要永久有效吧
    4. 买前,和买后都要是确定的状态吧

    二、什么是事务?

    事务就是一组DML语句组成,这些语句在逻辑上存在相关性,这一组DML语句要么全部成功,要么全部失败,是一个整体。MySQL提供一种机制,保证我们达到这样的效果。事务还规定不同的客户端看到的数据是不相同的。

    事务就是要做的或所做的事情,主要用于处理操作量大,复杂度高的数据

    假设一种场景:你毕业了,学校的教务系统后台 MySQL 中,不在需要你的数据,要删除你的所有信息(一般不会:) ), 那么要删除你的基本信息(姓名,电话,籍贯等)的同时,也删除和你有关的其他信息,比如:你的各科成绩,你在校表现,甚至你在论坛发过的文章等。这样,就需要多条 MySQL 语句构成,那么所有这些操作合起来,就构成了一个事务。

    正如我们上面所说,一个 MySQL 数据库,可不止你一个事务在运行,同一时刻,甚至有大量的请求被包装成事务,在向 MySQL 服务器发起事务处理请求。而每条事务至少一条 SQL ,最多很多 SQL ,这样如果大家都访问同样的表数据,在不加保护的情况,就绝对会出现问题。甚至,因为事务由多条 SQL 构成,那么,也会存在执行到一半出错或者不想再执行的情况,那么已经执行的怎么办呢?

    三、事务的特性

    所有,一个完整的事务,绝对不是简单的 sql 集合,还需要满足如下四个属性:

    (一)原子性:

    一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

    (二)一致性:

    在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。

    (三)隔离性:

    数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交( Readuncommitted )、读提交( read committed )、可重复读( repeatable read )和串行化( Serializable )。

    (四)持久性:

    事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

    上面四个属性,可以简称为 ACID 。
    原子性(Atomicity,或称不可分割性)
    一致性(Consistency)
    隔离性(Isolation,又称独立性)
    持久性(Durability)

    四、为什么会出现事务?

    事务被 MySQL 编写者设计出来,本质是为了当应用程序访问数据库的时候,事务能够简化我们的编程模型,不需要我们去考虑各种各样的潜在错误和并发问题.可以想一下当我们使用事务时,要么提交,要么回滚,我们不会去考虑网络异常了,服务器宕机了,同时更改一个数据怎么办对吧?
    因此事务本质上是为了应用层服务的,而不是伴随着数据库系统天生就有的。
    备注:我们后面把 MySQL 中的一行信息,称为一行记录!

    五、事务的版本支持

    在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务, MyISAM 不支持!
    在这里插入图片描述

    六、事务提交方式

    事务的提交方式常见的有两种:

    • 自动提交
    • 手动提交
      查看事务提交方式
    show variables like 'autocommit';
    
    
    • 1
    • 2

    在这里插入图片描述

    • 用 SET 来改变 MySQL 的自动提交模式:
    SET AUTOCOMMIT=0;
    
    • 1

    在这里插入图片描述

    七、事务常见操作方式

    简单银行用户表

    (一)创建测试表

    create table if not exists account(
    id int primary key,
    name varchar(50) not null default '',
    blance decimal(10,2) not null default 0.0
    )ENGINE=InnoDB DEFAULT CHARSET=UTF8;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (二)正常演示 - 证明事务的开始与回滚

    mysql> show variables like 'autocommit'; -- 查看事务是否自动提交。我们故意设置成自
    动提交,看看该选项是否影响begin
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | autocommit | ON |
    +---------------+-------+
    1 row in set (0.00 sec)
    mysql> start transaction; -- 开始一个事务begin也可以,推荐begin
    Query OK, 0 rows affected (0.00 sec)
    mysql> savepoint save1; -- 创建一个保存点save1
    Query OK, 0 rows affected (0.00 sec)
    mysql> insert into account values (1, '张三', 100); -- 插入一条记录
    Query OK, 1 row affected (0.05 sec)
    mysql> savepoint save2; -- 创建一个保存点save2
    Query OK, 0 rows affected (0.01 sec)
    mysql> insert into account values (2, '李四', 10000); -- 在插入一条记录
    Query OK, 1 row affected (0.00 sec)
    mysql> select * from account; -- 两条记录都在了
    +----+--------+----------+
    | id | name | blance |
    +----+--------+----------+
    | 1 | 张三 | 100.00 |
    | 2 | 李四 | 10000.00 |
    +----+--------+----------+
    2 rows in set (0.00 sec)
    mysql> rollback to save2; -- 回滚到保存点save2
    Query OK, 0 rows affected (0.03 sec)
    mysql> select * from account; -- 一条记录没有了
    +----+--------+--------+
    | id | name | blance |
    +----+--------+--------+
    | 1 | 张三 | 100.00 |
    +----+--------+--------+
    1 row in set (0.00 sec)
    mysql> rollback; -- 直接rollback,回滚在最开始
    Query OK, 0 rows affected (0.00 sec)
    mysql> select * from account; -- 所有刚刚的记录没有了
    Empty set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    (二)非正常演示1 - 证明未commit,客户端崩溃,MySQL自动会回滚(隔离级别设置为读未提交)

    -- 终端A
    mysql> select * from account; -- 当前表内无数据
    Empty set (0.00 sec)
    mysql> show variables like 'autocommit'; -- 依旧自动提交
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | autocommit | ON |
    +---------------+-------+
    1 row in set (0.00 sec)
    mysql> begin; --开启事务
    Query OK, 0 rows affected (0.00 sec)
    mysql> insert into account values (1, '张三', 100); -- 插入记录
    Query OK, 1 row affected (0.00 sec)
    mysql> select * from account; --数据已经存在,但没有commit,此时同时查看
    终端B
    +----+--------+--------+
    | id | name | blance |
    +----+--------+--------+
    | 1 | 张三 | 100.00 |
    +----+--------+--------+
    1 row in set (0.00 sec)
    mysql> Aborted -- ctrl + \ 异常终止MySQL
    --终端B
    mysql> select * from account; --终端A崩溃前
    +----+--------+--------+
    | id | name | blance |
    +----+--------+--------+
    | 1 | 张三 | 100.00 |
    +----+--------+--------+
    1 row in set (0.00 sec)
    mysql> select * from account; --数据自动回滚
    Empty set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    (三)非正常演示2 - 证明commit了,客户端崩溃,MySQL数据不会在受影响,已经持久化

    --终端 A
    mysql> show variables like 'autocommit'; -- 依旧自动提交
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | autocommit | ON |
    +---------------+-------+
    1 row in set (0.00 sec)
    mysql> select * from account; -- 当前表内无数据
    Empty set (0.00 sec)
    mysql> begin; -- 开启事务
    Query OK, 0 rows affected (0.00 sec)
    mysql> insert into account values (1, '张三', 100); -- 插入记录
    Query OK, 1 row affected (0.00 sec)
    mysql> commit; --提交事务
    Query OK, 0 rows affected (0.04 sec)
    mysql> Aborted -- ctrl + \ 异常终止MySQL
    --终端 B
    mysql> select * from account; --数据存在了,所以commit的作用是将数据持久
    化到MySQL中
    +----+--------+--------+
    | id | name | blance |
    +----+--------+--------+
    | 1 | 张三 | 100.00 |
    +----+--------+--------+
    1 row in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    (四)非正常演示3 - 对比试验。证明begin操作会自动更改提交方式,不会受MySQL是否自动提交影响

    -- 终端 A
    mysql> select *from account; --查看历史数据
    +----+--------+--------+
    | id | name | blance |
    +----+--------+--------+
    | 1 | 张三 | 100.00 |
    +----+--------+--------+
    1 row in set (0.00 sec)
    mysql> show variables like 'autocommit'; --查看事务提交方式
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | autocommit | ON |
    +---------------+-------+
    1 row in set (0.00 sec)
    mysql> set autocommit=0; --关闭自动提交
    Query OK, 0 rows affected (0.00 sec)
    mysql> show variables like 'autocommit'; --查看关闭之后结果
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | autocommit | OFF |
    +---------------+-------+
    1 row in set (0.00 sec)
    mysql> begin; --开启事务
    Query OK, 0 rows affected (0.00 sec)
    mysql> insert into account values (2, '李四', 10000); --插入记录
    Query OK, 1 row affected (0.00 sec)
    mysql> select *from account; --查看插入记录,同时查看终端B
    +----+--------+----------+
    | id | name | blance |
    +----+--------+----------+
    | 1 | 张三 | 100.00 |
    | 2 | 李四 | 10000.00 |
    +----+--------+----------+
    2 rows in set (0.00 sec)
    mysql> Aborted --再次异常终止
    -- 终端B
    mysql> select * from account; --终端A崩溃前
    +----+--------+----------+
    | id | name | blance |
    +----+--------+----------+
    | 1 | 张三 | 100.00 |
    | 2 | 李四 | 10000.00 |
    +----+--------+----------+
    2 rows in set (0.00 sec)
    mysql> select * from account; --终端A崩溃后,自动回滚
    +----+--------+--------+
    | id | name | blance |
    +----+--------+--------+
    | 1 | 张三 | 100.00 |
    +----+--------+--------+
    1 row in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    (五)非正常演示4 - 证明单条 SQL 与事务的关系

    --实验一
    -- 终端A
    mysql> select * from account;
    +----+--------+--------+
    | id | name | blance |
    +----+--------+--------+
    | 1 | 张三 | 100.00 |
    +----+--------+--------+
    1 row in set (0.00 sec)
    mysql> show variables like 'autocommit';
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | autocommit | ON |
    +---------------+-------+
    1 row in set (0.00 sec)
    mysql> set autocommit=0; --关闭自动提交
    Query OK, 0 rows affected (0.00 sec)
    mysql> insert into account values (2, '李四', 10000); --插入记录
    Query OK, 1 row affected (0.00 sec)
    mysql> select *from account; --查看结果,已经插入。此时可以在查
    看终端B
    +----+--------+----------+
    | id | name | blance |
    +----+--------+----------+
    | 1 | 张三 | 100.00 |
    | 2 | 李四 | 10000.00 |
    +----+--------+----------+
    2 rows in set (0.00 sec)
    mysql> ^DBye --ctrl + \ or ctrl + d,终止终
    端
    --终端B
    mysql> select * from account; --终端A崩溃前
    +----+--------+----------+
    | id | name | blance |
    +----+--------+----------+
    | 1 | 张三 | 100.00 |
    | 2 | 李四 | 10000.00 |
    +----+--------+----------+
    2 rows in set (0.00 sec)
    mysql> select * from account; --终端A崩溃后
    +----+--------+--------+
    | id | name | blance |
    +----+--------+--------+
    | 1 | 张三 | 100.00 |
    +----+--------+--------+
    1 row in set (0.00 sec)
    -- 实验二
    --终端A
    mysql> show variables like 'autocommit'; --开启默认提交
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | autocommit | ON |
    +---------------+-------+
    1 row in set (0.00 sec)
    mysql> select * from account;
    +----+--------+--------+
    | id | name | blance |
    +----+--------+--------+
    | 1 | 张三 | 100.00 |
    +----+--------+--------+
    1 row in set (0.00 sec)
    mysql> insert into account values (2, '李四', 10000);
    Query OK, 1 row affected (0.01 sec)
    mysql> select *from account; --数据已经插入
    +----+--------+----------+
    | id | name | blance |
    +----+--------+----------+
    | 1 | 张三 | 100.00 |
    | 2 | 李四 | 10000.00 |
    +----+--------+----------+
    2 rows in set (0.00 sec)
    mysql> Aborted --异常终止
    --终端B
    mysql> select * from account; --终端A崩溃前
    +----+--------+----------+
    | id | name | blance |
    +----+--------+----------+
    | 1 | 张三 | 100.00 |
    | 2 | 李四 | 10000.00 |
    +----+--------+----------+
    2 rows in set (0.00 sec)
    mysql> select * from account; --终端A崩溃后,并不影响,已经持久化。autocommit
    起作用
    +----+--------+----------+
    | id | name | blance |
    +----+--------+----------+
    | 1 | 张三 | 100.00 |
    | 2 | 李四 | 10000.00 |
    +----+--------+----------+
    2 rows in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    (六)结论

    • 只要输入begin或者start transaction,事务便必须要通过commit提交,才会持久化,与是否设置set autocommit无关。
    • 事务可以手动回滚,同时,当操作异常,MySQL会自动回滚
    • 单sql的本质是事务!
    • 对于 InnoDB 每一条 SQL 语言都默认封装成事务,自动提交。(select有特殊情况,因为MySQL 有 MVCC )
    • 从上面的例子,我们能看到事务本身的原子性(回滚),持久性(commit)
      那么隔离性?一致性?

    (七)事务操作注意事项

    • 如果没有设置保存点,也可以回滚,只能回滚到事务的开始。直接使用 rollback(前提是事务还没有提交)
    • 如果一个事务被提交了(commit),则不可以回退(rollback)可以选择回退到哪个保存点
    • InnoDB 支持事务, MyISAM 不支持事务
    • 开始事务可以使 start transaction 或者 begin
  • 相关阅读:
    【原创】浅谈指针(十三)指向数组的指针
    神经网络算法的基本原理,神经网络算法通俗解释
    守望者的逃离—贪心算法
    【图论 单源最短路】100276. 最短路径中的边
    万字详解C++避坑指南总结
    比较两个DataFrame的Python神器DataComPy
    基于java web的学生考勤带请假管理系统——计算机毕业设计
    Android 12 源码分析 —— 应用层 五(SystemUI的StatusBar类的启动过程和三个窗口的创建)
    基于spring boot的私人健身与教练预约管理系统
    typeof 在TypeScript中和JavaScript中的区别
  • 原文地址:https://blog.csdn.net/weixin_54447296/article/details/133033255