当前位置: 首页 > news >正文

【MySQL】深入浅出事务:保证数据一致性的核心武器

个人主页:♡喜欢做梦

欢迎  👍点赞  ➕关注  ❤️收藏  💬评论


目录

🍓一、什么是事务?

🍡1.定义

🍡2.事务的ACID特性

🍑原子性(Atomicity)

🍑一致性(Consistency)

🍑隔离性(Isolation)

🍑持久性(Durablity)

🍡3.如何使用事务?

🍠事务的控制的核心语句

🍠事务的使用

🍐回滚的使用

🍐提交的使用

🍐保存点的使用

 🍠自动/手动提交事务

🍎查看事务提交是自动/手动

🍎设置事务自动/手动

🍎手动的使用

🍎手动和自动的区别:

🍓二、事务的隔离级别

🍨1.事务并发执行可能引发的问题

🍉脏读(Dirty Read)

🍉不可重复读(Non-Repeatable Read)

🍉幻读(Phantom Read)

🍨2.查看和设置隔离级别

🍍查看隔离级别

🍍隔离级别

🍊设置隔离级别

🍊隔离级别

🍅读未提交(Read Uncommitted)

🍅读已提交(Read Committed)

🍅可重复读(Repeatable Read)

🍅串行化(Serializable)


🍓一、什么是事务?

🍡1.定义

事务是把一组SQL语句打包成一个整体,在这组SQL语句的执行过程中,要么全部成功,要么全部失败。这组SQL语句可以是一条也可以是多条。

示例:

在生活中,我们经常会转账给别人。以下举例,小王和小明的钱包余额均为200元,这时小王要转账给小明100元,这种操作必须两个要么一起成功,要么一起失败。否则结果可能会导致,小王转账成功了,而小明接收转账失败了,这种情况就是导致金钱丢失。为了避免这种情况,我们就需要将这一组SQL语句打包成一个整体,也就是放在事务中,利用事务的特性性来保证该操作过程要么全部成功,要么全部失败。

什么是事务的特性呢?接下来就要介绍事务的ACID特性了。 

🍡2.事务的ACID特性

🍑原子性(Atomicity)

含义:事务中的所有操作就是一个不可分割的整体,像原子一样。这些操作,要么全部成功,要么全部失败。数据库会记录事务执行前的数据状态,一旦事务执行过程出现失败,就会回滚到原来的初始状态。

🍑一致性(Consistency)

含义:事务执行前后,事务的完整性不会被破坏。事务执行完成之后,保证数据正确并且符合预期。

🍑隔离性(Isolation)

含义:数据库允许多个并发事务同时对数据进行修改和读写,隔离性可以保证多个事务并发执行,并且不相互干扰。

🍑持久性(Durablity)

含义:事务一旦成功提交,就会保存到存储介质中,并永久保存,不论数据库损坏,也不会发生丢失。

🍡3.如何使用事务?

查看MySQl支持的存储引擎

我们在使用事务之前要查看一下,MySQL是否支持事务。在MySQl中支持事务的存储引擎是InnoBD

语法 

show engines;

如图: 

🍠事务的控制的核心语句

语法

-- 开启一个事务
-- 方式1:
start transaction;-- 方式2:
begin;-- 根据执行结果决定提交还是回滚事务
-- 提交事务,使所有操作生效,并对更改持久化
commit;-- 回滚当前事务,撤销所有操作,回到原来的状态
rollback;
  • 开启事务后,所写的SQL语句就包含在事务中,都具有ACID特性;
  • 无论提交事务还是回滚事务,事务都会关闭;
  • commit之后也就是提交之后,事务就不能回滚了。

🍠事务的使用

🍐回滚的使用

含义:回滚是事务撤销修改并结束事务。

 修改数据后回滚到原来状态:

-- 查询数据
mysql> select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |   200 |
|  2 | 小明   |   200 |
+----+--------+-------+
2 rows in set (0.00 sec)-- 开始事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)-- 转账:小王转账100给小明
mysql>  update account set money=money-100 where name='小王';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> update account set money=money+100 where name='小明';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0-- 查询转账后的结果
mysql>  select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |   100 |
|  2 | 小明   |   300 |
+----+--------+-------+
2 rows in set (0.00 sec)-- 回滚,数据回到原来的状态
mysql> rollback;
Query OK, 0 rows affected (0.06 sec)查询数据:
mysql>  select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |   200 |
|  2 | 小明   |   200 |
+----+--------+-------+
2 rows in set (0.00 sec)
🍐提交的使用

