04. 事务 ACID 与隔离级别

数据库事务的 ACID 四大特性实现机制,四种隔离级别与脏读、不可重复读、幻读三种并发异常现象的完整解析。

1. ACID 属性

1.1 四大特性

属性含义InnoDB 实现机制
Atomicity(原子性)事务要么全完成,要么全不完成Undo Log(回滚日志)
Consistency(一致性)事务前后数据库处于一致状态约束、触发器、外键、业务规则
Isolation(隔离性)并发事务互不干扰MVCC + 锁
Durability(持久性)事务提交后数据永久保存Redo Log + WAL(预写日志)

1.2 InnoDB 的事务日志

Undo Log(回滚日志):
  - 记录数据修改前的旧值
  - 事务回滚时,用 Undo Log 恢复数据
  - 作用:原子性 + MVCC 版本链

Redo Log(重做日志):
  - 记录数据修改后的新值
  - 事务提交时,先写 Redo Log,再刷脏页
  - 作用:持久性(崩溃恢复)

Write-Ahead Logging (WAL):
  - 先写日志,再写磁盘
  - 日志顺序写(快) vs 数据页随机写(慢)
  - Redo Log 顺序写提高性能,同时保证不丢数据

2. 四种隔离级别

SQL 标准定义了四种隔离级别,从低到高限制越来越严格,但并发性能逐渐降低。

隔离级别脏读不可重复读幻读InnoDB 实现
READ UNCOMMITTED✅ 允许✅ 允许✅ 允许直接读取最新版本
READ COMMITTED❌ 禁止✅ 允许✅ 允许MVCC(每次读生成 ReadView)
REPEATABLE READ❌ 禁止❌ 禁止⚠️ InnoDB 禁止MVCC + 间隙锁
SERIALIZABLE❌ 禁止❌ 禁止❌ 禁止所有 SELECT 加锁

2.1 三种并发异常

脏读(Dirty Read)

事务 A                    事务 B
  │                        │
  ├──→ 修改余额 = 800
  │     (未提交)          │
  │                        ├──→ 读取余额 = 800  ← 脏读!
  │                        │
  ├──→ ROLLBACK(回滚)
  │     余额恢复 1000       │
  │                        │
  │                        ├──→ 之前读到的 800 是错误数据

不可重复读(Non-repeatable Read)

事务 A                    事务 B
  │                        │
  ├──→ 读取余额 = 1000     │
  │                        ├──→ 修改余额 = 800
  │                        │     COMMIT
  │                        │
  ├──→ 再次读取余额 = 800  ← 同一事务内两次读取结果不同!

幻读(Phantom Read)

事务 A                    事务 B
  │                        │
  ├──→ SELECT COUNT(*) FROM orders  
  │     结果: 10 条          │
  │                        ├──→ INSERT INTO orders ...
  │                        │     COMMIT
  │                        │
  ├──→ SELECT COUNT(*) FROM orders
  │     结果: 11 条  ← 同一事务内两次范围查询行数不同!

2.2 InnoDB 的 RR 如何解决幻读

InnoDB REPEATABLE READ 下:
  - 快照读(普通 SELECT):通过 MVCC + ReadView(事务首次 SELECT 时生成)
    → 整个事务看到一致快照,不会出现幻影行
    
  - 当前读(SELECT ... FOR UPDATE):通过 Next-Key Lock(记录锁 + 间隙锁)
    → 锁定查询范围及间隙,阻止其他事务插入新行

注意:SQL 标准中 RR 允许幻读,但 InnoDB 通过上述机制实际上在大部分场景下解决了幻读。

3. 隔离级别设置

-- 查看当前隔离级别
SELECT @@transaction_isolation;

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

-- 启动事务
START TRANSACTION;
-- 或
BEGIN;

-- 提交 / 回滚
COMMIT;
ROLLBACK;

4. 隔离级别选择建议

场景推荐隔离级别原因
普通 OLTP 应用READ COMMITTED避免锁竞争,Oralce/PostgreSQL 默认
金融交易、对账REPEATABLE READMySQL 默认,防不可重复读
报表统计(快照一致性)REPEATABLE READ事务内数据一致
极端严格同步SERIALIZABLE性能代价高,一般不用

注意:MySQL 默认 RR,Oracle/PostgreSQL/SQL Server 默认 RC。MySQL 的 RR 配合 MVCC 在很多场景下并不比 RC 锁更多。


延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「database」更多文章

  1. 缓存架构演进之路:从单机 Redis 到亿级分布式多级缓存体系
  2. Redis 7.x 重大新特性与架构升级深度解析
  3. Redis 消息队列深度对比:Pub/Sub、Streams 与 Kafka/RabbitMQ 选型指南