【数据库】事务

文章目录



这里是@那我掉的头发算什么
刷到我,你的博客算是养成了😁😁😁

什么是事务?

事务把⼀组SQL语句打包成为⼀个整体,在这组SQL的执⾏过程中,要么全部成功,要么全部失败。这组SQL语句可以是⼀条也可以是多条。来看⼀个转账的例⼦,如图:

在这个例子中涉及到了两个更新操作:

sql 复制代码
# ================更新操作===================
# 张三余额减少100
UPDATE bank_account set balance = balance - 100 where name = '张三';
# 李四余额增加100
UPDATE bank_account set balance = balance + 100 where name = '李四';

如果转账成功,应该有以下结果:

  1. 张三的账⼾余额减少 100 ,变成 900 ,李四的账⼾余额增加了 100 ,变成 1100 ,不能出现张三的余额减少⽽李四的余额没有增加的情况;
  2. 张三和李四在发⽣转账前后的总额不变,也就是说转账前张三和李四的余额总数为1000+1000=2000 ,转账后他们的余额总数为 900+1100=2000 ;
  3. 转账后的余额结果应当保存到存储介质中,以便以后读取;
  4. 还有⼀点需要要注意,在转账的处理过程中张三和李四的余额不能因其他的转账事件⽽受到⼲扰;

事务的ACID特性★★★

事务的ACID特性指的是 Atomicity (原⼦性), Consistency (⼀致性), Isolation (隔离性)和 Durability (持久性)。

• Atomicity (原⼦性):⼀个事务中的所有操作,要么全部成功,要么全部失败,不会出现只执⾏了⼀半的情况,如果事务在执⾏过程中发⽣错误,会回滚( Rollback )到事务开始前的状态,就像这个事务从来没有执⾏过⼀样;

• Consistency (⼀致性):在事务开始之前和事务结束以后,数据库的完整性不会被破坏。这表⽰写⼊的数据必须完全符合所有的预设规则,包括数据的精度、关联性以及关于事务执⾏过程中服务器崩溃后如何恢复;

• Isolation (隔离性):数据库允许多个并发事务同时对数据进⾏读写和修改,隔离性可以防⽌多个事务并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务可以指定不同的隔离级别,以权衡在不同的应⽤场景下数据库性能和安全;

• Durability (持久性):事务处理结束后,对数据的修改将永久的写⼊存储介质,即便系统故障也不会消失

事务最终要实现一致性,一致性是通过原子性,隔离性,持久性实现的。

为什么要使用事务?

事务具备的ACID特性,是我们使⽤事务的原因,在我们⽇常的业务场景中有⼤量的需求要⽤事务来保证。⽀持事务的数据库能够简化我们的编程模型, 不需要我们去考虑各种各样的潜在错误和并发问题,在使⽤事务过程中,要么提交,要么回滚,不⽤去考虑⽹络异常,服务器宕机等其他因素,因此我们经常接触的事务本质上是数据库对 ACID 模型的⼀个实现,是为应⽤层服务的。

只要提交就会被安全的保存,只要回滚就可以回到事务之初。

如何使用事务

语法

sql 复制代码
# 开始⼀个新的事务
START TRANSACTION;
# 或
BEGIN;
# 提交当前事务,并对更改持久化保存
COMMIT;
# 回滚当前事务,取消其更改
ROLLBACK;

无论是提交还是回滚,事务都会结束。

开启⼀个事务,执⾏修改后回滚

准备工作:

先创建一个表,插入两条数据。

回滚之后发现,数据又回到原来没有修改的时候了,即操作被取消了。这就是rollback的使用。

开启⼀个事务,执⾏修改后提交

使用了commit提交了事务之后,事务的操作安全的实现了,相应的操作改变的数据也被保存了。

保存点

在事务执⾏的过程中设置保存点,回滚时指定保存点可以把数据恢复到保存点的状态

rollback to 保存点名 可以指定保存点位置进行回滚,如果直接rollback则会直接将整个事务回滚。

⾃动/⼿动提交事务

• 默认情况下,MySQL是⾃动提交事务的,也就是说我们执⾏的每个修改操作,⽐如插⼊、更新和删除,都会⾃动开启⼀个事务并在语句执⾏完成之后⾃动提交,发⽣异常时⾃动回滚。

