Concurrency control of the database

并发控制的主要技术
-
封锁(Locking)
-
时间戳(Timestamp)
-
乐观控制法
锁的类型
- 排他锁 X锁
写锁
- 共享锁S锁读锁
并发事务运行存在的异常问题
并发操作带来的数据不一致性
丢失更新
两个事务同时读写一个数据
事务2的写影响(覆盖)了事务1的写
导致事务1的结果失效
不可重复读
三类不可重复读
-
T1读两次数据得到不同的值,因为T2修改了该数据
-
T1再读的时候数据消失,因为T2删除了该数据
-
T1同条件读时多了一些数据,因为T2插入了一些数据
读“脏”数据
事务2读到了事务1进行中的数据
事务1后来因为某种原因被撤销
事务2读到的数据就是不正确的数据
<u>不一致性是由并发操作引起的</u>
主要原因是<u>并发操作破坏了事务的隔离性</u>
封锁协议
-
一级封锁协议
修改前加X锁,结束才释放 读数据时不加锁,可能 [重复读](#不可重复读) 和 [读“脏数据”](#读“脏”数据) 可以解决丢失更新
-
二级封锁协议
在一级封锁协议之上 读数据前加 [S锁](#锁的类型) 读完之后释放 S锁 可以避免 读“脏数据”
-
三级封锁协议
在二级封锁协议之上 事务结束之后才释放 S锁
活锁
定义
某个事务由于请求封锁,总也得不到锁而长时间处于等待状态
如何解决:
采用先来先服务策略
死锁
定义
同时处于等待状态的2个或多个事务互相封锁了对方请求的资源
使得没有任何事务能够有足够资源运行完毕
解决方法:
预防死锁
一次封锁法:
将事务在执行过程中可能要封锁的数据对象全部加锁
问题:扩大了封锁的范围,降低了并发度
顺序封锁法:
事先规定一个封锁顺序,所有事务都按这个顺序封锁
问题:维护成本高,难以实现
事务重试法:
使用抢占机制和事务回滚
即后申请的事务可能能够抢占已有锁的事务的锁
结论:
操作系统中的预防死锁的策略 <u>不很适合</u> 数据库的特点
DBMS在解决死锁的问题上 采用的是 <u>诊断并解除死锁</u> 的方法
死锁的检测与恢复
即允许死锁先发生
由DBMS的并发控制子系统定期检测系统中是否存在死锁
检测到死锁,选择一个处理死锁代价最小的事务,将其撤消,使其它事务能继续运行。
被撤销的事务回滚
超时法
如果一个事务等待时间超过了限制,就认为发生了死锁
优点:实现简单 缺点:可能误判 或 发现死锁不及时
事务等待图法
用事务等待图动态反映所有事务的等待情况
事务等待图是一个有向图 $G=(V,U) $
若$V1$等待$V2$,则$V1$,$V2$之间画一条有向边,从$V1$指向$V2$
周期性检查图中是否有回路 有回路即出现了死锁
恢复
解除死锁的方法是回滚一个或多个相关事务
即选择一个处理死锁代价最小的事务,将其撤消
释放此事务持有的所有的锁,使其它事务能继续运行下去
两阶段封锁协议
是最常用的一种封锁协议
协议指所有事务必须分两个阶段对数据项加锁和解锁
在对数据进行任何读写操作之前要先获得对该数据的封锁
在释放一个封锁之后,事务不再获得任何其他封锁
**是保证并发调度可串行性的封锁协议**
含义
事务分为两个阶段
第一阶段是获得封锁,也称为扩展阶段
第二阶段是释放封锁,也称为收缩阶段
充分条件而不是必要条件
**所有遵守两阶段封锁协议的事务,其并行执行的结果一定是正确的**
事务遵守两段锁协议是可串行化调度的充分条件,而不是必要条件
可串行化的调度中,不一定所有事务都必须符合两阶段封锁协议
遵守两阶段封锁协议的事务也可能发生死锁
锁表
定义
锁的请求、授予和解除是由数据库系统的锁管理器(Lock Manager) 完成
锁管理器为目前已加锁的数据项维护一个记录链表
每个锁请求为一个记录,按请求的到达顺序排序这个表称为**锁表**
锁表将数据库元素和封锁信息联系在一起
多粒度封锁
封锁粒度
定义
封锁对象的大小称为封锁粒度
封锁对象可以是逻辑单元(属性至数据库)
也可以是物理单元(数据页)
多粒度封锁
定义
在一个系统中同时支持多种封锁粒度供不同的事务选择
原则
选择封锁粒度考虑封锁机构和并发度两个因素
对系统开销与并发度进行权衡
| 封锁的粒度
| 大
$\uparrow$
| 小 $\downarrow$ | | ---------------- | ---------------- | --------------- | | 系统被封锁的对象 | 少
$\downarrow$ | 多 $\uparrow$
| | 并发度
| 低
$\downarrow$ | 高 $\uparrow$
| | 系统开销
| 小
$\downarrow$ | 大 $\uparrow$
|
<u>多个关系</u>的<u>大量元组</u>的用户事务
$\to$
以数据库为封锁单位
需要处理<u>大量元组</u>的用户事务
$\to$
以关系为封锁单元
只处理<u>少量元组</u>的用户事务
$\to$
以元组为封锁单位
多粒度树
以树形结构来表示多级封锁粒度
根节点是整个数据库,表示最大的数据粒度
叶结点表示最小的数据粒度
多粒度封锁协议
-
允许多粒度树中的每个结点被独立地加锁
-
对一个结点加锁意味着
这个结点的所有后裔结点也被加以同样类型的锁
- 在多粒度封锁中一个数据对象可能以两种方式封锁,封锁的效果是一样的
显式封锁:直接加到数据对象上的封锁
隐式封锁:由于其上级结点加锁而使该数据对象加上了锁
检查封锁冲突:
意向锁
目的
提高对某个数据对象加锁时系统的检查效率
定义
对任一结点加基本锁,必须先对它的上层结点加意向锁
常用意向锁
意向共享锁 IS锁
表示它的后裔结点拟(意向)加S锁
意向排它锁 IX锁
表示它的后裔结点拟(意向)加X锁
共享意向排它锁 SIX锁
表示对它加S锁,再加IX锁
意向锁的相容矩阵
| T1\T2
| S
| X
| IS
| IX
| SIX
| 空
| | ------- | ------------ | ------------ | ------------ | ------------ | ------------ | ------- | | S
| $\surd$
| $\mathsf{X}$ | $\surd$
| $\mathsf{X}$ | $\mathsf{X}$ | $\surd$ | | X
| $\mathsf{X}$ | $\mathsf{X}$ | $\mathsf{X}$ | $\mathsf{X}$ | $\mathsf{X}$ | $\surd$ | | IS
| $\surd$
| $\mathsf{X}$ | $\surd$
| $\surd$
| $\surd$
| $\surd$ | | IX
| $\mathsf{X}$ | $\mathsf{X}$ | $\surd$
| $\surd$
| $\mathsf{X}$ | $\surd$ | | SIX | $\mathsf{X}$ | $\mathsf{X}$ | $\surd$
| $\mathsf{X}$ | $\mathsf{X}$ | $\surd$ | | 空
| $\surd$
| $\surd$
| $\surd$
| $\surd$
| $\surd$
| $\surd$ |
锁的强度
锁的强度是指它对其他锁的排斥程度
一个事务在申请封锁时以强锁代替弱锁是安全的,反之则不然
偏序关系 $X \succ SIX \succ S \parallel IX \succ IS \succ 空$
事务的隔离级别
定义
事务准备接受不一致数据的级别称为隔离级别
隔离级别是一个事务必须与其它事务进行隔离的程度
| 隔离级别
| 低
$\downarrow$ | 高
$\uparrow$ | | ---------- | -------------------- | ------------------ | | 并发
| 增加
$\uparrow$
| 降低
$\downarrow$ | | 数据正确性 | 降低
$\downarrow$
| 保证
$\uparrow$
|
申请封锁时应该按自上而下的次序进行
释放封锁时则应该按自下而上的次序进行