-
基本思想
事务T1要修改记录A之前先对A加锁,其他事务不能获得A的锁,从而也不能读取和修改A,直到T1修改完成再释放锁
-
基本的锁类型
(1) 共享锁(S锁)
一个事务T对A加上S锁以后,T只能读取数据A,不能写数据A
(2) 排他锁(X锁)
一个事务T对A加上X锁以后,T既可以读取数据A,也可以写数据A
(3) 加锁方式
当数据A上没有任何锁时,一个事务可以对其加S锁或X锁;
当数据A上有S锁时,其他事务只能对A加S锁;
当数据A上有X锁时,其他事务不能对A加任何锁,只能等X锁被释放
-
封锁协议:
对何时请求锁、何时释放锁做出的规定
(1) 一级封锁协议
事务T在修改数据A前必须加X锁,事务结束时释放。
可以解决 丢失更新的问题(见"一、并发事务运行时存在的可能问题")
(2) 二级封锁协议
在一级封锁协议的基础上,添加:事务T在读数据A前必须加S锁,读完以后释放
可以解决 丢失更新 + 读脏数据 的问题(见"一、并发事务运行时存在的可能问题")
(3) 三级封锁协议
和二级封锁协议的区别:S锁不是读完以后立刻释放,而是事务结束以后再释放
可以解决 丢失更新 + 读脏数据 + 不可重复读 的问题(见"一、并发事务运行时存在的可能问题")
-
活锁
(1) 情况
事务1对A加上S锁,事务2想对A加X锁但是加不上需要等待;此时事务3、事务4……都想对A加上S锁,它们都满足条件。于是,事务2必须等到事务3,4……全部都释放锁以后,才能加X锁,它称为“饿死事务”
(2) 解决办法
将事务加锁请求排队,先来先服务
-
死锁
(1) 情况
事务1对数据A持有S锁,事务2对数据B持有S锁;现在事务1想对数据B加X锁,事务2想对数据A加X锁。它们只能一直等待下去。
(2) 处理死锁的方法
1° 死锁预防:难以实现,效率较低
2° 死锁检测与解除:一般采用这种方法
(3) 死锁检测
所有的事务构成一张有向图,事务构成图的顶点。如果事务Ti申请的锁被Tj占有,则加入一条从Ti指向Tj的边……
如果图中出现回路,代表死锁发生;如果图中没有回路,代表无死锁
(4) 死锁恢复
1° 超时法
申请锁的事务的等待时间如果超过阈值,则该事务回滚重启。
评价:简单、不稳定
2° 事务等待图法
通过画图的方式检测到是否出现死锁,如果死锁发生,则回滚一个或多个相关事务。
回滚原则:使回滚的代价最小
-
锁表
(1) DBMS中的锁管理器负责锁的请求、授予和解除。锁管理器会维持一个链表用于记录事务与锁的信息,称为锁表
(2) 链表中的每个结点的信息包括
1° 事务名
2° 请求锁的类型
3° 授予成功/等待?的标志位
(3) 封锁请求处理
假设事务T请求对数据A加锁:如果A对应的链表为空,则创建新结点并授予锁;如果加的锁类型与已有锁不冲突,则授予锁并加入新结点;如果加的锁类型与已有锁冲突,则加入新结点,标记为等待
(3) 解锁处理
顺着链表,删除当前结点,更新其他结点状态
-
多粒度封锁
(1) 封锁的单元
属性列、元组、表、数据库、数据页、索引页、数据库存储空间等……
(2) 封锁粒度与并行度之间的关系
封锁粒度越小,并行度越大,但需要的锁越多,系统开销越大
(3) 多粒度树
树中的每层结点表示不同的粒度,从树根结点到叶结点,粒度不断减小
(4) 显式锁与隐式锁
1° 多粒度树中一旦某个中间结点加锁,则它所包含的子节点也被加上同类型的锁。
2° 显式锁:被加锁结点对应的锁
隐式锁:结点下面的子节点的锁
3° 显式锁和隐式锁的作用相同,系统检测封锁冲突时,需要从根部到当前结点进行扫描,只有路径上的锁都满足条件不冲突时,才加锁成功
(5) 意向锁
1° 目标:解决隐式锁的问题,防止出现从根本开始检测冲突这种影响效率的操作
2° 对任一结点加锁的时候,顺便将它所有上层的结点加意向锁。检测加锁和加意向锁的过程中是否出现冲突
3° 意向锁的类型
a. 意向共享锁IS:它的下层结点中存在要加S锁的结点
b. 意向排他锁IX:它的下层结点中存在要加X锁的结点
c. 共享意向排他锁SIX:对当前结点加S锁,它的下层结点中存在要加X锁的结点
(6) 各种锁的冲突情况
Y:不冲突 N:冲突
S X IS IX SIX S Y N Y N N X N N N N N IS Y N Y Y Y IX N N Y Y N SIX N N Y N N
IS与IX不冲突的解释:当前一层的结点确实不冲突,由于加锁时判断的是一个链条上加锁都成功才会成功,所以当前层确实不冲突,判断是否冲突的任务交给了其他层,所以当前还是Y
网友评论