12. 并发控制
### 主要内容
- 并发控制概述
- 封锁
- 封锁协议
- 活锁和死锁
- 并发调度的可串行性
- 两段锁协议
- 封锁的粒度
### 并发执行方式
- 串行执行: 每时刻只有一个事务运行, 资源利用率低
- 交叉并发: 单处理机系统, 并行事务轮流交叉运行
- 同时并发: 多处理机系统, 真正的并行运行
### 并发控制概述
- 并发操作可能破坏事务的 ACID 特性
- 主要是隔离性和一致性
- 示例: 飞机订票系统
- 两个事务同时读入同一余额
- 各自卖出一张票并写回
- 结果: 卖出两张票, 余额仅减少 1
### 数据不一致性
- 丢失修改: 两个事务同时修改, 后一个覆盖了前一个
- 不可重复读: 事务读取两次, 中间被其他事务修改, 结果不同
- 幻影现象: 按条件读取时, 记录数量发生变化 (插入或删除)
- 读“脏”数据: 读取了未提交的数据, 该数据随后被回滚
### 封锁
- 事务在操作数据前, 向系统申请加锁
- **排他锁** (X 锁):又称写锁
- 只允许本事务读取和修改
- 其他事务不能加任何锁
- **共享锁** (S 锁):又称读锁
- 允许本事务读取但不能修改
- 其他事务只能加 S 锁, 不能加 X 锁
### 封锁相容矩阵
- X 锁与任何锁都不相容 (包括 X 锁和 S 锁)
- S 锁与 X 锁不相容
- S 锁与 S 锁相容
- 意味着允许多个事务同时读取同一数据
### 一级封锁协议
- 修改数据前必须加 X 锁
- 事务结束才释放
- 效果: 防止丢失修改
- 不足: 不能保证可重复读, 不能防止读“脏”数据
### 二级封锁协议
- 在一级协议基础上增加
- 读取数据前必须加 S 锁
- 读完后即可释放 S 锁
- 效果: 防止丢失修改, 防止读“脏”数据
- 不足: 不能保证可重复读
### 三级封锁协议
- 在一级协议基础上增加
- 读取数据前必须加 S 锁
- 事务结束才释放 S 锁
- 效果: 防止丢失修改, 防止读“脏”数据, 防止不可重复读
### [活锁](../games/dist/livelock.html)
- 事务请求封锁, 但系统不断批准后续到达的请求
- 该事务永远处于等待状态
- 解决: 采用先来先服务策略
### [死锁](../games/dist/deadlock.html)
- 两个或多个事务互相等待对方持有的锁
- 永远不能结束
- 预防: 一次封锁法, 顺序封锁法
- 诊断: 超时法, 等待图法 (存在回路即死锁)
- 解除: 选择代价最小的事务撤销
### 并发调度的可串行性
- 多个事务的并发执行是正确的, 当且仅当其结果与按某一次序串行执行的结果相同
- 可串行性是并发事务正确调度的准则
- 冲突操作: 读-写, 写-写
- 冲突可串行化: 通过交换不冲突操作次序得到串行调度
### 两段锁协议 (2PL)
- 所有事务必须分两个阶段对数据项加锁和解锁
- 扩展阶段: 申请并获得封锁, 不能释放任何锁
- 收缩阶段: 释放封锁, 不能再申请任何锁
- 若所有事务遵守 2PL, 则调度一定是可串行化的
- 遵守 2PL 的事务仍可能发生死锁
### 封锁的粒度
- 封锁对象的大小
- 逻辑单元: 属性, 元组, 关系, 数据库
- 物理单元: 页, 块
- 粒度大: 并发度低, 开销小
- 粒度小: 并发度高, 开销大
- 多粒度封锁: 同时支持多种封锁粒度
### 意向锁
- 为了高效检查多粒度树中的冲突
- 含义: 如果对一个结点加意向锁, 说明该结点的下层结点正在被加锁
- IS 锁: 后裔结点拟加 S 锁
- IX 锁: 后裔结点拟加 X 锁
- SIX 锁: 对它加 S 锁, 再加 IX 锁
- 加锁顺序: 自上而下 (先加上层意向锁)
- 解锁顺序: 自下而上
### 12. 并发控制
- 举例说明并发操作可能导致的三类数据不一致性.
- 阐述排他锁和共享锁的区别.
- 比较一级、二级和三级封锁协议在操作方式和一致性保证上的主要区别.
- 什么是数据库中的死锁?
- 为什么说可串行化调度是并发事务正确调度的准则?
- 为什么遵守 2PL 协议能保证调度是可串行化的?
----
[ 11.1 数据库恢复技术](dbpa-11-1.html#/overview)
[| 练习 |](dbpa-exec.html)
[ 14.1 数据库发展概述](dbpa-14-1.html#/overview)