MySQL InnoDB事务完整教程|ACID、MVCC、隔离级别、事务控制

教学向博客:零基础可以建立完整认知,有开发经验的人用来巩固知识、备战面试。全部是后端开发高频核心知识点,剔除冷门边角内容。 实验环境:MySQL8.0,InnoDB引擎,默认隔离级别 REPEATABLE‑READ(可重复读)。

前言

本文完整讲解InnoDB事务整套核心概念:事务基础、ACID四大特性、并发事务问题、四大隔离级别、MVCC多版本并发控制、事务控制与线上最佳实践。 文中配有可直接复制运行的SQL示例、Mermaid示意图、小节坑点总结,文末附带汇总表格与课后思考题,方便复盘。


第一部分:SQL事务基础

1.1 什么是事务

事务是一组逻辑相关的SQL操作单元 ,这一组操作是一个不可分割整体。事务内的所有修改,要么全部执行成功提交,要么全部失败回滚,不会停留在中间状态。

最经典业务场景:账户转账。A扣钱、B加钱,这两步必须作为一个事务。不能出现A扣了钱,B却没有收到钱的异常数据。

1.2 为什么需要事务,没有事务会发生什么

如果没有事务机制,当程序崩溃、数据库宕机、代码抛出异常时,就会出现部分SQL执行成功、部分SQL没有执行的半完成状态,产生脏数据。 转账案例中就会出现:A余额减少,B余额不变,资金凭空消失,业务数据彻底错乱。

1.3 事务控制:SQL手动事务语法

核心命令:

  • BEGIN / START TRANSACTION:开启一个新事务
  • COMMIT:提交事务,所有修改持久化到数据库
  • ROLLBACK:回滚事务,撤销本次事务内全部修改
  • autocommit:自动提交参数,MySQL默认开启,每执行一条SQL自动提交事务

实操SQL示例,模拟转账:

text 复制代码
-- 关闭自动提交方式1:手动开启事务
BEGIN;

-- A账户扣100
UPDATE account SET balance = balance - 100 WHERE id = 1;
-- B账户加100
UPDATE account SET balance = balance + 100 WHERE id = 2;

-- 一切正常则提交
COMMIT;

-- 如果中间出错,执行回滚,撤销上面两条update
-- ROLLBACK;

查看与修改自动提交:

text 复制代码
-- 查看自动提交状态,1代表开启
SHOW VARIABLES LIKE 'autocommit';

-- 关闭自动提交,当前会话生效
SET autocommit = 0;

Mermaid流程图:事务正常提交与异常回滚

1.4 Java业务层事务演示(Spring @Transactional)

在Spring开发中,一般不手写BEGIN/COMMIT,使用注解声明式事务。

text 复制代码
@Service
public class TransferService {

    @Autowired
    private AccountMapper accountMapper;

    /**
     * 声明式事务:方法执行完成正常退出自动commit;抛出异常自动rollback
     */
    @Transactional
    public void transfer(Long fromId, Long toId, Integer money){
        accountMapper.deduct(fromId, money);
        accountMapper.add(toId, money);
        // 方法结束,事务提交
    }
}

小节小结&坑点

  1. autocommit开启时,每条SQL就是一个独立小事务;
  2. 手动开启事务后,忘记commit,事务会一直存活,持续持有锁,引发锁等待、连接占用问题;
  3. Spring事务只对public方法生效,内部调用会出现事务失效。

第二部分:ACID 四大特性

ACID是事务的四个核心特性,是判断数据库是否支持事务的标准。

重点:理解每个特性的含义,以及InnoDB依靠什么底层技术实现。

2.1 A 原子性 Atomicity

是什么 :事务是不可分割原子单元,事务内操作要么全部成功,要么全部失败回滚,不存在中间状态。 解决什么 :防止出现部分执行的脏数据。 底层实现:undo log 回滚日志。

执行DML修改数据时,InnoDB会把修改前的数据写入undo log;当事务执行ROLLBACK回滚时,依靠undo log把数据恢复成修改之前的样子。

Mermaid:undo log回滚流程

2.2 C 一致性 Consistency

是什么:事务执行前后,数据库业务数据完整性约束不会被破坏,数据始终处于合法状态。

⚠️高频误区:一致性不是数据库底层某一个组件直接实现,它是最终目标结果。 原子性、隔离性、持久性 + 业务代码约束、数据库约束(主键、唯一索引、非空)共同保障一致性。

举例转账:转账前后A+B总余额必须不变,这个业务上的一致性,需要原子性保证不会半更新,同时业务代码也要写正确。数据库不能自动帮你实现业务逻辑层面的一致性。

2.3 I 隔离性 Isolation

是什么 :数据库允许多个事务并发执行,隔离性保证各个事务之间互相不可随意看见对方未提交的数据,避免并发互相干扰。 解决什么 :并发事务互相读取到对方中间状态数据,产生脏读等问题。 底层依赖:锁 + MVCC多版本并发控制。多个事务并发读写时,通过锁控制写冲突,MVCC控制读写冲突。

隔离性有强弱之分,也就是后面讲解的四大隔离级别。

