第12章 事务处理 — 数据库设计与实践
来源:田浩然上传的资料 / 0A102 数据库设计与实践 / chap12 事务处理.ppt 教师:陈立军 状态:partial_extraction(图片型 PPT,文本提取有限,基于数据库事务处理高级知识体系 + 课程框架整理) 关联章节:chap09-事务-数据库设计与实践(第9章事务基础)
一、本章概述
本章是第9章”事务”的进阶内容,深入探讨事务处理的实现机制与高级技术。第9章侧重事务的基本概念和ACID特性,本章则聚焦于事务管理的内部原理、并发控制的具体实现算法、以及分布式事务等高级主题。
本章与第9章的关系:
- 第9章:事务是什么 → 概念、特性、状态、SQL语法
- 第12章:事务怎么实现 → 并发控制算法、恢复技术、分布式事务
二、并发控制技术
2.1 基于锁的协议(Lock-Based Protocols)
锁的类型
| 锁类型 | 符号 | 兼容性 | 说明 |
|---|---|---|---|
| 共享锁(Shared) | S | 与S兼容,与X互斥 | 读操作,多个事务可同时持有 |
| 排他锁(Exclusive) | X | 与S、X都互斥 | 写操作,只能有一个事务持有 |
锁相容性矩阵:
| S | X | |
|---|---|---|
| S | ✓ | ✗ |
| X | ✗ | ✗ |
两阶段封锁协议(2PL, Two-Phase Locking)
定义:所有事务分两个阶段提出加锁和解锁请求:
- 增长阶段(Growing Phase):事务可以获得任何锁,但不能释放任何锁
- 缩减阶段(Shrinking Phase):事务可以释放任何锁,但不能获得任何锁
定理:若所有事务均遵守两阶段封锁协议,则这些事务的所有冲突可串行化调度都是可能的。
2PL 的变体:
- 严格两阶段封锁(Strict 2PL):事务持有的排他锁必须在事务提交后才能释放
- 强两阶段封锁(Rigorous 2PL):事务持有的所有锁都必须在事务提交后才能释放
严格2PL保证了无级联回滚,是实际数据库系统中最常用的封锁协议。
锁的粒度与多粒度封锁
- 锁粒度:可以锁定的数据单元大小(字段、记录、页面、表、数据库)
- 多粒度封锁:允许系统中存在不同大小的封锁单元
- 意向锁(Intention Lock):
- 意向共享锁(IS):表示事务要在节点的下层获取共享锁
- 意向排他锁(IX):表示事务要在节点的下层获取排他锁
- 共享意向排他锁(SIX):节点上加共享锁,同时还要加意向排他锁
2.2 基于时间戳的协议(Timestamp-Based Protocols)
基本思想:每个事务在开始时获得一个唯一的时间戳,系统按照时间戳的顺序来保证事务的可串行化。
时间戳产生方式:
- 系统时钟:使用事务开始时的系统时间
- 逻辑计数器:使用一个递增的计数器
基本规则:
- 若事务Ti的时间戳 < 事务Tj的时间戳,则系统必须保证Ti在Tj之前执行
- 通过回滚违反顺序的事务来保证可串行化
Thomas 写规则:
- 若事务Ti对数据项Q执行写操作,且TS(Ti) < W-timestamp(Q)(已有更晚的事务写过Q),则Ti的写操作可以被忽略(而不是回滚)
- 这是对时间戳协议的优化,减少了不必要的回滚
2.3 基于验证的协议(Validation-Based Protocols)
也称为乐观并发控制(Optimistic Concurrency Control, OCC)。
三个阶段:
- 读阶段:事务读取数据,执行计算,将更新写入临时工作区
- 验证阶段:检查事务是否满足可串行化条件
- 写阶段:验证通过则将临时工作区的更新写入数据库,否则回滚
适用场景:
- 冲突少的场景(读多写少)
- 短事务
2.4 多版本并发控制(MVCC)
基本思想:每个写操作创建数据项的一个新版本,读操作选择合适的版本读取,从而读写不冲突。
优点:
- 读操作永远不会等待写操作
- 写操作永远不会阻塞读操作
- 特别适合读多写少的场景
实现方式:
- 时间戳排序的MVCC
- 基于2PL的MVCC
主流数据库中的实现:
- MySQL InnoDB:undo日志 + Read View
- PostgreSQL:tuple visibility + xmin/xmax
- Oracle:回滚段(Rollback Segment)
三、数据库恢复技术
3.1 故障类型
| 故障类型 | 原因 | 影响范围 | 恢复方法 |
|---|---|---|---|
| 事务故障 | 逻辑错误、系统错误 | 单个事务 | 事务回滚(UNDO) |
| 系统故障 | 操作系统错误、断电 | 所有未提交事务 | 重启后REDO+UNDO |
| 介质故障 | 磁盘损坏、磁头碰撞 | 整个数据库 | 重装备份 + 日志重做 |
| 计算机病毒 | 恶意程序 | 部分或全部数据 | 备份恢复 + 杀毒 |
3.2 恢复的基本原理
核心思想:冗余 —— 数据库中任何一部分被破坏或不正确的数据可以根据存储在别处的冗余数据来重建。
建立冗余数据的技术:
- 数据转储(备份):定期将数据库复制到另一个存储介质
- 登记日志文件:记录事务对数据库的所有更新操作
3.3 日志文件
日志文件的内容:
- 事务开始标记(
) - 事务结束标记(
/ ) - 更新操作记录(<T, X, old_v, new_v>)
日志文件的作用:
- 事务故障恢复和系统故障恢复必须用日志文件
- 介质故障恢复时,日志文件 + 后备副本可恢复到故障前状态
写日志的原则:先写日志(Write-Ahead Logging, WAL)
- 先写日志文件,后写数据库
- 保证故障发生时,日志中已经记录了足够的信息用于恢复
3.4 恢复策略
事务故障恢复
- 反向扫描日志文件,查找该事务的更新操作
- 对每个更新操作执行逆操作(UNDO)
- 直到读到该事务的开始标记
系统故障恢复
- 正向扫描日志文件,找出所有在故障发生时未提交的事务(放入UNDO队列)和已提交的事务(放入REDO队列)
- 反向扫描日志,对UNDO队列中的事务执行UNDO操作
- 正向扫描日志,对REDO队列中的事务执行REDO操作
介质故障恢复
- 装入最新的数据库后备副本
- 装入相应的日志文件副本
- 对已提交的事务执行REDO操作
3.5 检查点技术(Checkpoint)
问题:系统故障恢复时需要扫描整个日志文件,效率低。
解决方案:定期建立检查点,保存数据库的状态。
检查点时刻的操作:
- 将日志缓冲区中的所有日志记录写入磁盘
- 在日志文件中写入一条检查点记录
- 将数据缓冲区中的所有数据块写入磁盘
- 将检查点记录在日志文件中的地址写入”重新开始文件”
恢复时:只需从最近的检查点开始扫描日志,大大缩短恢复时间。
四、死锁处理
4.1 死锁的概念
死锁:两个或多个事务都在等待对方释放封锁,导致所有事务都无法继续执行的状态。
死锁产生的四个必要条件:
- 互斥条件:资源不能共享,只能由一个事务使用
- 占有和等待条件:事务已占有至少一个资源,又请求其他被占有的资源
- 不剥夺条件:已获得的资源不能被强行剥夺
- 循环等待条件:存在一个事务循环等待链
4.2 死锁预防
一次封锁法:事务在开始时一次性申请所有需要的锁
- 缺点:降低了并发度,难以准确预知需要的锁
顺序封锁法:预先对所有数据规定封锁顺序,所有事务按此顺序申请封锁
- 缺点:维护成本高,难以适应数据变化
4.3 死锁检测与解除
死锁检测:
- 超时法:事务等待时间超过阈值则认为死锁
- 等待图法:维护事务等待图,定期检测是否有环路
死锁解除:
- 选择一个代价最小的事务作为牺牲品
- 回滚该事务,释放其持有的所有锁
- 选择标准:事务的计算代价、已执行时间、剩余时间、持有锁的数量
五、分布式事务
5.1 分布式数据库的特点
- 数据分布性:数据物理分布在多个节点上
- 逻辑整体性:全局应用看来是一个整体
- 节点自治性:每个节点有独立处理能力
5.2 分布式事务的特点
- 事务的执行涉及多个节点
- 需要保证事务在所有节点上的原子性
- 一个分布式事务可以分解为若干个子事务,每个子事务在一个节点上执行
5.3 两阶段提交协议(2PC, Two-Phase Commit)
目标:保证分布式事务的原子性(要么全部提交,要么全部回滚)。
参与者:
- 协调者(Coordinator):发起事务的节点
- 参与者(Participants):参与事务执行的其他节点
第一阶段:准备阶段(Prepare Phase)
- 协调者向所有参与者发送 prepare 消息
- 参与者收到后,执行事务操作(但不提交),写入日志
- 参与者向协调者回复:
- Ready:可以提交
- Abort:不能提交
第二阶段:提交阶段(Commit Phase)
- 如果所有参与者都回复 Ready:
- 协调者发送 commit 消息
- 参与者提交事务,释放资源
- 参与者向协调者发送 ACK
- 如果有任何一个参与者回复 Abort:
- 协调者发送 abort 消息
- 参与者回滚事务
- 参与者向协调者发送 ACK
2PC 的问题:
- 阻塞问题:协调者故障时,参与者可能长时间阻塞
- 单点故障:协调者是单点
5.4 三阶段提交协议(3PC)
在 2PC 的基础上增加了一个阶段,解决阻塞问题:
- CanCommit 阶段
- PreCommit 阶段
- DoCommit 阶段
改进点:参与者在 PreCommit 阶段知道其他参与者都准备好了,即使协调者故障也能继续。
5.5 Paxos 与 Raft 算法
现代分布式系统中,为了解决一致性问题,发展出了更完善的共识算法:
| 算法 | 特点 | 应用场景 |
|---|---|---|
| Paxos | 经典共识算法,理论完善但实现复杂 | Google Chubby、Spanner |
| Raft | Paxos的简化版本,易理解易实现 | etcd、Consul、TiKV |
六、事务隔离级别
6.1 三种数据不一致现象
| 现象 | 描述 | 产生原因 |
|---|---|---|
| 脏读(Dirty Read) | 事务读取了另一个未提交事务修改的数据 | 读-写冲突 |
| 不可重复读(Non-repeatable Read) | 同一事务内两次读取同一数据,结果不同(中间被其他事务修改并提交) | 写-读冲突 |
| 幻读(Phantom Read) | 同一事务内两次查询同一范围,返回的行数不同(中间有其他事务插入/删除) | 范围操作冲突 |
6.2 四个隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 实现方式 |
|---|---|---|---|---|
| 读未提交(Read Uncommitted) | ✓(会出现) | ✓ | ✓ | 几乎不加锁 |
| 读已提交(Read Committed) | ✗(避免了) | ✓ | ✓ | 写加锁,读用快照 |
| 可重复读(Repeatable Read) | ✗ | ✗ | ✓(MySQL InnoDB通过MVCC+间隙锁也避免了) | 2PL / MVCC |
| 串行化(Serializable) | ✗ | ✗ | ✗ | 严格的2PL / 可串行化验证 |
注意:SQL标准中,可重复读级别允许幻读;但在 MySQL InnoDB 的实现中,通过 Next-Key Lock(间隙锁+行锁)在可重复读级别也避免了幻读。
七、本章重点总结
- 并发控制是事务处理的核心:2PL、时间戳、乐观并发控制、MVCC 是四大类方法
- WAL 是恢复技术的基础:先写日志后写数据,保证故障可恢复
- 检查点技术:减少系统故障恢复时需要扫描的日志量
- 死锁:预防 vs 检测与解除,各有取舍
- 分布式事务:2PC 保证原子性,但有阻塞问题;现代系统更多用共识算法
- 隔离级别:是并发控制强度的”档位”,级别越高一致性越好但性能越低
八、与其他知识的关联
- chap09-事务-数据库设计与实践:事务基础概念、ACID特性、SQL事务语法
- chap10-数据库性能调优-数据库设计与实践:事务设计与性能调优的关系
- ../study/分布式系统:分布式一致性与共识算法(待补充)