含义: 提交是事务的更改生效并结束事务。

 修改数据后提交事务

-- 开始事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)-- 再次执行转账操作
mysql> update account set money=money-100 where name='小王';
Query OK, 1 row affected (0.09 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> update account set money=money+100 where name='小明';
Query OK, 1 row affected (0.04 sec)
Rows matched: 1  Changed: 1  Warnings: 0-- 提交事务
mysql> commit;
Query OK, 0 rows affected (0.00 sec)-- 提交事务后的数据
mysql> select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |   100 |
|  2 | 小明   |   300 |
+----+--------+-------+
2 rows in set (0.00 sec)-- 提交后尝试回滚
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)-- 回滚失败:提交事务后,事务关闭,数据落盘
mysql> select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |   100 |
|  2 | 小明   |   300 |
+----+--------+-------+
2 rows in set (0.00 sec)
  • 事务回滚或提交后,事务关闭。也就是说事务回滚后,提交无效。事务提交后,回滚无效。
  • 回滚会回到数据最初的状态。 

无论我们修改多少次数据,我们使用回滚都会回到最初的状态。如果我们想要数据回滚到某一个状态,我们应该怎么办?这时候我们需要使用保存点 ,有了保存点,当我们回滚时,可以只回滚到某个保存点。

🍐保存点的使用

含义:当回滚时,可以只回滚到某个保存点,而不是最初状态。

语法:

设置保存点

savepoint 保存点名称;

回滚到保存点

rollback to 保存点名称;

释放保存点

release savepoint 保存点名称;

使用: 

-- 开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)-- 转账1:小王向小明转账100
mysql> update account set  money=money-100 where name='小王';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> update account set  money=money+100 where name='小明';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0-- 查询转账后的结果
mysql>  select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |   100 |
|  2 | 小明   |   300 |
+----+--------+-------+
2 rows in set (0.00 sec)-- 设置保存点
mysql> savepoint money1;
Query OK, 0 rows affected (0.00 sec)-- 转账2:小王再次向小明转账50
mysql>  update account set  money=money-50 where name='小王';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> update account set  money=money+50 where name='小明';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0-- 查询转账结果
mysql>  select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |    50 |
|  2 | 小明   |   350 |
+----+--------+-------+
2 rows in set (0.00 sec)-- 返回到第一个保存点的数据结果
mysql> rollback to money1;
Query OK, 0 rows affected (0.00 sec)-- 查看
mysql> select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |   100 |
|  2 | 小明   |   300 |
+----+--------+-------+
2 rows in set (0.00 sec)-- 回滚保存点,事务不会关闭
-- 再次尝试转账,回滚到保存点
mysql> update account set  money=money-50 where name='小王';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> update account set  money=money+50 where name='小明';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |    50 |
|  2 | 小明   |   350 |
+----+--------+-------+
2 rows in set (0.00 sec)mysql>  rollback to money1;
Query OK, 0 rows affected (0.00 sec)-- 回滚到保存点成功
mysql> select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |   100 |
|  2 | 小明   |   300 |
+----+--------+-------+
2 rows in set (0.00 sec)mysql> update account set  money=money-50 where name='小王';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> update account set  money=money+50 where name='小明';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0-- 释放保存点
mysql> release savepoint money1;
Query OK, 0 rows affected (0.00 sec)-- 再次使用保存点失败:保存点释放后不能再使用
mysql> rollback to money1;
ERROR 1305 (42000): SAVEPOINT money1 does not exist-- 回滚到最初状态
mysql> rollback;
Query OK, 0 rows affected (0.04 sec)mysql> select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |   200 |
|  2 | 小明   |   200 |
+----+--------+-------+
2 rows in set (0.00 sec)
  • 在同一事务中,保存点名称是唯一的。如果设置相同名称的保存点名称,后设置的保存点会覆盖先设置的;
