1.1 什么是事务
1.1.1 概念
事务(Transaction)是最小的不可再分的工作单元;一个完整业务由多条 DML(insert、update、delete)语句组成,这些 SQL要么全部执行成功,要么全部不执行,是一组不可分割的数据库操作序列。
重点:MySQL 中只有 InnoDB 引擎支持事务,MyISAM 不支持事务。
-- 查看数据库支持的引擎
show engines;
InnoDB 默认引擎,支持事务、行锁、外键。
1.1.2 示例:银行转账业务
张三转账 200 元给李四,需要两条 update 语句:
update account set money = money - 200 where id = 1;
update account set money = money + 200 where id = 2;
分析: 两条语句必须同时成功或者同时失败。第一条修改只是在内存记录操作,不会持久化到磁盘;两条全部执行成功后,才同步底层磁盘数据;如果第二条执行失败,清空所有操作记录,全部回滚,这就是事务的作用。
1.1.3 事务处理原则
事务作为一个完整单元执行:
- 全部成功:执行
commit提交,修改永久保存; - 任意一步失败:执行
rollback回滚,撤销所有修改,回到事务开始之前的状态; 保证数据一致性,不会出现一半成功一半失败的异常数据。
1.2 事务四大特性 ACID
1.2.1 原子性(Atomicity)
事务是不可分割的最小单元,事务内部操作要么全部发生,要么全部不发生。 转账案例:不能出现张三扣钱、李四没加钱的情况。
1.2.2 一致性(Consistency)
事务执行前后,数据库都处于合法一致状态。 转账案例:转账前两人总额 2000,无论事务成功还是失败,总额始终保持 2000,不会凭空多钱或者少钱。
1.2.3 隔离性(Isolation)
多个并发事务互相隔离,每个事务拥有独立的数据空间,一个事务的操作,在未提交前,不应该随意干扰其他事务。
1.2.4 持久性(Durability)
事务一旦提交成功,对数据的修改永久写入磁盘。就算数据库宕机、服务器断电,提交的数据也不会丢失。
1.3 MySQL 事务基础使用
1.3.1 事务三大核心命令
- 开启事务:
begin/start transaction - 提交事务:
commit,持久化修改到磁盘 - 回滚事务:
rollback,撤销本次事务所有 DML 操作
1.3.2 事务开启方式汇总
| 方式 | 命令 |
|---|---|
| 显式开启 1 | begin + SQL + commit/rollback |
| 显式开启 2 | start transaction + SQL + commit/rollback |
| 自动提交关闭 | set autocommit=0,后续 DML 属于同一个事务,需要手动 commit |
| 自动提交开启 | set autocommit=1,执行 DML 自动提交 |
1.3.3 autocommit 自动提交参数
MySQL默认 autocommit=1,自动提交,每执行一条 DML 就直接持久化。
-- 查询自动提交状态
select @@autocommit;
show variables like 'autocommit';
-- 仅当前会话关闭自动提交
set autocommit=0;
-- 全局关闭自动提交(所有新连接生效)
set global autocommit=0;
注意:事务是基于数据库连接(会话 session),不是基于表。同一个连接中,在 commit/rollback 之前,所有 DML 都会属于同一个事务;开启事务后必须提交或者回滚,否则会长期占用锁资源。
1.3.4 实操示例 1:begin、commit、rollback
-- 创建测试库和账户表
create database mydb17_transcation;
use mydb17_transcation;
create table account (id int(10) primary key not null, name varchar (40), money double);
insert into account values(1, '张三', 1000);
insert into account values(2, '李四', 2000);
-- 测试:开启事务修改,不提交,断开连接
begin;
update account set money=3000 where id=2;
select * from account;
exit;
重新登录查询:李四金额依旧是 2000,未提交的事务断开连接自动回滚。
-- 提交事务示例
begin;
update account set money=3000 where id=2;
commit;
exit;
-- 重连查询,数据永久生效
-- 回滚事务示例
begin;
delete from account where id=1;
select * from account;
rollback;
select * from account; -- 张三数据恢复
1.3.5 实操示例 2:关闭自动提交
select @@autocommit;
set autocommit=0;
insert into account values(3, '王五', 3000);
commit;
select * from account;
1.3.6 回滚点 savepoint
可以在事务内标记回滚点,支持只回滚到指定标记,而不是整个事务全部回滚。
savepoint s1; -- 创建回滚点s1
rollback to s1; -- 回滚到s1标记处
注意:自动提交开启时,语句执行就提交事务,回滚点会失效,报错
SAVEPOINT does not exist。
1.3.7 隐式提交
部分 DDL 语句(create、alter、drop 等)会触发隐式提交,哪怕没有写 commit,事务也会自动提交,无法 rollback。
生产建议:业务事务使用显式
commit/rollback,避免隐式提交带来数据风险。
1.4 InnoDB 如何保证 ACID:redo log 与 undo log
1.4.1 WAL 预写日志技术
WAL:Write-Ahead Logging,先写日志,再刷磁盘数据页。 磁盘随机 IO 很慢、顺序 IO 很快,InnoDB 更新数据不会直接修改磁盘数据页,流程:
- 数据页加载到 Buffer Pool 缓冲池
- 在内存 Buffer Pool 修改数据,生成 redo log 写入 redo log buffer
- 事务提交时,先把 redo log 顺序追加写入磁盘 redo log 文件,提交才算成功
- 后台线程空闲时,再把脏页(修改过的数据页)随机刷入磁盘数据文件(刷脏)
通俗理解:先记在粉板(redo log),空闲的时候再誊写到账本(数据文件)。
- redo log 作用:保证持久性 D,宕机后用来恢复已经提交、但是脏页还没刷盘的数据。
1.4.2 redo log 查看命令
-- 查看redo日志总容量
show variables like 'innodb_redo_log_capacity';
-- 查看redo运行状态、LSN信息
show global status like '%innodb%redo%';
redo 日志文件存放路径:Data/#innodb_redo,一共 32 个日志文件,循环复用。
1.4.3 CSR 前滚(Crash Safe Recovery)
LSN:日志逻辑序列号,每次写 redo log LSN 递增。 MySQL 重启时对比数据页 LSN 和 redo log 的 LSN:
- 如果 redo 里面有已经提交、但是还没刷到数据页的修改,自动执行前滚,把数据补齐;
- 没有提交的事务,通过 undo 回滚撤销。 这个宕机自动恢复流程就是 CSR。
1.4.4 undo 回滚日志
undo log 记录数据修改前的旧版本数据,两大核心作用:
- 原子性 A:事务 rollback 的时候,读取 undo 日志恢复修改前的数据;
- 隔离性 I、MVCC 多版本:保存历史数据快照,实现可重复读,让读不加锁。
redo log:记录修改之后的数据,用于宕机恢复(前滚); undo log:记录修改之前的数据,用于回滚、快照读。
1.5 事务隔离级别
1.5.1 并发事务三大问题
并发事务不加隔离控制,会出现三类问题:
- 脏读 Dirty Read :一个事务读到其他事务未提交的数据,如果对方回滚,读到的数据是脏数据。
- 不可重复读 Non-repeatable Read :同一个事务内,多次读取同一条记录,中间被别的事务修改并提交,两次查询结果不一样(针对行数据内容修改)。
- 幻读 Phantom Read:同一个事务范围查询,别的事务在范围内新增 / 删除数据并提交,再次查询发现行数变了,像出现幻觉(针对记录条数)。
区分重点: 不可重复读:行内容被 UPDATE 修改; 幻读:范围里面新增 INSERT 或者删除 DELETE 行,查询结果行数变化。
1.5.2 SQL 标准 4 种隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED 读未提交 | ✅ 允许 | ✅ 允许 | ✅ 允许 |
| READ COMMITTED 读已提交 | ❌ 禁止 | ✅ 允许 | ✅ 允许 |
| REPEATABLE READ 可重复读(MySQL 默认) | ❌ 禁止 | ❌ 禁止 | ✅ 允许(InnoDB 通过间隙锁缓解) |
| SERIALIZABLE 串行化 | ❌ 禁止 | ❌ 禁止 | ❌ 禁止 |
1.隔离级别越高,一致性越强,并发性能越低。
2.四种隔离的并发性能如图:

1.5.3 查看和修改隔离级别
-- 查询隔离级别(会话+全局)
select @@GLOBAL.transaction_isolation, @@transaction_isolation;
show variables like "transaction_isolation";
-- 修改当前会话隔离级别
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
set session transaction isolation level serializable;
-- 修改全局隔离级别,新连接生效
set global transaction isolation level repeatable read;
1.5.4 并发实操演示(两个 mysql 连接)
use mydb17_transcation;
create table ac (id int(2) primary key not null, name varchar (10), money double);
insert into ac values(1, '张三', 1000);
insert into ac values(2, '李四', 2000);
set autocommit = 0;
- READ UNCOMMITTED:可以读到对方未提交修改,产生脏读;
- READ COMMITTED:只能读到对方提交之后的数据,解决脏读,但是会出现不可重复读;
- REPEATABLE READ(MySQL 默认):同一个事务内,多次读取结果一致,解决脏读、不可重复读,InnoDB 间隙锁抑制幻读;
- SERIALIZABLE 串行化:事务串行排队执行,读操作也会上锁,完全隔离,并发极差,生产极少使用。