2.4 D 持久性 Durability

是什么 :事务一旦执行commit提交成功,修改就永久生效,即使数据库宕机、断电重启,修改的数据也不会丢失。 解决什么 :内存数据断电丢失问题。 底层实现:redo log 重做日志。

MySQL修改数据优先修改内存页,不会立刻刷入磁盘数据文件。事务提交时,把修改记录写入redo log持久化到磁盘;就算宕机重启,MySQL读取redo log把已经提交的变更恢复到数据文件。

Mermaid:redo log持久化机制

ACID汇总表格

特性 全称 含义 InnoDB底层依赖
A Atomicity 原子性 事务全部成功或全部回滚 undo log回滚日志
C Consistency 一致性 事务前后数据业务状态合法正确 原子性+隔离性+持久性+业务约束
I Isolation 隔离性 并发事务之间数据互相隔离 锁 + MVCC
D Durability 持久性 提交后修改永久保存,宕机不丢 redo log重做日志

第三部分:并发事务会产生的三类问题

当多个事务同时读写同一份数据,如果隔离做得不够,就会出现三类经典现象:脏读、不可重复读、幻读。

注意:这三类是现象,不一定是bug,不同业务可以容忍不同现象。

3.1 脏读 Dirty Read

现象:一个事务读到了另一个事务还没有commit提交的数据;对方事务后续回滚,当前事务读到的数据就是无效脏数据。

时序示意Mermaid

业务危害:读到最终不会生效的数据,业务逻辑出错。只有读未提交隔离级别才会出现脏读。

3.2 不可重复读 Non‑Repeatable Read

现象:同一个事务内部,对同一条记录执行两次相同查询,中间被其他事务修改并且提交,两次查询返回结果不一样 。 重点:针对同一条行记录,其他事务做update/update。

Mermaid时序示意

业务危害:同一个事务内,同一数据前后不一致,统计、报表逻辑出错。读已提交级别会出现;可重复读级别解决该现象。

3.3 幻读 Phantom Read

现象:同一个事务内,相同条件执行两次范围查询,其他事务执行insert/delete并提交,两次查询返回的行数发生变化,仿佛出现幻影行。 重点:针对范围查询,新增或者删除行,不是修改已有行。

Mermaid时序示意

业务危害:范围统计、批量更新,同一个事务内前后行数不一致。

注意:MySQL InnoDB RR(可重复读)级别,依靠临键锁Next‑Key Lock解决了幻读问题。


第四部分:InnoDB四大事务隔离级别

隔离级别由低到高,隔离越强,并发性能越低。每一级别对应允许或者禁止上面三种并发现象。

隔离级别 脏读 不可重复读 幻读 实现手段 性能
READ UNCOMMITTED 读未提交 允许 允许 允许 无隔离,直接读内存数据 最高
READ COMMITTED 读已提交 RC 禁止 允许 允许 MVCC,每次select生成新ReadView 高
REPEATABLE READ 可重复读 RR(MySQL默认) 禁止 禁止 禁止(InnoDB) MVCC+临键锁Next‑Key Lock 中等
SERIALIZABLE 串行化 禁止 禁止 禁止 全部select隐式转当前读,加共享锁 最低

4.1 READ UNCOMMITTED 读未提交

  • 现象:可以读到其他事务未提交的数据,会发生脏读。
  • 业务场景:几乎不会使用。
  • 设置语法:
text 复制代码
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

4.2 READ COMMITTED 读已提交(RC)

  • 只能读到其他事务已经提交的数据,解决脏读;但是会出现不可重复读、幻读。
  • MVCC行为:事务内每一次select查询,都会生成全新ReadView,所以可以看到别的事务已经提交的修改。
  • 适用场景:大部分互联网业务,追求高并发,能够接受同一个事务内数据变化。

4.3 REPEATABLE READ 可重复读 RR(MySQL默认)

  • 解决脏读、不可重复读;InnoDB通过临键锁解决幻读。
  • MVCC行为:事务内第一次执行select的时候生成ReadView,整个事务复用同一个ReadView快照,保证同一个事务多次查询结果不变。
  • 适用场景:MySQL默认,绝大多数业务直接使用该级别。

4.4 SERIALIZABLE 串行化

  • 隔离级别最高,全部并发事务串行执行,所有问题全部解决。
  • 实现:普通select语句会隐式转换为SELECT ... LOCK IN SHARE MODE,读也加共享行锁,读写互相阻塞,并发性能很差。
  • 适用场景:对数据一致性要求极高、并发很小的业务,极少使用。

Mermaid对比RC与RR ReadView生成时机

查看与设置隔离级别语法:

text 复制代码
-- 查看当前会话隔离级别
SELECT @@transaction_isolation;

-- 设置会话级别隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

小节坑点

  1. RR是MySQL特有行为,标准SQL中RR隔离级别不能解决幻读,InnoDB依靠临键锁实现;
  2. RC隔离级别没有临键锁,幻读会出现。

第五部分:MVCC 多版本并发控制(重点章节)

MVCC Multi‑Version Concurrency Control,多版本并发控制,是InnoDB实现高并发读写的核心机制。

