DBMS  必须提供并发控制机制,并发控制机制是衡量一个 DBMS 性能的重要标志之一

并发控制机制的任务:

  • 对并发操作进行正确调度
  • 保证事务的隔离性
  • 保证数据库的一致性

并发操作带来的数据不一致性:

  • 丢失修改(  lost update)
  • 不可重复读(    non-repeatable read)
  • 读 “脏 ”数据( dirty read )

1.    丢失修改

丢失修改是指事务1 与事务  2 从数据库中读入同一数据并修改,事务 2 的提交结果破坏了事务    1 提交的结果,导致事务  1 的修改被丢失。

2.    不可重复读

不可重复读是指事务1 读取数据后,事务2执行更新操作,使事务1 无法再现前一次读取结果。

事务 1 读取某一数据后:

1.    事务  2 对其做了修改,当事务    1 再次读该数据时,得到与前一次不同的值。

2.    事务 2 删除了其中部分记录,当事务1 再次读取数据时,发现某些记录神密地消失了。

3.    事务 2 插入了一些记录,当事务    1 再次按相同条件读取数据时,发现多了一些记录。

后两种不可重复读有时也称为幻影现象(    phantom row )

3.    读 “脏 ”数据

事务 1 修改某一数据,并将其写回磁盘

事务 2 读取同一数据后

事务 1 由于某种原因被撤消,这时事务    1 已修改过的数据恢复原值

事务 2 读到的数据就与数据库中的数据不一致,是不正确的数据,又称为    “脏 ”数据。

8.2    封锁

一、什么是封锁

封锁就是事务 T 在对某个数据对象(例如表、记录等)操作之前,先向系统发出请求,对其加锁

加锁后事务 T 就对该数据对象有了一定的控制, 在事务 T 释放它的锁之前, 其它的事务不能更新此数据对象。

封锁是实现并发控制的一个非常重要的技术

二、基本封锁类型

8.3    封锁协议

在运用  X 锁和 S 锁对数据对象加锁时,需要约定一些规则:封锁协议(    Locking Protocol )

  • 何时申请    X 锁或 S 锁
  • 持锁时间、何时释放
  • 不同的封锁协议,在不同的程度上为并发操
  • 作的正确调度提供一定的保证

常用的封锁协议:三级封锁协议

1 级封锁协议

事务 T 在修改数据    R 之前必须先对其加    X 锁,直到事务结束才释放

正常结束(  COMMIT )

非正常结束(    ROLLBACK  )

1    级封锁协议可防止丢失修改

在 1 级封锁协议中, 如果是读数据, 不需要加锁的, 所以它不能保证可重复读和不读    “脏 ”数据。

2    级封锁协议

1    级封锁协议  +事务 T 在读取数据    R 前必须先加  S 锁,读完后即可释放    S 锁
2    级封锁协议可以防止丢失修改和读    “脏”数据。    
在 2 级封锁协议中,由于读完数据后即可释放    S 锁,所以它不能保证可重复读。

3    级封锁协议

1 级封锁协议    + 事务 T 在读取数据    R 之前必须先对其加    S 锁,直到事务结束才释放

3    级封锁协议可防止丢失修改、读脏数据和不可重复读。

8.4    活锁和死锁

1.    死锁的预防

( 1)一次封锁法

要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行一次封锁法存在的问题:降低并发度

扩大封锁范围

将以后要用到的全部数据加锁,势必扩大了封锁的范围,从而降低了系统的并发度难于事先精确确定封锁对象

数据库中数据是不断变化的,原来不要求封锁的数据,在执行过程中可能会变成封锁对象,所以很难事先精确地确定每个事务所要封锁的数据对象

解决方法:将事务在执行过程中可能要封锁的数据对象全部加锁,这就进一步降低了并发度。

( 2)顺序封锁法

顺序封锁法是预先对数据对象规定一个封锁顺序,所有事务都按这个顺序实行封锁。

顺序封锁法存在的问题:

  • 维护成本高:

数据库系统中可封锁的数据对象极其众多,并且随数据的插入、删除等操作而不断地变化,要维护这样极多而且变化的资源的封锁顺序非常困难,成本很高

  • 难于实现:

