XIV. Concurrency Control
Lock-Based Protocols
-
可串行化调度是并发控制的基础,而为了确保可串行化,需要通过锁来保护数据库对象.
-
锁(lock) 是一种控制并发访问同一数据项的机制.
-
两种 锁模式(lock mode):
-
exclusive(X):表示数据项可以读和写,用 lock-X 表示. -
shared(S):表示数据项只能读,用 lock-S 表示.
-
-
锁兼容性矩阵(lock-compatiability matrix):
-
如果请求的锁和这个数据项上已有的锁兼容(可通过兼容性矩阵判断),则可以批准这个锁(让事务 持有(obtain) 这个锁).
-
如果一个锁没有被批准,就会产生一个请求事务,等到所有冲突的锁被 释放(release) 后,再批准该锁.
-
对于这个锁兼容性矩阵来说,单个数据项上可以有任意多事务持有 S 锁,但是如果有一个事务持有 X 锁,其他事务就都不可以持有这个事务项的锁(无论是 S 或 X).
-
- 基于锁的协议(lock-based protocol):是对所有事务在请求和释放锁时的一组规则. 限制了可能的调度集合.
The Two-Phase Locking Protocol
-
二阶段锁协议(two-parse locking protocol, 2PL):对于每个事务,分为两个阶段 (1) growing 阶段:每个事务可以请求锁,锁管理器可以 批准(grant) 或 拒绝(deny) 锁请求.(2) shrinking 阶段进允许 释放(release) / 降级(downgrade) 先前获取的锁,而不能获取新的锁.
-
二阶段锁协议可以确保冲突可串行化调度.
-
无法解决死锁的问题.
-
不能避免级联回滚.
-
Example: 二阶段锁协议
-
严格二阶段锁协议(strict two-phase locking protocol):事务必须持有所有 互斥锁(exclusive lock) 直到事务提交/中止.
-
可确保可恢复性:事务可以按照他们提交的顺序进行序列化(TBD:?)
-
可以避免级联回滚.
-
-
强二阶段锁协议(rigorous two-phase locking protocol):事务必须持有所有锁直到事务提交/中止.
- 可确保事务可以按照提交的顺序进行序列化.
-
二阶段锁不是可串行化的必要条件. 存在一些冲突可串行化调度无法通过二阶段锁协议实现.
Two-Phase Locking with Lock Conversions
-
带 锁转换(lock conversion) 的二阶段锁协议:(1) upgrade 阶段:可以申请 X 锁或 S 锁,可以把 S 锁升级为 X 锁.(2) downgrade 阶段:可以释放 X 锁或 S 锁,可以把 X 锁降级为 S 锁.
-
锁转换同样可以确保冲突可串行化.
-
按照我的理解,带锁转换的 2PL 和普通的 2PL 能处理的调度集合其实是相同的.
-
-
带锁转换的二阶段锁协议可以让事务自动获取/释放锁,而不需要显示的锁定调用.当需要 read 时如果没有锁就自动申请 S 锁,当需要 write 时如果没有锁就自动申请 X 锁,如果已经持有 S 锁就升级成 X 锁;当事务提交/中止时自动释放所有锁.
-
锁管理器(lock manager) 用于处理申请和释放锁的请求.
-
锁管理器一般通过称为 锁表(lock table) 的数据结构进行维护,其是一个哈希表套队列.每个数据项有一个队列,用于维护等待获取该数据项的锁的事务.
Graph-Based Protocols
-
图锁协议(graph-based protocol) 是二阶段锁协议的一个替代方案,如果偏序关系(就是说是路径,而不是说边) 存在,那么同时访问 和 的事务必须在访问 之前访问 .
-
这个图是一个有向无环图,称为 数据库图(database graph).
-
使用图锁协议可以避免死锁的出现.
-
-
树锁协议(tree-based protocol) 是图锁协议的一个简化版本.要求如下:
-
只允许使用排他锁.
-
每个事务的第一个锁可以加在任何数据项上,之后如果想要再锁别的数据项,必须已经锁定其父节点.
-
数据项可以在任何时候释放锁.
-
已经被事务锁定并解锁的数据项,不能再次被同一事务锁定.
-
-
比较树锁协议与二阶段锁协议:
-
优点:
- 相比于二阶段锁协议可以更早解锁,并且可保证不会出现死锁.
-
缺点:
-
无法保证可恢复性或无级联回滚.
-
事务可能需要锁定更多的数据项,并带来额外开销.
-
-
二阶段锁协议无法所实现的调度可能通过树锁协议实现,反之亦然.
-
Example: 树协议下的可串行化调度
Deadlock Handling
-
死锁(deadlock):两个(或多个)事物的锁相互等待造成事务无法执行.
-
饥荒(starvation):包括但不限于:
-
一个事务在等一个数据项的 X-lcok,而一群别的事务在等他释放.
-
同一事务因为死锁问题被反复回滚.
-
Example: 死锁
-
死锁处理(deadlock handling):死锁处理分为两种:
-
deadlock prevention(预防)
-
deadlock detection and deadlock recovery(检测并修复)
-
Deadlock Prevention Strategies
-
死锁预防(deadlock prevention):用于确保系统永远不会进入死锁状态.
-
下图是一些预防策略,后面还有三个预防策略.
-
一开始就获得所有锁.
-
timeout-based schema:事务仅在指定时间内等待锁,超时后自动回滚.
- 实现简单,但可能出现饥荒.
-
wait-die schema:(非抢占式)
- 老事务等待新事务;新事务遇到老事务则回滚(自杀).
-
wound-wait schema:(抢占式)
-
老事务强制回滚新事务(创伤);新事务等待老事务.
-
可以避免饥荒.
-
Example: wait-die 与 wound-wait 策略对比
抄骗人纸的时候可以记一下这个例子,还挺深刻的. 
Deadlock Detection & Recovery
-
死锁检测(deadlock detection):死锁可以通过 等待图(wait-for graph) 来描述并检测.当等待图中出现环时说明出现了死锁.
-
死锁恢复(deadlock recovery):检测到死锁之后,必须回滚某些事务以打破死锁.
-
选择成本最小的事务进行回滚,但是如果总是选择同时事务作为牺牲品,则可能出现 饥荒(starvation).应将回滚次数纳入考量以避免饥荒.
-
系统还可以选择完全回滚(中止事务并重新启动他)还是部分回滚事务.
-
Multiple Granularity Locking
-
锁的粒度:
-
粗粒度(coarse granularity):higher in tree:低锁开销,低并发度.
-
细粒度(fine granularity):lower in tree:高锁开销,高并发度.
-
-
意向锁(intention lock):意向锁允许 higher-level node 锁定在共享锁或排他锁上,从而避免对所有子节点进行检查(TBD:?).
-
意向锁的主要作用是用于支持行级锁与表级锁的并存
-
InnoDB 提供了行级锁,而在某些场景下,数据库系统仍需要对整张表加锁,例如 LOCK TABLES 或 ALTER TABLE 操作.在这些场景中,如果没有意向锁机制,系统需要扫描所有行级锁来判断是否可以安全地加表锁,这会严重影响性能
-
通过提供表级的锁定信息,避免了系统去逐行检查是否可以加表锁.
-
-
意向锁模式(intention lock mode):
-
意向共享锁(intention-shared, IS):表示在树的较低层级仅使用共享锁进行显示锁定.
-
意向排他锁(intention-exclusive, IX):表示在树的较低层级使用共享锁或排他锁进行显示锁定.
-
共享且意向排他锁(shared and intention exclusive, SIX):以该节点为跟的子树以共享模式进行锁定,并且在较低层级使用排他模式进行显示锁定.(SIX = S + IX)
-
-
在显示锁定某个节点之前,需要对其所有祖先节点设置意向锁.
-
意向锁模式下的兼容性矩阵:
| IS | IX | S | SIX | X | |
|---|---|---|---|---|---|
| IS | true | true | true | true | false |
| IX | true | true | false | false | false |
| S | true | false | true | false | false |
| SIX | true | false | false | false | false |
| X | false | false | false | false | false |
Example: 意向锁模式下的多粒度锁

