可恢复调度
对于每对事务Ti和Tj,如果Tj读取了之前由Ti所写的数据项,则Ti先于Tj提交。
无级联调度
对于每对事务Ti和Tj,如果Tj读取了先前由Ti所写的数据项,则Ti必须在Tj这一读操作前提交。
锁
定义:令{T0,T1,.....,Tn}是参与调度S的一个事务集,如果存在数据项Q,使得Ti在Q上持有A型锁,后来,Tj在Q上持有B型锁,且comp(A,B)=false,则我们称在S中Ti先于(precede)Tj,记为Ti->Tj。如果Ti->Tj,这一居先意味着在任何等价的串行调度中,Ti必须出现在Tj之前。指令之间的冲突对应于锁类型之间的不相容性。
如果调度S是那些遵从*协议规则的事务集的可能调度之一,我们称调度S在给定的*协议下是合法的(legal)。当且仅当其所有合法的调度为冲突可串行化时,我们称一个*协议保证冲突可串行性;换句话说,对于任何合法的调度,其关联的关系是无环的。
两阶段锁
要求每个事务分两个阶段提出加锁和解锁申请。
- 增长阶段(growing phase):事务可以获得锁,但不能释放锁。
- 缩减阶段(shrinking phase):事务可以释放锁,但不能获得新锁。
缺点:依旧存在级联回滚
严格两阶段*协议(strict two-phase locking protocol)
要求事务所有的排他锁必须在事务提交后方可释放,避免级联回滚。
强两阶段*协议(rigorous two-phase locking protocol)
要求事务提交之前不得释放任何锁。
树形协议(tree protocol)
要求所有的数据项集合D={d1,d2,...,dn}满足偏序->:如果di->dj,则任何既访问di对访问dj的事务必须先访问di,然后访问dj。这种偏序可以是数据的逻辑或物理组织的结果,也可以只是为了并发控制而加上的。
- Ti第一个加锁可以对任何数据项进行。
- 此后,Ti对数据项Q加锁的前提是Ti当前持有Q的父项上的锁。
- 对数据项解锁可以随时进行。
- 数据项被Ti加锁并解锁后,Ti不能再对该数据项加锁。
不保证可恢复性和无级联回滚。为了保证可恢复性和无级联回滚,可以将协议修改为在事务结束前不允许释放排他锁。直到事务结束前一直持股排他锁降低了并发性。
这里有一个提高并发性的替代方案,但它只保证可恢复性:为每一个发生了未提交写操作的数据项,我们记录是哪个事务最后对它执行了操作,当事务Ti执行了对未提交数据项的读操作,我们就在最后对该数据项执行了写操作的事务上记录一个Ti的提交依赖(commit dependency),在有Ti依赖的所有事务提交完成之前,Ti不能提交。如果其中一个事务中止,Ti也必须中止。
多粒度锁
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 | flase | false |
对数据项Q加锁规则:
- 事务Ti必须遵从锁类型相容函数
- 事务Ti第一次*树的根结点,并且可以加任意类型的锁
- 仅当Ti当前对Q的父节点具有IX或IS锁时,Ti对结点Q可加S或者IS锁
- 仅当Ti当前对Q的父节点具有IX或SIX锁时,Ti对结点Q可加X、SIX或者IX锁
- 仅当Ti未曾对任何节点解锁时,Ti可对结点加锁(也就是说,Ti是两阶段的)
- 仅当Ti当前不持有Q的子节点的锁时,Ti可对节点Q解锁
多粒度协议要求加锁自顶向下的顺序(根到叶),而锁的释放则按自底向上的顺序(叶到根)