• 查看当前事务是否⾃动提交可以使⽤以下语句:

可以通过以下语句设置事务为⾃动或⼿动提交:

sql 复制代码
# 设置事务⾃动提交
mysql> SET AUTOCOMMIT=1; # ⽅式⼀
mysql> SET AUTOCOMMIT=ON; # ⽅式⼆
# 设置事务⼿动提交
mysql> SET AUTOCOMMIT=0; # ⽅式⼀
mysql> SET AUTOCOMMIT=OFF; # ⽅式⼆

注意:

• 只要使⽤ START TRANSACTION 或 BEGIN 开启事务,必须要通过 COMMIT 提交才会持久化,与是否设置 SET autocommit ⽆关。

• ⼿动提交模式下,不⽤显⽰开启事务,执⾏修改操作后,提交或回滚事务时直接使⽤ commit或 rollback

• 已提交的事务不能回滚

其实,我们在正常情况下,每一条语句都默认是一条事务,成功执行就相当于commit,执行失败就相当于自动回滚。

开启手动提交之后,相当于只要没有commit或者rollback,你写的所有的代码都在一个事务里。

事务的隔离性和隔离级别

什么是隔离性

MySQL服务可以同时被多个客⼾端访问,每个客⼾端执⾏的DML语句以事务为基本单位,那么不同的客⼾端在对同⼀张表中的同⼀条数据进⾏修改的时候就可能出现相互影响的情况,为了保证不同的事务之间在执⾏的过程中不受影响,那么事务之间就需要要相互隔离,这种特性就是隔离性。

隔离级别

并发场景下,事务可能出现脏读、不可重复读、幻读等问题。MySQL 通过隔离级别控制并发影响,默认隔离级别为可重复读(Repeatable Read)。

四种隔离级别(从低到高)

读未提交(Read Uncommitted):允许读取未提交的事务数据,可能出现脏读,极少使用。

读已提交(Read Committed):只能读取已提交的事务数据,避免脏读,但可能出现不可重复读(同一事务内多次查询同一数据,结果不一致)。

可重复读(Repeatable Read):同一事务内多次查询结果一致,避免脏读和不可重复读,InnoDB 通过 MVCC 机制实现,是 MySQL 默认级别。

串行化(Serializable):最高隔离级别,事务串行执行,完全避免并发问题,但性能极低,仅用于数据一致性要求极高的场景。

不同隔离级别存在的问题

脏读:

出现在事务的 READ UNCOMMITTED 隔离级别下,由于在读取数据时不做任何限制,所以并发性能很⾼,但是会出现⼤量的数据安全问题,⽐如在事务A中执⾏了⼀条 INSERT 语句,在没有执⾏COMMIT 的情况下,会在事务B中被读取到,此时如果事务A执⾏回滚操作,那么事务B中读取到事务A写⼊的数据将没有意义,我们把这个理象叫做 "脏读"。

不可重复读:

为了解决脏读问题,可以把事务的隔离级别设置为 READ COMMITTED ,这时事务只能读到了其他事务提交之后的数据,但会出现不可重复读的问题,⽐如事务A先对某条数据进⾏了查询,之后事务B对这条数据进⾏了修改,并且提交( COMMIT )事务,事务A再对这条数据进⾏查询时,得到了事务B修改之后的结果,这导致了事务A在同⼀个事务中以相同的条件查询得到了不同的值,这个现象叫"不可重复读"。

幻读:

为了解决不可重复读问题,可以把事务的隔离级别设置为 REPEATABLE READ ,这时同⼀个事务中读取的数据在任何时候都是相同的结果,但还会出现⼀个问题,事务A查询了⼀个区间的记录得到结果集A,事务B向这个区间的间隙中写⼊了⼀条记录并提交,事务A再查询这个区间的结果集时会查到事务B新写⼊的记录得到结果集B,两次查询的结果集不⼀致,这个现象就是"幻读"。

不可重复读针对的是一条数据,幻读针对的是数据集合。

查看和设置隔离级别

事务的隔离级别分为全局作⽤域和会话作⽤域,查看不同作⽤域事务的隔离级别,可以使⽤以下的⽅式:

