一、事务的核心定义
事务是一组不可分割的数据库操作集合,是数据库执行的最小逻辑工作单元。事务内的所有操作,要么全部执行成功,要么全部执行失败回滚,不存在 "部分成功、部分失败" 的中间状态。
经典场景示例
最典型的转账场景:A 账户给 B 账户转账 100 元,需要执行两个核心操作:
- A 账户余额扣减 100 元
- B 账户余额增加 100 元
如果没有事务,可能出现 "扣减成功、增加失败" 的情况,导致资金错乱;而事务会将这两个操作打包,保证要么全部生效,要么全部回滚,从根本上避免数据逻辑错误。
二、事务的四大特性(ACID)及底层实现
ACID 是事务的四大核心特性,也是事务的灵魂。其中:原子性是基础,隔离性是并发保障,持久性是可靠性底线,一致性是最终目的,原子性、隔离性、持久性均为保证一致性服务。
原子性(Atomicity)
原子性指事务是一个不可分割的最小执行单元,事务内的所有操作,要么全部执行成功并提交,要么全部执行失败并回滚到事务开始前的状态,不存在任何中间状态。
核心价值
彻底解决 "操作中途异常,数据半改半不改" 的问题,保证操作的完整性。比如转账操作中途数据库宕机,重启后不会出现 "扣款成功、到账失败" 的资金错乱。
底层实现原理:undo log(回滚日志)
InnoDB 完全通过 undo log(回滚日志) 实现事务的原子性,核心逻辑如下:
- undo log 本质 :undo log 是一种逻辑日志 ,记录的是数据修改的反向操作 。
- 执行
INSERT语句时,undo log 会记录对应的DELETE语句; - 执行
UPDATE语句时,undo log 会记录将数据还原为修改前状态的反向更新语句; - 执行
DELETE语句时,undo log 会记录对应的INSERT语句。
- 执行
- 写入时机:事务执行过程中,每修改一行数据,都会先将对应的反向操作写入 undo log,并持久化到磁盘,再执行数据修改。
- 回滚逻辑 :当事务执行失败、手动执行
ROLLBACK,或数据库宕机重启时,InnoDB 会读取 undo log 中的反向操作,逐行将数据恢复到事务开始之前的状态,撤销所有未提交的修改,保证所有操作要么全成、要么全败。 - 补充特性:undo log 不仅用于实现原子性,也是 InnoDB MVCC(多版本并发控制)的核心基础,用于实现事务的隔离性。
2.2 一致性(Consistency)
一致性指事务执行前后,数据库的完整性约束不会被破坏,数据始终符合业务逻辑的预期。完整性约束分为两个层面:
- 数据库层面约束:主键不重复、唯一索引约束、外键关联约束、非空约束、字段类型约束等;
- 业务层面约束:转账前后两个账户的总余额不变、商品库存不能为负数、用户积分不能为负等符合业务规则的逻辑约束。
核心价值
一致性是事务的最终目标,原子性、隔离性、持久性都是手段,最终都是为了保证数据始终符合预期,不会出现逻辑错乱。
底层实现原理
一致性无法完全由数据库独立实现,需要数据库层面保障 + 业务层面保障共同完成:
- 数据库层面的一致性保障
- 依托原子性、隔离性、持久性的底层机制,保证事务不会出现中间状态、不会被其他并发事务干扰、不会丢失数据,从根本上避免数据错乱;
- 内置约束校验:主键、唯一索引、外键、非空等约束,在数据写入时自动校验,不符合约束的操作会直接报错拒绝,保证数据的物理完整性。
- 业务层面的一致性保障
- 数据库无法校验业务逻辑的合理性,比如转账时 "扣减 100 元、增加 200 元" 的 SQL,数据库不会拦截,这部分必须由开发人员在业务代码中保证;
- 业务层面需要通过合理的 SQL 编写、参数校验、异常处理,保证事务内的操作符合业务规则,最终实现数据的逻辑一致性。
2.3 隔离性(Isolation)
核心定义
隔离性指多个并发执行的事务之间,是相互隔离、互不干扰的,一个事务的内部操作和中间状态,对其他事务不可见,每个事务都感觉不到其他事务在并发执行。
核心价值
解决多事务并发读写同一行数据时,出现的数据安全问题,在并发性能和数据安全之间做平衡。
无隔离性会出现的 3 类并发问题
表格
| 问题类型 | 核心定义 | 场景示例 |
|---|---|---|
| 脏读 | 一个事务读取到了另一个事务未提交的修改数据 | 事务 A 修改了用户余额,未提交;事务 B 读取到了这个修改后的值;随后事务 A 回滚,事务 B 读到的就是无效的脏数据 |
| 不可重复读 | 一个事务内,两次读取同一行数据,结果不一致 | 事务 A 第一次读取用户余额为 1000 元;期间事务 B 修改了该余额为 800 元并提交;事务 A 第二次读取,余额变成了 800 元,同一事务内两次读取结果不一致 |
| 幻读 | 一个事务内,两次用相同条件查询,返回的行数不一致 | 事务 A 用WHERE id>10查询到 5 条数据;期间事务 B 插入了一条id=15的数据并提交;事务 A 再次用相同条件查询,得到 6 条数据,仿佛出现了 "幻觉" |
SQL 标准的 4 个隔离级别
SQL 标准定义了 4 个从低到高的隔离级别,级别越高,数据安全性越强,并发性能越差。InnoDB 的默认隔离级别为可重复读(RR)。
表格
| 隔离级别 | 英文全称 | 解决的问题 | 存在的问题 | 适用场景 |
|---|---|---|---|---|
| 读未提交 | Read Uncommitted(RU) | 无 | 脏读、不可重复读、幻读 | 几乎无生产使用场景 |
| 读已提交 | Read Committed(RC) | 脏读 | 不可重复读、幻读 | Oracle、SQL Server 默认级别,适合对实时性要求高的场景 |
| 可重复读 | Repeatable Read(RR) | 脏读、不可重复读 | 理论上存在幻读,InnoDB 通过临键锁解决了幻读 | InnoDB 默认级别,生产环境通用 |
| 串行化 | Serializable | 脏读、不可重复读、幻读 | 完全串行执行,并发性能极差 | 仅对数据安全性要求极高、无并发的场景 |
底层实现原理:MVCC + 锁机制
隔离性的底层实现分为两大核心:MVCC(多版本并发控制) 实现读写不阻塞,提升并发性能;锁机制 实现写写互斥,保证数据安全。
1. 锁机制
InnoDB 提供了多粒度的锁,核心分类如下:
- 按锁的功能划分
- 共享锁(S 锁 / 读锁):多个事务可以同时对同一行数据加 S 锁,加锁后只能读不能修改;
- 排他锁(X 锁 / 写锁):只有一个事务能对同一行数据加 X 锁,加锁后其他事务不能加任何锁,也无法修改、读取该行数据。
- 按锁的粒度划分
- 行级锁:仅锁定目标行数据,锁粒度最细,并发性能最高,InnoDB 核心使用;
- 表级锁:锁定整个表,锁粒度最粗,并发性能极差,仅特殊场景使用;
- 临键锁(Next-Key Lock):InnoDB RR 级别下的默认行锁算法,是「行锁 + 间隙锁」的组合,锁定一个左开右闭的区间,不仅锁定已存在的数据行,还锁定区间内的间隙,防止其他事务插入符合条件的数据,从根本上解决了幻读问题。
2. MVCC(多版本并发控制)
MVCC 是 InnoDB 实现隔离性的核心,核心价值是不加锁就能实现读写不阻塞,大幅提升数据库的并发性能,仅在 RC 和 RR 两个隔离级别下生效。
-
实现基础
- 每行数据的隐藏列:InnoDB 会为每一行数据添加 3 个隐藏列,核心两个为:
DB_TRX_ID:最后一次修改该行数据的事务 ID;DB_ROLL_PTR:回滚指针,指向该行数据在 undo log 中的历史版本。
- undo log 历史版本链:每次修改数据,都会在 undo log 中记录修改前的版本,通过回滚指针形成一条完整的版本链。
- Read View(读视图):事务执行 SELECT 时生成的一致性读视图,用于判断数据的哪个版本对当前事务可见。
- 每行数据的隐藏列:InnoDB 会为每一行数据添加 3 个隐藏列,核心两个为:
-
核心工作逻辑事务执行 SELECT 时,会生成 Read View,根据规则判断版本链中的数据是否可见,只读取符合可见规则的版本,无需加锁,也不会被写操作阻塞。
-
RC 与 RR 隔离级别的核心差异两个隔离级别的核心区别,在于 Read View 的生成时机:
- RC 级别 :事务内每次执行 SELECT 都会生成一个全新的 Read View,因此能读到其他事务已经提交的最新数据,所以会出现不可重复读;
- RR 级别 :事务内只有第一次执行 SELECT 时生成 Read View,后续所有 SELECT 都复用这个视图,因此同一个事务内读到的数据版本始终一致,实现了可重复读。
2.4 持久性(Durability)
核心定义
持久性指一个事务一旦提交成功,它对数据库的修改就会永久保存到磁盘中,后续的任何操作、服务器宕机、断电、系统崩溃,都不会导致这次提交的修改丢失。
核心价值
是数据库数据可靠性的底线,保证已提交的数据不会因为系统故障而丢失。
底层实现原理:redo log + WAL 预写日志 + 两阶段提交
1. 核心痛点:直接刷磁盘的性能问题
InnoDB 的数据存储在磁盘的「数据页」中(默认每页 16KB),如果每次修改数据都直接把数据页刷到磁盘,属于随机 IO,性能极差,完全无法支撑高并发场景。
为了解决这个问题,InnoDB 采用了 WAL(Write-Ahead Logging,预写日志) 机制,核心逻辑是:先写日志,再写内存,最后异步刷磁盘。只要日志写入磁盘成功,事务就算提交成功,就算数据库宕机,重启后也能通过日志恢复数据,保证持久性。
2. redo log(重做日志)
redo log 是 WAL 机制的核心,也是实现持久性的基础:
- 本质:物理日志,记录的是「某个数据页在某个偏移量做了什么修改」,不是 SQL 逻辑,宕机恢复速度极快;
- 写入特性:循环写入,固定大小配置(默认 4 个文件,每个 1GB,总计 4GB),写满后会将日志对应的修改刷到磁盘的数据页中,再循环复用;
- 归属:InnoDB 引擎特有,仅用于 InnoDB 数据的崩溃恢复。
3. 关键刷盘策略
InnoDB 通过 innodb_flush_log_at_trx_commit 参数控制 redo log 的刷盘时机,直接决定了持久性和性能的平衡:
- 值为 1(默认值):每次事务提交,都会将 redo log 从内存刷到磁盘并持久化。宕机不会丢失任何已提交的数据,安全性最高,生产环境必须使用该配置;
- 值为 0:每秒将 redo log 刷到磁盘一次,事务提交时不刷盘。数据库宕机最多丢失 1 秒的数据,性能最高,安全性极低,生产环境禁止使用;
- 值为 2:事务提交时,将 redo log 写入操作系统缓存,每秒刷到磁盘一次。数据库宕机不会丢失数据,服务器宕机最多丢失 1 秒的数据,性能优于 1,安全性低于 1,非核心场景可使用。
4. 两阶段提交(2PC)
MySQL 分为 Server 层和引擎层,Server 层有 binlog(归档日志,用于主从复制和数据归档恢复),InnoDB 有 redo log。为了保证两个日志的一致性,避免主从数据不一致,事务提交采用两阶段提交机制:
- 准备阶段(Prepare):InnoDB 执行数据修改,将 redo log 写入磁盘,标记为「Prepare 准备状态」;
- 提交阶段(Commit):Server 层将 binlog 写入磁盘并持久化,随后向 InnoDB 发送 commit 指令,InnoDB 将 redo log 标记为「Commit 提交状态」,事务正式提交完成。
两阶段提交的核心价值:保证 redo log 和 binlog 要么都写入成功,要么都失败,不会出现一个日志有记录、另一个没有的情况,从根本上避免了数据不一致、主从同步异常的问题。
三、事务的生命周期与基础语法
3.1 事务的基础语法
-- 1. 开启事务(两种写法等价)
BEGIN;
-- 或
START TRANSACTION;
-- 2. 执行事务内的SQL操作(增删改查)
UPDATE account SET balance = balance - 100 WHERE user_id = 1;
UPDATE account SET balance = balance + 100 WHERE user_id = 2;
-- 3. 提交事务:所有操作永久生效
COMMIT;
-- 4. 回滚事务:执行失败时,撤销所有操作,恢复到事务开启前的状态
ROLLBACK;
3.2 自动提交机制
MySQL 默认开启自动提交 (autocommit = 1),即:
- 不手动开启事务时,每一条单独的 SQL 语句都是一个独立的事务,执行完成后自动提交;
- 关闭自动提交(
SET autocommit = 0;)后,所有 SQL 都会默认在一个事务中,直到手动执行COMMIT或ROLLBACK才会结束事务。
3.3 隐式提交
执行以下语句时,会触发事务的隐式提交 ,相当于提前执行了 COMMIT,破坏事务的原子性,生产环境需避免在事务中执行:
- DDL 语句:
CREATE/ALTER/DROP TABLE等表结构操作; - 管理类语句:
FLUSH PRIVILEGES、ALTER USER等; - 锁语句:
LOCK TABLES、UNLOCK TABLES等。
四、事务生产最佳实践
-
严格避免长事务 / 大事务长事务会导致 undo log 无法清理,占用大量磁盘空间;锁长时间不释放,引发并发阻塞、死锁;主从同步严重延迟。建议将事务执行时间控制在秒级,禁止分钟级的长事务。
-
最小化事务范围只把需要保证原子性的核心操作放在事务中,不要在事务内执行无关操作,比如远程接口调用、循环等待、大量无关查询,避免事务无故拉长。
-
合理设置隔离级别生产环境优先使用 InnoDB 默认的 RR 级别,无特殊需求不要修改为更低或更高的级别;对数据实时性要求极高的场景,可改为 RC 级别。
-
禁止在事务中混合使用不同存储引擎的表MyISAM 等引擎不支持事务,回滚时无法恢复其数据修改,会导致数据不一致、原子性破坏。
-
避免在事务中执行大量批量操作 大批量的
INSERT/UPDATE/DELETE会拉长事务执行时间,引发锁等待和主从延迟,建议拆分为多个小事务分批执行。 -
异常场景必须回滚 业务代码中,事务执行出现异常时,必须手动执行
ROLLBACK回滚,避免未提交的事务长期持有连接和锁。
五、总结
- 事务是数据库保证数据完整性、一致性、可靠性的核心机制,核心是 ACID 四大特性;
- 原子性由 undo log 实现,保证操作要么全成、要么全败;
- 隔离性由 MVCC + 锁机制实现,保证并发事务互不干扰,平衡安全与性能;
- 持久性由 redo log + WAL 机制 + 两阶段提交实现,保证已提交数据永不丢失;
- 一致性是事务的最终目标,由数据库底层机制和业务代码共同保障。