-
多粒度锁机制(multiple granularity locking schema):
-
(条件1)必须遵循锁兼容性矩阵.
-
(条件2)必须首先锁定树的根节点,并且可以以任何模式锁定.
-
(条件3)仅当 Q 的父节点目前被 以 IX 或 IS 模式加锁时, 才能以 S 或 IS 模式锁定节点 Q.
-
(条件4)仅当 Q 的父节点目前被 以 IX 或 SIX 模式加锁时, 才能以 X、SIX 或 IX 模式锁定节点 Q.
-
(条件5) 只有在之前没有解锁过任何节点的情况下才能对节点加锁(即,多粒度锁机制中的事务是二阶段的).
-
(条件6) 只有在 Q 的子节点都没有被 加锁的情况下,才能解锁节点 Q.
-
-
锁粒度升级(lock granularity esclation):如果在某一特定级别有太多锁,则切换到更高级别的 S 锁或 X 锁.
Index Locking Protocol
-
幻读(phantom read):一个事务读取某个范围的数据时,另一个事务在该范围内插入/删除一以些数据,导致第一个事务再次读到了一些不存在(或读不到应该存在的数据)的数据.
-
如果只使用 元组锁(tuple lock),就可能出现幻读.
- 除非对整个表枷锁,但这样会大大降低并发度,不可行.
-
索引锁协议(index locking protocol) 可以避免出现幻读.
-
每个关系必须至少有一个索引.
-
事务只能在通过一个或多个索引在关系中找到元组后才能访问元组.
-
执行 查找(lookup) 操作的事务 必须以 S 模式锁住其访问的所有索引叶节点.
- 即使叶节点不包含任何满足索引查找条件的元组(例如,对于范围查询,叶节点中没有元组在这个范围内).
-
执行插入、更新或删除元组 于关系 的事务 .
-
必须更新关系 锁拥有的所有索引.
-
必须对所有受插入/更新/删除影响的索引的叶节点获取 X 锁.
-
-
必须遵守两阶段锁协议的规则
-
Comments