多用户数据库系统
- 允许多个用户同时使用的数据库系统
- 飞机订票数据库系统
- 银行数据库系统
- 特点:
- 在同一时刻并发运行的事务数可达数百上千个
多事务执行方式
- 事务串行执行
- 每个时刻只有一个事务运行,其他事务必须等到这个事务结束之后方能运行
- 不能充分利用系统资源,发挥数据库共享资源的热点
- 交叉并发方式
- 在单处理机系统中,事务的并行执行是这些并行事务的并行操作轮流交叉运行
- 单处理机系统中的并行事务并没有真正地并行运行,但能够减少处理机的空闲时间,提高系统的效率
- 同时并发方式
- 多处理机系统中,每个处理机可以运行一个事务,多个处理机可以同时运行多个事务,实现多个事务真正的并行运行
- 最理想的并发方式,但受制于硬件环境
- 更复杂的并发方式机制
讨论第二种方式
事务并发执行带来的问题
- 会产生多个事务同时存取同一数据的情况
- 可能会存取和存储不正确的数据,破坏事务隔离性和数据库的一致性
数据库管理系统必须提供并发控制机制
并发控制机制是衡量一个数据库管理系统性能的重要标志之一
并发控制概述
事务是并发控制的基本单位
并发控制机制的任务
- 对并发操作进行正确调度
- 保证事务的隔离性
- 保证数据库的一致性
在并发操作情况下,对T1,T2两个事务的操作序列的调度是随机的
并发操作带来的数据不一致性
- 丢失修改
- 不可重复读
- 读“脏”数据
记号
- R(x):读数据x
- W(x):写数据x
丢失修改
两个事务T1和T2读入同一数据并修改,T2的提交,结果破坏了T1提交的结果,导致T1的修改被丢失
不可重复读
不可重复读是指事务T1读取数据后,事务T2执行更新操作,使T1无法再现前一次读取结果
三种情况:
- 事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时,得到与前一次不同的值
- 事务T1按一定条件从数据库中读取了某些数据记录后,事务T2删除了其中部分记录,当T1再次按相同条件读取数据时,发现记录神秘地消失了
- 事务T1按一定条件从数据库中读取某些数据记录后,事务T2插入了一些记录,当T1再次按相同条件读取数据时,发现多了一些记录
- 后两者不可重复读有时也称为幻影现象
读“脏”数据
含义:
- 事务T1修改某一数据,并将其写回磁盘
- 事务T2读取同一数据后,T1由于某种原因被撤销
- 这时T1已修改过的数据恢复原值,T2读到的数据就与数据库中的数据不一致
- T2读到的数据就为“脏”数据,即不正确的数据
数据不一致性:由于并发操作破坏了事务的隔离性
并发控制就是要用正确的方式调度并发操作,使一个用户事务的执行不受其他事务的干扰,从而避免造成数据的不一致性
对数据库的应用有时允许某些不一致性,例如有些统计工作涉及数据量很大,读到一些“脏”数据对统计精度没什么影响,可以降低对一致性的要求以减少系统开销
并发控制的主要技术
- 封锁
- 时间戳
- 乐观控制法
- 多版本并发控制
封锁
什么是封锁
封锁就是事务T在对某个数据对象操作之前,先想系统发出请求,对其加锁
加锁后事务T就对该数据对象有了一定的控制,在事务T释放它的锁之前,其它的事务不能更新此数据对象
封锁是实现并发控制的一个非常重要的技术
基本封锁类型
一个事务对某个数据对象加锁后究竟拥有什么样的控制由封锁的类型决定
基本封锁类型
- 排他锁(X锁)
- 共享锁(S锁)
排他锁
又称为写锁,相互排斥
若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,直到T释放A上的锁
保证其他事务在T锁释放A上的锁之前不能再读取和修改A
共享锁
又称为读锁
若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁
保证其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改
锁的相容矩阵
封锁协议
什么是封锁协议
- 在运用X锁和S锁对数据对象加锁时,需要约定一些规则,这些规则为封锁协议
- 何时申请X锁或S锁
- 持锁时间
- 何时释放
- 对封锁方式规定不同的规则,就形成了各种不同的封锁协议,他们分别在不同的程度上为并发操作的正确调度提供了一定的保证
三级封锁协议
- 一级封锁协议
- 二级封锁协议
- 三级封锁协议
一级封锁协议
事务T在修改数据R之前必须对其加X锁,知道事务结束才释放
- 正常结束(COMMIT)
- 非正常结束(ROLLBACK)
一级封锁协议可防止丢失修改,并保证事务T是可恢复的
在一级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,所以它不能保证可重复读和不读“脏”数据
二级封锁协议
一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后即可释放S锁
二级封锁协议可以防止丢失修改和读“脏”数据
在二级封锁协议中,由于读完数据后即可释放S锁,所以它不能保证可重复读
三级封锁协议
一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放
三级封锁协议可防止丢失修改,读“脏”数据和不可重复度
三级协议的主要区别
- 什么操作想哟啊申请封锁以及何时释放锁(即持锁时间)
不同的封锁协议使事务达到的一致性级别不同
- 封锁协议级别越高,一致性程度越高
活锁和死锁
封锁技术可以有效地解决并行操作的一致性问题,但也带来了一些新的问题
- 死锁
- 活锁
活锁
事务T1封锁了数据R
事务T2又请求封锁R,于是T2等待
T3也请求封锁R,当T1释放了R上的封锁之后系统首先批准了T3的请求,T2仍然等待
T4又请求封锁R,当T3释放了R上的封锁之后系统又批准了T4的请求……
T2有可能永远等待,这就是活锁的情形
避免活锁:采用先来先服务的策略
- 当多个事务请求封锁同一数据对象时
- 按请求封锁的先后次序对这些事务排队
- 该数据对象上的锁一旦释放,首先批准申请队列中第一个事务获得锁
死锁
事务T1封锁了数据R1
T2封锁了R2
T1又请求封锁R2,因T2已封锁了R2,于是T1等待T2释放R2上的锁
接着T2又申请封锁R1,因T1已封锁了R1,T2也只能等待T1释放R1上的锁
这样T1在等待T2,而T2又在等待T1,T1和T2两个事务永远不能结束,形成死锁
两类方法:
- 死锁的预防
- 死锁的诊断与解除
死锁的预防
产生死锁的原因是两个或多个事务都已封锁了一些数据对象,然后又都请求对已为其他事务封锁的数据对象加锁,从而出现死等待
预防死锁的发生就是要破坏产生死锁的条件
预防死锁的方法
- 一次封锁法
- 要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行
- 存在的问题
- 降低系统并发度
- 难于事先精确确定封锁对象
- 数据库中数据是不断变化的,原来不要求封锁的数据,在执行过程中可能会变成封锁对象,所以很难事先精确地确定每个事务要封锁的数据对象
- 解决方法:将事务在执行过程中可能要封锁的数据对象全部加锁,这就进一步降低了并发度
- 顺序封锁法
- 顺序封锁法是预先对数据对象规定一个封锁顺序,所有事务都按这个顺序实行封锁
- 顺序封锁法存在的问题
- 维护成本
- 数据库系统中封锁的数据对象极多,并且随数据的插入、删除等操作而不断地变化,要维护这样的资源的封锁顺序非常困难,成本很高
- 难以实现
- 事务的封锁请求可以随着事务的执行而动态地决定,很难事先确定每一个事务要封锁哪些对象,因此也就很难按规定的顺序去施加封锁
- 维护成本
死锁的诊断与解除
死锁的诊断
- 超时法
- 如果一个事务的等待时间超过了规定的时限,就认为发生了死锁
- 优点:实现简单
- 缺点
- 有可能误判死锁
- 时限若设置的太长,死锁发生后不能及时发现
- 等待图法
- 用事务等待图动态反映所有事务的等待情况
- 事务等待图是一个有向图G=(T,U)
- T为结点的集合,每个结点表示正运行的事务
- U为边的集合,每条边表示事务等待的情况
- 若T1等待T2,则T1,T2之间划一条有向边,从T1指向T2
-
- a产生了死锁,b产生了死锁
- 并发控制子系统周期性地生成事务等待图,检测事务,如果发现图中存在回路,则表示系统中出现了死锁
- 解除死锁
- 选择一个处理死锁代价最小的事务,将其撤销
- 释放此事务持有的所有的锁,使其他事务能继续运行下去
- 用事务等待图动态反映所有事务的等待情况
并发调度的可串行性
数据库管理系统对并发事务不同的调度可能会产生不同的结果
串行调度是正确的
执行结果等价于串行调度的调度也是正确的,称为可串行化调度
可串行化调度
可串行化调度
- 多个事务的并发执行是正确的,当且仅当其结果与按某一次序串行地执行这些事务时的结果相同
可串行性
- 是并发事务正确调度的准则
- 一个给定的并发调度,当且仅当它是可串行化的,才认为是正确调度
冲突可串行化调度
冲突可串行化
- 一个比可串行化更严格的条件
- 商用系统中的调度器采用
冲突操作:是指不同的事务对同一数据的读写操作和写写操作:
R i (x) 与 W j (x) /* 事务 T i 读 x , T j 写 x ,其中 i≠j*/W i (x) 与 W j (x) /* 事务 T i 写 x , T j 写 x ,其中 i≠j*/
其他操作是不冲突操作
冲突
- 不能交换的动作
- 同一事务的两个操作
- 不同事务的冲突操作
一个调度SC在保证冲突操作的次序不变的情况下,通过交换两个事务不冲突操作的次序得到另一个调度SC',如果SC'是串行的,称调度SC是冲突可串行化的调度
若一个调度是冲突可串行化,则一定是可串行化的调度
可用这种方法判断一个调度是否是冲突可串行化的
冲突可串行化调度是可串行化调度的充分条件,不是必要条件,还有不满足冲突可串行化条件的可串行化调度