-- 开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)-- 转账100
mysql> update account set  money=money-100 where name='小王';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> update account set  money=money+100 where name='小明';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0-- 查询结果
mysql> select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |   100 |
|  2 | 小明   |   300 |
+----+--------+-------+
2 rows in set (0.00 sec)-- 先设置保存点的名称
mysql> savepoint money1;
Query OK, 0 rows affected (0.00 sec)-- 转账50
mysql> update account set  money=money-50 where name='小王';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> update account set  money=money+50 where name='小明';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0-- 查询结果
mysql> select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |    50 |
|  2 | 小明   |   350 |
+----+--------+-------+
2 rows in set (0.00 sec)-- 后设置相同名称的保存点
mysql> savepoint money1;
Query OK, 0 rows affected (0.00 sec)-- 转账25
mysql> update account set  money=money-25 where name='小王';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> update account set  money=money+25 where name='小明';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |    25 |
|  2 | 小明   |   375 |
+----+--------+-------+
2 rows in set (0.00 sec)-- 回滚到保存点money1
mysql> rollback to  savepoint money1;
Query OK, 0 rows affected (0.00 sec)-- 回滚到后设置的保存点位置
mysql> select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |    50 |
|  2 | 小明   |   350 |
+----+--------+-------+
2 rows in set (0.00 sec)
  • 回滚保存点不会关闭事务。

 🍠自动/手动提交事务

默认情况下,MySQL采用自动提交事务模式,也就是说我们执行每个修改操作,比如插入、删除,都会自动开启一个事务并在语句执行完成之后自动提交,发生异常事自动回滚。

🍎查看事务提交是自动/手动

语法

show variables like 'autocommit';
  • autocommmit:系统变量,标识事务是否自动提交。 

示例: 

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    | -- ON表示自动提交开启
+---------------+-------+
1 row in set, 1 warning (0.02 sec)
🍎设置事务自动/手动

设置自动

-- 方式一:
set autocommit=1;
-- 方式二:
set autocommit=ON;

设置手动

方式一:
set autocommit=0;
方式二:
set autocommit=off;

示例:设置手动

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)mysql> set autocommit=OFF;
Query OK, 0 rows affected (0.00 sec)mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set, 1 warning (0.00 sec)
🍎手动的使用
-- 设置手动:
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)-- 不用显示开启事务
-- 转账
mysql> update account set  money=money-100 where name='小王';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> update account set  money=money+100 where name='小明';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0查询
mysql>  select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |   100 |
|  2 | 小明   |   300 |
+----+--------+-------+
2 rows in set (0.00 sec)-- 回滚
mysql> rollback;
Query OK, 0 rows affected (0.05 sec)mysql> select* from account;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 小王   |   200 |
|  2 | 小明   |   200 |
+----+--------+-------+
2 rows in set (0.00 sec)
🍎手动和自动的区别:
  • 自动要开启事务(start transaction/begin),通过commit或rollback结束事务 
  • 手动不用显示开启事务,通过commit或rollback结束事务 

🍓二、事务的隔离级别

事务具有隔离性,前面也有提到,就是控制多个事务并发执行相互影响的机制。事务之间具有不同程度的隔离称为事务的隔离级别。不同的隔离级别可能会引发不同的情况,有脏读、幻读、不可重复读的问题。这里先说一下,什么是脏读、幻读、和不可重复读。

🍨1.事务并发执行可能引发的问题

🍉脏读(Dirty Read)

        一个事务读取另一个未提交事务的数据。例如,你原本打算给一个人转账500元,你还没发出去,你朋友看到了,以为你要转账得金额是500,后面,你又不想只转账,你最后转账了1000。所以对方读的是你还未提交的数据。

🍉不可重复读(Non-Repeatable Read)

        在同一个事务中,多次读取一个事务的某一行数据可能会得到不同的结果。例如,还是刚刚那个朋友,他原本以为的是你要转账得500,结果第二次又去看,看到的是,你给对方发的1000。这个第一次读的结果与第二次不一样。

🍉幻读(Phantom Read)

       在一个事务中,两次执行相同的查询,得到的结果集不同。例如,班上原本只有500个人,后面又新来了一个人。

🍨2.查看和设置隔离级别

在MySQL中InnoDB引擎中事务的隔离级别分为4种,分别有读未提交、读已提交、可重复读、串行化。

🍍查看隔离级别

事务的隔离级别分为全局作用域会话作用域

查看作用域和隔离级别语法

-- 全局作用域
select @@global.transaction_isolation;-- 会话作用域
select @@session.transaction_isolation;

示例:

