MySQL《事务》

文章目录

  • 前言
  • 一、什么是事务?
  • 二、事务的ACID特性
  • 三、如何使用事务?
    • [3.1 查看支持事务的存储引擎](#3.1 查看支持事务的存储引擎)
    • [3.2 语法](#3.2 语法)
    • [3.3 开启一个事务,执行修改后回滚](#3.3 开启一个事务,执行修改后回滚)
    • [3.4 开启一个事务,执行修改后提交](#3.4 开启一个事务,执行修改后提交)
    • [3.5 保存点](#3.5 保存点)
    • [3.6 自动/手动提交事务](#3.6 自动/手动提交事务)
  • 四、事务的隔离性和隔离级别
    • [4.1 什么是隔离性](#4.1 什么是隔离性)
    • [4.2 隔离级别](#4.2 隔离级别)
      • [4.2.1 READ UNCOMMITTED 读未提交](#4.2.1 READ UNCOMMITTED 读未提交)
      • [4.2.2 READ COMMITTED 读已提交](#4.2.2 READ COMMITTED 读已提交)
      • [4.2.3 REPEATABLE READ 可重复读](#4.2.3 REPEATABLE READ 可重复读)
      • [4.2.4 SERIALIZABLE 串行化](#4.2.4 SERIALIZABLE 串行化)
      • [4.2.5 对比总结](#4.2.5 对比总结)
  • 总结

前言

文章中很多概念的来源>>概念来源

一、什么是事务?

事务把⼀组SQL语句打包成为一个整体,在这组SQL的执行过程中,要么全部成功,要么全部失败。

比如小明向小李转账100块钱,那么小明的账户要扣除100,小李的账户要增加100,不能小明扣钱了而小李没有收到,也不能两人合计的金额发生改变,不能被其他转账事务干扰,同时记录应该存储起来便于查找,这就涉及到事务的ACID特性了


二、事务的ACID特性

  1. 原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  2. 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
  3. 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
  4. 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

三、如何使用事务?

3.1 查看支持事务的存储引擎

在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务

sql 复制代码
show engines

3.2 语法

开启事务

sql 复制代码
# 显式地开启一个事务
BEGIN
# 或者
START TRANSACTION

提交事务

sql 复制代码
# 提交事务,并使已对数据库进行的所有修改成为永久性的
COMMIT
# 或者
COMMIT WORK

回滚

sql 复制代码
# 回滚会结束用户的事务,并撤销正在进行的所有未提交的修改
ROLLBACK
# 或者
ROLLBACK WORK

3.3 开启一个事务,执行修改后回滚

执行例子的代码

sql 复制代码
create table bank_account (
	id bigint primary key auto_increment,
	name varchar(255) not null, # 姓名
	balance decimal(10,2) not null # 余额
);
insert into bank_account(name,balance) values('张三',1000);
insert into bank_account(name,balance) values('李四',1000);


select * from bank_account;

开启事务

sql 复制代码
start transaction


修改事务

sql 复制代码
# 修改前
select * from bank_account
sql 复制代码
update bank_account set balance = balance - 100 where name = '张三';
update bank_account set balance = balance + 100 where name = '李四';

# 修改后
select * from bank_account

执行回滚

sql 复制代码
# 执行回滚,事务结束
rollback
sql 复制代码
# 回滚后
select * from bank_account


整体代码

sql 复制代码
start transaction

# 修改前
select * from bank_account

update bank_account set balance = balance - 100 where name = '张三';
update bank_account set balance = balance + 100 where name = '李四';

# 修改后
select * from bank_account

# 执行回滚,事务结束
rollback

# 回滚后
select * from bank_account

3.4 开启一个事务,执行修改后提交

与上面的回滚代码类似

开启事务

sql 复制代码
begin


修改事务

sql 复制代码
# 修改前
select * from bank_account
sql 复制代码
update bank_account set balance = balance - 100 where name = '张三';
update bank_account set balance = balance + 100 where name = '李四';

# 修改后
select * from bank_account


执行提交

sql 复制代码
# 执行提交,事务结束
commit
sql 复制代码
# 提交后
select * from bank_account


整体代码

sql 复制代码
# 开启事务
begin

# 修改前
select * from bank_account

update bank_account set balance = balance - 100 where name = '张三';
update bank_account set balance = balance + 100 where name = '李四';

# 修改后
select * from bank_account

# 执行提交,事务结束
commit

# 提交后
select * from bank_account

3.5 保存点

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

开启事务

sql 复制代码
# 开启事务
begin

# 修改前
select * from bank_account


修改事务

sql 复制代码
update bank_account set balance = balance - 100 where name = '张三';
update bank_account set balance = balance + 100 where name = '李四';

# 修改后
select * from bank_account


设置第一个保存点

sql 复制代码
# 设置保存点
savepoint savepoint1;


再次修改事务

sql 复制代码
# 再次执行让张三-100,李四+100
update bank_account set balance = balance - 100 where name = '张三';
update bank_account set balance = balance + 100 where name = '李四';

# 第二次修改后
select * from bank_account

设置第二个保存点

插入新数据

sql 复制代码
# 插入数据后
select * from bank_account


回滚到第二个保存点

sql 复制代码
# 回滚到第二个保存点
rollback to savepoint2;

select * from bank_account



回滚到第一个保存点

sql 复制代码
# 回滚到第一个保存点
rollback to savepoint1;

select * from bank_account



回滚到事务最初状态

sql 复制代码
# 回滚时不指定保持点,直接回滚到事务最开始的状态,事务结束
rollback
select * from bank_account



完整代码

sql 复制代码
# 开启事务
begin

# 修改前
select * from bank_account

update bank_account set balance = balance - 100 where name = '张三';
update bank_account set balance = balance + 100 where name = '李四';

# 修改后
select * from bank_account

# 设置保存点
savepoint savepoint1;

# 再次执行让张三-100,李四+100
update bank_account set balance = balance - 100 where name = '张三';
update bank_account set balance = balance + 100 where name = '李四';

# 第二次修改后
select * from bank_account

# 设置第二个保存点
savepoint savepoint2;

# 插一条新记录
insert into bank_account(name,balance) values('王五',1000)

# 插入数据后
select * from bank_account

# 回滚到第二个保存点
rollback to savepoint2;

select * from bank_account

# 回滚到第一个保存点
rollback to savepoint1;

select * from bank_account

# 回滚时不指定保持点,直接回滚到事务最开始的状态,事务结束
rollback

select * from bank_account

3.6 自动/手动提交事务

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

查看当前事务是否为自动提交

sql 复制代码
show variables like 'autocommit'

可以通过以下语句设置事务为自动或手动提交

sql 复制代码
# 设置事务自动提交
SET AUTOCOMMIT=1; # 方式一
SET AUTOCOMMIT=ON; # 方式二
# 设置事务⼿动提交
SET AUTOCOMMIT=0; # 方式一
SET AUTOCOMMIT=OFF; # 方式二

注意:

  1. 只要使用 START TRANSACTION 或 BEGIN 开启事务,必须要通过 COMMIT 提交才会持久化,与是否设置 SET AUTOCOMMIT 无关。
  2. 手动提交模式下,不用显示开启事务,执行修改操作后,提交或回滚事务时直接使用 commit或 rollback
  3. 已提交的事务不能回滚

四、事务的隔离性和隔离级别

4.1 什么是隔离性

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

4.2 隔离级别

在MySQL的InnoDB引擎中事务的隔离级别有四种,分别是:

  1. READ UNCOMMITTED ,读未提交
  2. READ COMMITTED ,读已提交
  3. REPEATABLE READ ,可重复读(默认)
  4. SERIALIZABLE ,串行化

4.2.1 READ UNCOMMITTED 读未提交

定义:事务可以读取其他事务尚未提交的数据变更(即 "脏读")。

特点

  • 最低隔离级别,并发性能最高,但存在严重的数据不一致风险。
  • 可能出现的问题:
    • 脏读(Dirty Read):事务 A 读取到事务 B 未提交的数据,若 B 后续回滚,A 读到的数据是无效的。
    • 也可能引发不可重复读和幻读。
  • 适用场景:几乎不推荐使用,仅在对数据一致性要求极低、追求高并发的场景(如临时统计分析)中偶尔使用。

4.2.2 READ COMMITTED 读已提交

定义:事务只能读取其他事务已提交的数据变更。

特点

  • 避免脏读,但可能存在不可重复读(Non-repeatable Read):事务 A 在两次读取同一数据期间,事务 B 提交了对该数据的修改,导致 A 两次读到的数据不一致。
  • MySQL 中默认隔离级别?:
    • 非默认,Oracle、SQL Server 默认是此级别,但 MySQL InnoDB 默认是 REPEATABLE READ 可重复读
  • 适用场景:适用于需要避免脏读,但允许不可重复读的场景(如大多数 OLTP 系统)。

4.2.3 REPEATABLE READ 可重复读

定义:事务在执行过程中,多次读取同一数据时,结果始终保持一致,不受其他事务提交操作的影响。

特点

  • 避免脏读和不可重复读,但可能存在幻读(Phantom Read):事务 A 按条件读取数据时,事务 B 插入满足条件的新数据并提交,导致 A 再次按相同条件读取时,结果集新增了数据("幻觉")。
  • MySQL InnoDB 默认隔离级别:通过 MVCC(多版本并发控制) 和 间隙锁(Gap Lock) 机制,默认情况下可避免幻读(其他数据库如 Oracle 在此级别仍可能存在幻读)。
    注意:MVCC并不能解决幻读,它是用来解决不可重复读的,MVCC 仅管理已有数据行的版本,但无法感知 "是否有新行插入到查询条件的范围内"

实现原理

  • 使用快照读(Snapshot Read)获取数据的历史版本,保证同一事务内多次读取结果一致。
  • 写操作(如更新、删除)会加锁,防止其他事务修改当前数据。
    适用场景:默认推荐级别,适用于需要保证事务内数据一致性(如金融交易、库存管理)的场景。

不可重复读和幻读的区别

  • 不可重复读是 "行内数据的版本变更",幻读是 "结果集的成员变更"(行的增删)。
  • 前者关注数据值的一致性,后者关注数据集合的稳定性。

4.2.4 SERIALIZABLE 串行化

定义:最高隔离级别,事务按串行顺序依次执行,完全串行化执行。

特点

  • 避免所有并发问题(脏读、不可重复读、幻读),但并发性能最低,因为会对所有读取的行加锁,可能导致大量锁竞争和超时。
  • 实现方式:通过在读取的行上加排他锁(X Lock),强制事务串行执行。

适用场景:仅在对数据一致性要求极高、并发量极低的场景(如银行转账核心系统)中使用。

4.2.5 对比总结

注:MySQL InnoDB 通过 MVCC + 间隙锁在 REPEATABLE READ 级别下可避免幻读,其他数据库(如 Oracle)在此级别可能仍存在幻读。


总结

本篇文章介绍了MySQL中事务的相关内容,包括事务的特性,如何使用事务,和事务的隔离性以及隔离级别,如果有什么不正确的地方,还望评论区指正,谢谢大家。

相关推荐
Yushan Bai4 小时前
ORACLE RAC环境REDO日志量突然增加的分析
数据库·oracle
躺着听Jay4 小时前
Oracle-相关笔记
数据库·笔记·oracle
瀚高PG实验室4 小时前
连接指定数据库时提示not currently accepting connections
运维·数据库
运维成长记5 小时前
mysql数据库-中间件MyCat
数据库·mysql·中间件
尘客.5 小时前
DataX从Mysql导数据到Hive分区表案例
数据库·hive·mysql
华纳云IDC服务商6 小时前
SQL Server权限设置的几种方法
mysql·sqlserver
TiDB 社区干货传送门6 小时前
从开发者角度看数据库架构进化史:JDBC - 中间件 - TiDB
数据库·oracle·中间件·tidb·数据库架构
虾球xz6 小时前
游戏引擎学习第280天:精简化的流式实体sim
数据库·c++·学习·游戏引擎
uwvwko7 小时前
BUUCTF——web刷题第一页题解
android·前端·数据库·php·web·ctf
今天我又学废了7 小时前
Spark,SparkSQL操作Mysql, 创建数据库和表
大数据·mysql·spark