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 updatelock 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一致性,是由哪几部分共同保障?
相关推荐
一条泥憨鱼1 小时前
苍穹外卖【day09| 对订单的各种操作】
后端·苍穹外卖
Wang's Blog1 小时前
Java框架快速入门: Spring Security+OAuth2之搭建授权服务器(依赖与表结构)
java·服务器·spring
Persistent的粽子!1 小时前
C++的内存管理
c++·经验分享·笔记
落叶上的秋1 小时前
基于spring-ai快速搭建ai-client、mcp服务
spring·ai
前端snow1 小时前
ai agent --- AGUI 协议,流式渲染组件
人工智能·后端
用户8181870627462 小时前
第26章 Kafka vs RocketMQ vs RabbitMQ真实选型对比
java·后端
易境通代购商城系统、集运SAAS系统2 小时前
海运拼箱配载效率升级:货代如何实现智能化高效装柜?
经验分享
小兔崽子去哪了2 小时前
深度学习 2 / CNN 神经网络
后端·python
rannn_1112 小时前
【Java面试八股】Java基础篇|数据类型、面向对象、关键字、反射、注解、异常、新特性、序列化、设计模式、IO
java·后端·面试