-- 全局作用域
mysql> select @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| REPEATABLE-READ                |-- 隔离级别默认是可重复读
+--------------------------------+
1 row in set (0.00 sec)-- 会话作用域
mysql> select @@session.transaction_isolation;
+---------------------------------+
| @@session.transaction_isolation |
+---------------------------------+
| REPEATABLE-READ                 |
+---------------------------------+
1 row in set (0.00 sec)
  • 全局级:对新创建的所有会话都采用隔离级别;
  • 会话级:只对当前数据库有效,不会影响其他会话
  • @@表示查看系统变量 ;

🍍隔离级别

🍊设置隔离级别

语法

-- 方式一
set [session|global] transaction isolation level 隔离级别名称|访问模式;-- 方式二
set [session|global] transaction_isolation = '隔离级别名称';-- 方式三
set session|global.transaction_isolation = '隔离级别名称';

示例:

-- 设置为读未提交
-- 方式一
mysql> set transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
-- 方式二
mysql> set transaction_isolation = 'read-uncommitted';
Query OK, 0 rows affected (0.00 sec)
-- 方式三
mysql> set @@session.transaction_isolation = 'read-uncommitted';
Query OK, 0 rows affected (0.00 sec)
  • 后面两种方式遇到空格要用“-”代替; 
  • access_mode(访问模式):只读(read only)、读写(read write,默认)
  • 隔离级别分明四种:读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)、串行化(Serializable)
🍊隔离级别
🍅读未提交(Read Uncommitted)

含义: 允许一个事务读取另一个未提交事务修改的数据,可能出现脏读、不可重复读、幻读的问题。

🍅读已提交(Read Committed)

含义:事务只能读取其他事务已经提交的数据,避免了脏读,但可能出现幻读和不可重复读的问题。

🍅可重复读(Repeatable Read)

含义:在同一个事务中,多次读取同一个数据结果是一致的,避免了脏读和不和重复读,但仍然可能出现幻读的情况。

🍅串行化(Serializable)

 含义:最高的隔离级别,事务只能串行执行,即一个事务完毕后,才能执行另一个事务。可以完全避免脏读、不可重复读、幻读问题,当会极大降低并发性能。例如,你去厕所只有一个坑位,你上完厕所,下一个才能进去。

  •  在安全性方面:读未提交<读已提交<可重复读<串行化。
  • 在并发性能方面:读未提交>读已提交>可重复读>串行化。并发性能是指数据库在同一时刻能够处理多个并发事务的能力。
http://www.xdnf.cn/news/1188343.html

相关文章:

  • 深度解析 noisereduce:开源音频降噪库实践
  • 【影刀RPA_初级课程_我的第一个机器人】
  • LeetCode|Day26|191. 位 1 的个数|Python刷题笔记
  • 像素、视野、光源,都有哪些因素影响测量精度?
  • DSP在CCS中实现双核在线仿真调试及下载的方法(以TMS320F28x为例)
  • 【Redis】 Redis 基础命令和原理
  • 详解力扣高频SQL50题之1193. 每月交易 I【简单】
  • MySQL操作进阶
  • 1. 多线程开发
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 热词评论查询功能实现
  • 机器学习(重学版)基础篇(概念与评估)
  • Qt 远程过程调用(RPC)实现方案
  • 大模型应用班-第2课 DeepSeek使用与提示词工程课程重点 学习ollama 安装 用deepseek-r1:1.5b 分析PDF 内容
  • UniappDay03
  • 高斯数据库触发器实现流水号的
  • 去中心化时代的通信革命:briefing与cpolar技术融合带来的安全范式革新
  • 【Linux系统】理解硬件 | 引入文件系统
  • 机器学习特征工程:特征选择及在医学影像领域的应用
  • 数字孪生映射探索驱动的具身导航!MorphoNavi:面向对象映射的空地机器人导航
  • 2D游戏背景滚动教程(JavaSwing)
  • 机器学习特征工程详解:特征选择与降维(PCA)
  • 数据赋能(336)——技术平台——智能化运营
  • 2.JVM跨平台原理(字节码机制)
  • Java研学-RabbitMQ(二)
  • C++中new和delete的多重面孔:operator new、new operator与placement new解析
  • 初识java
  • Python 程序设计讲义(20):选择结构程序设计——双分支结构的简化表示(三元运算符)
  • Model Control Protocol 三层架构设计,三种传输方式,完成MCP项目构建实现工具调试,多维度评价指标检测多工具多资源调用的鲁棒性和稳健性
  • java面试题(二)
  • 栈----1.有效的括号