目录
- 文章声明⭐⭐⭐
- 让我们开始今天的学习吧!
- 事务简介
- 事务操作
- 模拟转账操作
- 开启事务
- 提交事务
- 回滚事务
- 查看/设置事务提交方法
- 实例演示
- 事务四大特性
- 并发事务问题
- 分类
- 事务隔离级别
- 分类
- 查看/设置事务隔离级别
- 实例演示
文章声明⭐⭐⭐
- 该文章为我(有编程语言基础,非编程小白)的 MySQL复习笔记
- 知识来源为 B站UP主(黑马程序员)的MySQL课程视频,归纳为自己的语言与理解记录于此并加以实践
- 此前我已经学习过了MySQL,现在是在复习阶段,所以不是面向小白的教学文章
- 不出意外的话,我大抵会 持续更新
- 想要了解前端开发(技术栈大致有:Vue2/3、微信小程序、uniapp、HarmonyOS、NodeJS、Typescript)与Python的小伙伴,可以关注我!谢谢大家!
让我们开始今天的学习吧!
事务简介
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作当成一个整体,一起向系统提交或撤销操作请求,这意味着这些操作要么同时成功,要么同时失败
最常见的例子就是转账操作:
- 要么成功转账:A的余额-1000并且B的余额+1000
- 要么转账失败:A和B的余额都不变,和转帐前一致
- 不允许出现:A已经转账了1000,但是由于网络问题或者系统问题,B没有收到1000转账,这就造成了数据库的数据错乱
MYSQL默认事务自动提交,也就是说,每当执行一条DML语句时,MYSQL会隐式地提交事务
事务操作
模拟转账操作
模拟Richie转账1000给Taylor(未出错,正常情况),操作示例如下:
mysql> select * from account where name = 'Richie'; # 首先查询Richhie账户余额是否足够1000
+----+--------+-------+
| id | name | money |
+----+--------+-------+
| 1 | Richie | 2000 |
+----+--------+-------+
1 row in set (0.00 sec)mysql> update account set money = money - 1000 where name = 'Richie'; # 将Richie账户余额-1000
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0mysql> update account set money = money + 1000 where name = 'Taylor'; # 将Taylor账户余额+1000
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0mysql> select * from account; # 可以看到数据没问题,转账成功
+----+--------+-------+
| id | name | money |
+----+--------+-------+
| 1 | Richie | 1000 |
| 2 | Taylor | 3000 |
+----+--------+-------+
2 rows in set (0.00 sec)
模拟Richie转账1000给Taylor(出错了),操作示例如下:
mysql> select * from account;
+----+--------+-------+
| id | name | money |
+----+--------+-------+
| 1 | Richie | 2000 |
| 2 | Taylor | 2000 |
+----+--------+-------+
2 rows in set (0.00 sec)mysql> update account set money = money - 1000 where name = 'Richie'; # 将Richie账户余额-1000
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0mysql> update account set money = money + 1000 where name = 'Taylor' # 这里我们模拟程序错误-> 模拟出错。。。;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '模拟出错。。。' at line 2mysql> select * from account; # 可以看到数据出现了错乱的现象,Taylor并未收到1000转账
+----+--------+-------+
| id | name | money |
+----+--------+-------+
| 1 | Richie | 1000 |
| 2 | Taylor | 2000 |
+----+--------+-------+
2 rows in set (0.00 sec)
开启事务
开启事务这个操作,只有在自动提交事务的情形下才能发挥作用
start transaction;
# 或者
begin;
提交事务
commit;
回滚事务
rollback;
查看/设置事务提交方法
mysql> # 查看事务提交方式
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.03 sec)mysql> # 关闭自动事务提交(0关闭,1开启)
mysql> set @@autocommit = 0;
Query OK, 0 rows affected (0.03 sec)mysql> # 开启自动事务提交(0关闭,1开启)
mysql> set @@autocommit = 1;
Query OK, 0 rows affected (0.00 sec)
实例演示
select 语句会自动提交一次事务!!!!!!
mysql> # 关闭自动提交事务
mysql> set @@autocommit = 0;
Query OK, 0 rows affected (0.00 sec)mysql> # 查看原始数据
mysql> select * from account;
+----+--------+-------+
| id | name | money |
+----+--------+-------+
| 1 | Richie | 2000 |
| 2 | Taylor | 2000 |
+----+--------+-------+
2 rows in set (0.00 sec)mysql> # 更改数据
mysql> update account set money = money - 1000 where name = 'Richie';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0mysql> update account set money = money + 1000 where name = 'Taylor';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0mysql> # 再查看数据,此时数据应该还是Richie2000,Taylor2000,只不过因为select语句会自动提交一次事务,所以变成了正常提交过事务后的数据,原表数据应该如下:
+----+--------+-------+
| id | name | money |
+----+--------+-------+
| 1 | Richie | 2000 |
| 2 | Taylor | 2000 |
+----+--------+-------+mysql> select * from account; # select语句自动提交了一次事务,数据变为如下:
+----+--------+-------+
| id | name | money |
+----+--------+-------+
| 1 | Richie | 1000 |
| 2 | Taylor | 3000 |
+----+--------+-------+
2 rows in set (0.00 sec)
模拟出现错误时,回滚事务:
mysql> select * from account; # 先查询原始数据
+----+--------+-------+
| id | name | money |
+----+--------+-------+
| 1 | Richie | 2000 |
| 2 | Taylor | 2000 |
+----+--------+-------+
2 rows in set (0.00 sec)mysql> update account set money = money - 1000 where name = 'Richie'; # Richie转账1000
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0mysql> update account set money = money + 1000 where name = 'Taylor' # 模拟错误-> 模拟出现错误。。。;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '模拟出现错误。。。' at line 2
mysql> rollback; # 回滚事务,以至于上次提交事务之前的所有SQL语句都失效
Query OK, 0 rows affected (0.03 sec)mysql> select * from account; # 再次查询会发现,跟什么事都没发生一样
+----+--------+-------+
| id | name | money |
+----+--------+-------+
| 1 | Richie | 2000 |
| 2 | Taylor | 2000 |
+----+--------+-------+
2 rows in set (0.00 sec)
事务四大特性
- 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败
- 一致性(Consistency):事务完成时,必须使数据全部保持一致状态
- 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
- 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的
并发事务问题
分类
问题 | 描述 |
---|---|
脏读 | 一个事务读到另一个事务还没有提交的数据 |
不可重复读 | 一个事务先后读取同一个记录,但两次读取的数据不同,称之为不可重复读 |
幻读 | 一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了一个幻影 |
事务隔离级别
分类
√:表示会出现这种情况
❌:表示不会出现这种情况
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
read uncommitted | √ | √ | √ |
read committed(Oracle默认) | ❌ | √ | √ |
repeatable read(MySQL默认) | ❌ | ❌ | √ |
serializable(串行化) | ❌ | ❌ | ❌ |
事务隔离级别越高,数据越安全,但是性能就越低
查看/设置事务隔离级别
session指的是更改当前会话的事务隔离级别,global指的是更改全局的事务隔离级别
# 查看事务隔离级别
select @@transaction_isolation;
# 设置事务隔离级别,session指的是更改当前会话的事务隔离级别,global指的是更改全局的事务隔离级别
set [session | global] transaction isolation level {read uncommitted | read committed |repeatable read | serializable};
实例演示
这里因为验证的流程比较复杂,大家可以自行尝试验证
大家可以开启两个终端打开登录mysql,从而来模拟两个主机对数据库同时进行操作: