第12章:事务管理(Transaction Management)
一、导读
12.1 本章学习目标
事务是数据库操作的逻辑单位。本章讲解事务的概念、ACID 属性、并发执行的问题。
本章的核心学习目标包括:
- 理解事务的概念:数据库操作的逻辑单位
- 掌握 ACID 属性:原子性、一致性、隔离性、持久性
- 理解并发执行的问题:丢失更新、脏读、不可重复读、幻读
- 掌握可串行化:冲突可串行化、视图可串行化
- 了解事务隔离级别
12.2 为什么事务管理如此重要
事务管理是数据库系统的核心功能。它保证:
- 原子性:事务要么全部执行,要么全部不执行
- 一致性:事务执行后数据库处于一致状态
- 隔离性:并发事务互不干扰
- 持久性:事务提交后数据永久保存
理解事务管理,对于开发可靠的数据库应用至关重要。
二、核心概念详解
2.1 事务的概念
事务(Transaction)是数据库操作的逻辑单位,由一组操作组成。
BEGIN TRANSACTION;
UPDATE Account SET balance = balance - 100 WHERE id = 'A';
UPDATE Account SET balance = balance + 100 WHERE id = 'B';
COMMIT;2.2 ACID 属性
2.2.1 原子性(Atomicity)
事务中的操作要么全部执行,要么全部不执行。
转账事务:
1. 从 A 账户扣除 100 元
2. 向 B 账户增加 100 元
如果步骤 1 成功但步骤 2 失败,必须回滚步骤 1。实现机制:日志(Undo Log)
2.2.2 一致性(Consistency)
事务执行后,数据库从一个一致状态转换到另一个一致状态。
一致性约束:
- A 账户余额 + B 账户余额 = 总金额
- 转账前后总金额不变实现机制:完整性约束、触发器、应用代码
2.2.3 隔离性(Isolation)
并发事务的执行效果与串行执行相同。
事务 T1:读取 A 的余额
事务 T2:修改 A 的余额
T1 和 T2 并发执行时,T1 应该看到 T2 修改前或修改后的值,
不应该看到中间状态。实现机制:锁、多版本控制
2.2.4 持久性(Durability)
事务提交后,其对数据库的修改永久保存,即使系统崩溃也不会丢失。
实现机制:日志(Redo Log)、检查点
2.3 并发执行的问题
并发事务可能导致以下问题:
2.3.1 丢失更新(Lost Update)
两个事务同时修改同一数据,一个事务的修改被覆盖。
T1: 读取 A = 100
T2: 读取 A = 100
T1: A = A + 10, 写入 A = 110
T2: A = A + 20, 写入 A = 120 ← T1 的更新丢失2.3.2 脏读(Dirty Read)
一个事务读取了另一个未提交事务的修改。
T1: 修改 A = 100
T2: 读取 A = 100 ← 脏读
T1: 回滚,A 恢复为 90
T2: 基于错误的 A = 100 继续执行2.3.3 不可重复读(Non-Repeatable Read)
一个事务两次读取同一数据,结果不同。
T1: 读取 A = 100
T2: 修改 A = 200, 提交
T1: 再次读取 A = 200 ← 不可重复读2.3.4 幻读(Phantom Read)
一个事务两次查询,第二次查询看到了第一次查询没有的行。
T1: 查询所有余额 > 100 的账户,得到 10 行
T2: 插入一个新账户,余额 150, 提交
T1: 再次查询所有余额 > 100 的账户,得到 11 行 ← 幻读2.4 可串行化
可串行化是并发控制的正确性标准。
2.4.1 串行调度
事务一个接一个执行,没有交错。
调度 1:T1 → T2
调度 2:T2 → T12.4.2 冲突可串行化
如果调度可以通过交换相邻的非冲突操作转换为串行调度,则它是冲突可串行化的。
冲突操作:
- 同一数据项的读写
- 同一数据项的写读
- 同一数据项的写写
2.4.3 优先图(Precedence Graph)
优先图用于判断调度是否冲突可串行化:
- 节点:事务
- 边:如果 Ti 的某个操作在 Tj 的某个操作之前,且它们冲突,则有一条从 Ti 到 Tj 的边
如果优先图无环,则调度冲突可串行化。
2.5 事务隔离级别
SQL 标准定义了四种隔离级别:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 |
| READ COMMITTED | 不可能 | 可能 | 可能 |
| REPEATABLE READ | 不可能 | 不可能 | 可能 |
| SERIALIZABLE | 不可能 | 不可能 | 不可能 |
-- 设置隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;三、重要知识点
3.1 事务状态转换
活跃 → 部分提交 → 提交
↓
失败 → 中止3.2 并发控制的实现
- 封锁:共享锁(S 锁)、排他锁(X 锁)
- 两阶段封锁:扩展阶段、收缩阶段
- 时间戳排序:每个事务有唯一的时间戳
- 多版本并发控制(MVCC):维护数据的多个版本
3.3 死锁处理
- 死锁预防:等待-死亡、受伤-等待
- 死锁检测:等待图
- 死锁恢复:回滚事务
四、常见误区与难点
4.1 误区一:"隔离级别越高越好"
隔离级别越高,并发度越低,性能越差。需要根据业务需求选择合适的隔离级别。
4.2 误区二:"事务越长越好"
长事务持有锁的时间长,降低并发度,增加死锁风险。
4.3 难点:理解可串行化
可串行化是抽象的概念,需要通过优先图来理解。
4.4 难点:理解 MVCC
MVCC 通过维护数据的多个版本实现并发控制,实现比较复杂。
五、实践应用
5.1 使用 MySQL 测试事务
-- 开始事务
START TRANSACTION;
-- 执行操作
UPDATE Account SET balance = balance - 100 WHERE id = 'A';
UPDATE Account SET balance = balance + 100 WHERE id = 'B';
-- 提交事务
COMMIT;
-- 或者回滚
ROLLBACK;5.2 设置隔离级别
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;5.3 使用 JDBC 控制事务
Connection conn = DriverManager.getConnection(url, user, password);
conn.setAutoCommit(false); // 关闭自动提交
try {
Statement stmt = conn.createStatement();
stmt.executeUpdate("UPDATE Account SET balance = balance - 100 WHERE id = 'A'");
stmt.executeUpdate("UPDATE Account SET balance = balance + 100 WHERE id = 'B'");
conn.commit(); // 提交事务
} catch (SQLException e) {
conn.rollback(); // 回滚事务
}六、本章小结
第12章讲解了事务管理:
事务 是数据库操作的逻辑单位
ACID 属性 保证事务的可靠性
并发执行 可能导致丢失更新、脏读、不可重复读、幻读
可串行化 是并发控制的正确性标准
隔离级别 提供不同级别的并发控制
后续章节将讲解并发控制的具体实现。