sql 复制代码
# 全局作⽤域
SELECT @@GLOBAL.transaction_isolation;
# 会话作⽤域
SELECT @@SESSION.transaction_isolation;
# 可以看到默认的事务隔离级别是REPEATABLE-READ(可重复读)
+---------------------------------+
| @@SESSION.transaction_isolation |
+---------------------------------+
| REPEATABLE-READ | # 默认是可重复读
+---------------------------------+
1 row in set (0.00 sec)

设置事务的隔离级别和访问模式,可以使⽤以下语法:

sql 复制代码
# 通过GLOBAL|SESSION分别指定不同作⽤域的事务隔离级别
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level|access_mode;

# 隔离级别
level: {
 REPEATABLE READ # 可重复读
 | READ COMMITTED # 读已提交
 | READ UNCOMMITTED # 读未提交
 | SERIALIZABLE # 串⾏化
}

# 设置全局事务隔离级别为串⾏化,后续所有事务生效,不影响当前事务
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 设置会话事务隔离级别为串⾏化,当前会话后续的所有事务⽣效,不影响当前事务,可以在任何时候执⾏
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 如果不指定任何作⽤域,设置只针对下⼀个事务,随后的事务恢复之前的隔离级别
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

其中:

sql 复制代码
# ⽅式⼀
SET GLOBAL transaction_isolation = 'SERIALIZABLE';
# 注意使⽤SET语法时有空格要⽤"-"代替
SET SESSION transaction_isolation = 'REPEATABLE-READ'; 
# ⽅式⼆
SET @@GLOBAL.transaction_isolation='SERIALIZABLE';
# 注意使⽤SET语法时有空格要⽤"-"代替
SET @@SESSION.transaction_isolation='REPEATABLE-READ';

不同隔离级别问题重现

脏读:

准备工作:

创建两个会话,将隔离级别设置成读未提交。

如果此时回滚会话1,那么会话2读到的王五的数据相当于垃圾,无效数据。

不可重复读:

事务1 在执行中修改了李四的值,此时事务2 看不到修改的值,但是当事务1 提交之后,事务2 得到了不同的李四的值。

幻读:

插入一个新数据,重新演示:


总结

本文围绕 MySQL 事务展开,先阐述事务是一组 SQL 语句的原子操作(要么全成功要么全失败),并以转账场景为例说明其作用。接着解析 ACID 特性(原子性、一致性、隔离性、持久性),说明事务保障数据一致性的原理。

在使用上,介绍了事务的语法(START TRANSACTION、COMMIT、ROLLBACK、保存点),以及自动与手动提交的区别和操作方法。最后聚焦隔离性,分析读未提交、读已提交、可重复读、串行化四种隔离级别,对应讲解脏读、不可重复读、幻读问题,并演示了隔离级别的查看与设置,帮助读者全面掌握 MySQL 事务的原理与实践,保障数据操作的一致性和安全性。

相关推荐
正在走向自律9 分钟前
豆包编程模型Doubao-Seed-Code深度体验,从零开始构建全栈项目的完整指南
java·服务器·数据库·doubao·claude code·火山方舟
h***85610 分钟前
MySQL数据分析
数据库·mysql
倔强的石头_15 分钟前
从海量时序数据到无人值守:数据库在新能源集控系统中的架构实践
数据库
合作小小程序员小小店26 分钟前
web网页开发,在线%医院诊断管理%系统,基于Idea,html,css,jQuery,java,jsp,ssh,mysql。
java·前端·css·数据库·jdk·html·intellij-idea
Alita11101_27 分钟前
深度测评:openGauss 在虚拟机中对 SQL92 标准的兼容性
数据库
w***954936 分钟前
【Redis】centos7 systemctl 启动 Redis 失败
数据库·redis·缓存
Mr.徐大人ゞ37 分钟前
8.事务在 Group Replication 中的处理流程
mysql
纪伊路上盛名在1 小时前
Alphafold实用指南—官网教程3
数据库·人工智能·机器学习·alphafold·计算生物学·结构生物学
Mr.wangh1 小时前
spring-cloud服务远程调用(Eureka、Nacos、OpenFeign)
数据库
Elias不吃糖2 小时前
NebulaChat:C++ 高并发聊天室服务端
开发语言·c++·redis·sql·项目文档