5.1 什么是MVCC

核心思想:写操作加锁,快照读不加锁;数据库保存数据的多个历史版本,读操作读取历史快照版本,做到读写互不阻塞。

5.2 为什么要有MVCC,解决什么

如果没有MVCC,为了保证隔离性,读数据也需要加锁。写的时候读被阻塞,读的时候写被阻塞,数据库并发能力会大幅下降。 MVCC实现:写加锁,快照读不加锁,读写不冲突,极大提升并发性能。

区分两个概念:快照读、当前读

  1. 快照读 :普通select * from table,走MVCC,读取快照版本,不加行锁。
  2. 当前读 :select ... for update、lock in share mode、update、delete。读取数据库最新版本,会加行锁。

SQL演示快照读和当前读区别:

text 复制代码
-- 快照读,不加锁,走MVCC
SELECT * FROM account WHERE id = 1;

-- 当前读,加X排他锁,读取最新数据
SELECT * FROM account WHERE id =1 FOR UPDATE;

5.3 MVCC核心组件

  1. 行隐藏字段:InnoDB每一行数据有隐藏列
  • DB_TRX_ID:最后修改这条记录的事务ID
  • DB_ROLL_PTR:回滚指针,指向undo log里面上一个历史版本
  1. undo log:保存数据修改的历史版本,多个版本形成版本链表
  2. ReadView读视图:快照读的时候生成,一套判断规则,用来判断:这条历史版本对当前事务是否可见。

Mermaid:数据版本链 + ReadView判断逻辑

5.4 MVCC在RC、RR下核心差异

  • RC(读已提交):每一次快照读,都会生成新ReadView;可以看到其他事务已经提交的修改。
  • RR(可重复读):事务第一次快照读时生成ReadView,整个事务复用同一个ReadView,所以同一个事务多次查询结果不变。

小节坑点小结

  1. MVCC只针对快照读普通select生效;当前读(update、for update)不走快照,直接读最新数据,加锁;
  2. 长事务会导致undo log版本链无法回收,磁盘暴涨,数据库性能下降。

第六部分:事务常见线上问题与最佳实践

6.1 长事务的巨大危害

  • 长事务不提交,行锁持续持有,容易发生锁等待、死锁;
  • MDL锁跟随事务,长事务会直接造成DDL阻塞,引发业务雪崩;
  • MVCC版本链不能回收,undo log持续膨胀,磁盘占用高,查询性能下降。

最佳实践:尽量缩小事务范围,事务内不要执行网络调用、http请求、大量耗时业务逻辑。

6.2 避免大事务

不要把大量insert/update放在同一个事务;可以拆分成多个小事务分批提交。

6.3 Spring事务高频坑

  1. try‑catch捕获异常后,异常不会抛出,事务不会自动回滚,需要手动TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  2. 非public方法、同类内部调用,@Transactional失效。

6.4 隔离级别选型建议

  1. 绝大多数业务直接使用MySQL默认RR;
  2. 追求更高并发、可以接受同一个事务内数据变化,选择RC;
  3. 串行化隔离级别,业务开发尽量避免。

文末综合总结

  1. undo log:实现原子性回滚,同时提供MVCC历史数据版本;
  2. redo log:实现持久性,保证宕机事务数据不丢失;
  3. 锁:解决写‑写并发冲突;
  4. MVCC:解决读写并发冲突,快照读不加锁,提升并发;
  5. RR可重复读是MySQL默认隔离级别,依靠MVCC+临键锁,同时解决不可重复读和幻读。

课后思考题(巩固)

  1. RC和RR隔离级别,MVCC ReadView生成时机有什么区别,造成什么现象?
  2. 快照读会不会被行锁阻塞?为什么?
  3. 为什么线上业务严禁运行长时间不提交的长事务?分别从锁、MDL、MVCC角度说明。
  4. ACID中的C一致性,是由哪几部分共同保障?
相关推荐
明月_清风17 小时前
Muse 登顶 App Store 第一,SDK 直接开源:AI Agent 开始进入下一个阶段
人工智能·后端
ychqsq19 小时前
207.炸酱面
经验分享·职场和发展
hsfxuebao19 小时前
Loop Engineering 保姆级教程 + 项目实战
人工智能·后端
打工仔折腾 AI19 小时前
从 Demo 到生产级 Agent:8 个关键设计机制与 Python 实现拆解
java·jvm·人工智能·后端·python·langchain·ai agent 实战
架构技术专栏20 小时前
交叉熵:AI 怎样给概率预测打分
后端
IT_陈寒21 小时前
SpringBoot自动配置坑了我一把,原来是这样绕过去的
前端·人工智能·后端
要努力啊4691 天前
用 Codex 加速 Java 开发:从代码生成到测试覆盖的完整实战
后端
dora1 天前
LangChain4j 新手入门实战教程(Java版)
后端·langchain·agent
蜗牛互联网1 天前
MongoDB Atlas Agent Engine之后,如何用版本门禁防止陈旧写入
java·数据库·人工智能·后端·mongodb
付威20231 天前
Rust 生命周期:为什么要有它,实际代码里到底怎么用?
后端