事务的封锁请求可以随着事务的执行而动态地决定,很难事先确定每一个事务要封锁哪些对象,因此也就很难按规定的顺序去施加封锁。

2.    死锁的诊断与解除允许死锁发生解除死锁

由    DBMS 的并发控制子系统定期检测系统中是否存在死锁一旦检测到死锁,就要设法解除

检测死锁:

  • 超时法:

如果一个事务的等待时间超过了规定的时限,就认为发生了死锁

优点:实现简单

缺点:  有可能误判死锁

时限若设置得太长,死锁发生后不能及时发现

  • 等待图法:

用事务等待图动态反映所有事务的等待情况

事务等待图是一个有向图    G=(T,U )

T    为结点的集合,每个结点表示正运行的事务

U    为边的集合,每条边表示事务等待的情况

若 T1 等待 T2,则 T1, T2 之间划一条有向边,从    T1 指向 T2

并发控制子系统周期性地(比如每隔    1 min )检测事务等待图,如果发现图中存在回路,则表

示系统中出现了死锁。

8.5    并发调度的可串行性

几个事务的并行执行是正确的,当且仅当其结果与按某一次序串行地执行它们时的结果相同。

这种并行调度策略称为可串行化(    Serializable)的调度。

可串行性是并行事务正确性的唯一准则

保证并发操作调度正确性的方法

封锁方法:两段锁(    Two-Phase Locking ,简称  2PL)协议

时标方法

乐观方法

8.6    两段锁协议

两段锁协议的内容:

  • 1.    在对任何数据进行读、写操作之前,事务首先要获得对该数据的封锁
  • 2.    在释放一个封锁之后,事务不再获得任何其他封锁。

“两段 ”锁的含义

事务分为两个阶段:

  • 第一阶段是获得封锁,也称为扩展阶段;
  • 第二阶段是释放封锁,也称为收缩阶段。

并行执行的所有事务均遵守两段锁协议,则对这些事务的所有并行调度策略都是可串行化的。

所有遵守两段锁协议的事务,其并行执行的结果一定是正确的,事务遵守两段锁协议是可串行化调度的充分条件,而不是必要条件可串行化的调度中,不一定所有事务都必须符合两段锁协议。

两段锁协议与三级封锁协议

两类不同目的的协议

两段锁协议

保证并发调度的正确性

三级封锁协议

在不同程度上保证数据一致性

遵守第三级封锁协议必然遵守两段协议

8.7    封锁的粒度

一、什么是封锁粒度

X    锁和 S 锁都是加在某一个数据对象上的封锁的对象 :逻辑单元,物理单元

逻辑单元 :  属性值、属性值集合、元组、关系、索引项、整个索引、整个数据库等

物理单元:页(数据页或索引页)    、物理记录等

二、选择封锁粒度的原则

封锁的粒度越    大,小,

系统被封锁的对象    少,多,

并发度    小,高,

系统开销    小,大,

选择封锁粒度:

考虑封锁机构和并发度两个因素,对系统开销与并发度进行权衡:

  • 需要处理多个关系的大量元组的用户事务:以数据库为封锁单位;
  • 需要处理大量元组的用户事务:以关系为封锁单元;
  • 只处理少量元组的用户事务:以元组为封锁单位

8.7.2 多粒度封锁

多粒度树:

  • 以树形结构来表示多级封锁粒度
  • 根结点是整个数据库,表示最大的数据粒度
  • 叶结点表示最小的数据粒度
  • 显式封锁和隐式封锁
  1. 显式封锁 :  直接加到数据对象上的封锁
  2. 隐式封锁 :  由于其上级结点加锁而使该数据对象加上了锁
  3. 显式封锁和隐式封锁的效果是一样的

8.7.3 意向锁

引进意向锁(    intention lock )目的:

  • 提高对某个数据对象加锁时系统的检查效率
  • 对任一结点加基本锁,必须先对它的上层结点加意向锁
  • 如果对一个结点加意向锁,则说明该结点的下层结点正在被加锁