我们进入《DDIA》第7章:事务(Transactions)。这是全书中最核心的一章之一,也是理解数据库和分布式系统一致性保障机制的关键。
第7章:事务(Transactions)
一、事务的基本定义
一个事务就是一组操作的组合,这些操作要么全部成功,要么全部失败,系统状态不会处于“中间状态”。
Martin 用这个模型描述事务特性:ACID
ACID 四大特性:
特性 | 含义 | 解释 |
---|---|---|
A - 原子性(Atomicity) | 要么全做,要么全不做 | 用日志或回滚机制保证 |
C - 一致性(Consistency) | 操作后数据仍满足约束 | 由开发者和数据库共同维护 |
I - 隔离性(Isolation) | 并发执行时相互不影响 | 后面会详讲 |
D - 持久性(Durability) | 提交后数据不会丢 | 写入日志、磁盘刷盘保证 |
二、并发执行带来的问题
多个事务同时执行会带来竞态条件和数据冲突,不加限制会出现这些问题:
问题 | 示例 | 说明 |
---|---|---|
脏读 | 读到未提交的数据 | T1 修改数据,T2 马上读 |
不可重复读 | 前后两次读取结果不同 | T1 连续两次读,T2 在中间改了数据 |
幻读 | 读到数据行数变化 | T1 查询符合条件记录数,T2 插入新记录 |
三、事务的隔离级别(Isolation Levels)
不同数据库支持的隔离级别不同,常见的四个等级如下:
隔离级别 | 允许问题 | 实现方式 |
---|---|---|
读未提交(Read Uncommitted) | 脏读 | 最低级别,几乎不隔离 |
读已提交(Read Committed) | 不可重复读 | Oracle 默认,防脏读 |
可重复读(Repeatable Read) | 幻读 | MySQL 默认,防不可重复读 |
可串行化(Serializable) | 无并发问题 | 最严格,性能开销最大 |
Martin 强调:可串行化 ≠ 串行执行,有智能调度算法实现高性能的串行化。
四、实现隔离性的三种机制
1. 锁机制(2PL - 两阶段锁)
- 读写时加锁,保证串行效果
- 容易死锁、性能差
2. MVCC(多版本并发控制)
- 数据的每次修改都生成新版本,读操作只读特定版本
- 写不阻塞读,读不阻塞写
- 常见于 PostgreSQL、MySQL InnoDB、Oracle
3. 可串行化调度器(Serializable Schedulers)
- 用验证器/冲突图分析事务是否冲突
- 比如:Serializable Snapshot Isolation (SSI)
五、分布式事务的挑战
Martin 写得很清楚:在分布式系统中实现事务非常困难。
原因:
- 数据跨多个节点
- 网络可能丢包、延迟、分区
- 无法同步原子提交
解决方案:
两阶段提交(2PC)
- 协调者(Coordinator)先发“准备提交”给所有节点
- 所有节点都同意后,再发“正式提交”
问题:
- 如果协调者挂了,系统可能一直卡住(blocking)
三阶段提交(3PC)
- 增加“预提交”阶段,理论上解决 2PC 挂起问题,但代价更高
补偿事务(Sagas)
- 每个步骤都有对应的回滚操作
- 适合异步链式流程(比如订单系统)
六、实际数据库的事务支持差异
数据库 | 默认隔离级别 | 实现机制 |
---|---|---|
MySQL InnoDB | 可重复读 | MVCC |
PostgreSQL | 可串行化可选 | MVCC + SSI |
Oracle | 读已提交 | MVCC |
MongoDB | 单文档事务(后期支持多文档) | 日志 +锁 |
Cassandra | 无事务 | 应用层负责一致性 |
七、本章关键观点总结
- 事务是构建“正确系统”的基石
- 但在分布式环境中,完美事务 ≠ 高可用性
- 所以实践中需要权衡:
- 局部事务(本地原子性)
- 最终一致性(容忍短暂不一致)
- 幂等性 + 补偿机制
Martin 的经典语录:
“一致性是系统的可预测性,而事务是达成一致性的强大工具。”
下一章《第8章:分布式系统的挑战》会深入讨论现实中的网络分区、时钟漂移、CAP 原则等,这些是构建真实可用系统绕不开的“真相”。