Python学习之复习MySQL-Day8(事务)

2023-12-18 17:36:37


文章声明???

  1. 该文章为我(有编程语言基础,非编程小白)的 MySQL复习笔记
  2. 知识来源为 B站UP主(黑马程序员)的MySQL课程视频,归纳为自己的语言与理解记录于此并加以实践
  3. 此前我已经学习过了MySQL,现在是在复习阶段,所以不是面向小白的教学文章
  4. 不出意外的话,我大抵会 持续更新
  5. 想要了解前端开发(技术栈大致有: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: 0

mysql> 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: 0

mysql> 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: 0

mysql> 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> 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: 0

mysql> update account set money = money + 1000 where name = 'Taylor';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> # 再查看数据,此时数据应该还是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: 0

mysql> 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,从而来模拟两个主机对数据库同时进行操作:
在这里插入图片描述

文章来源:https://blog.csdn.net/Richieeea/article/details/135061987
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。