以下课程来源于MOOC学习—原课程请见:数据库原理与应用
考研复习
DBMS保证系统中一切事务的原子性、一致性、隔离性和持续性
DBMS必须对事务故障、系统故障和介质故障进行恢复
恢复中最经常使用的技术:数据库转储和登记日志文件
恢复的基本原理:利用存储在后备副本、日志文件和数据库镜像中的冗余数据来重建数据库
常用恢复技术
事务故障的恢复(UNDO)
系统故障的恢复(UNDO + REDO)
介质故障的恢复(重装备份并恢复到一致性状态 + REDO)
提高恢复效率的技术 检查点技术(
Ø可以提高系统故障的恢复效率
Ø可以在一定程度上提高利用动态转储备份进行介质故障恢复的效率)
镜像技术(镜像技术可以改善介质故障的恢复效率)
定义的一个数据库操作序列,这些操作要么全做,要不都不做,不可分割
当多条SQL语句必须当作一个整体执行才能实现它的功能时,需要定义一个事务
目的:维护企业状态 和 数据库状态一致的与数据库交互的程序
事务是数据库系统的逻辑工作单元
begin transaction
sql 语句序列
commit #提交事务,事务中所有操作均成功执行
rollback #回滚事务,事务夭折,不能继续执行,已经执行的更新操作撤销,恢复至原先状态
begin transaction
update accounts set bal=bal-100 where accno='A'
update accounts set bal=bal+100 where accno='b'//转账业务
commit
//只有两种结果:要么提交;要么回滚
原子性:Atomicity
一致性:Consistency
隔离性:Isolation
比如:若在并发执行的一个事务中,同一个查询的两次查询结果不一样, 请问是由于事务没有保持什么特性造成的?
持久性:Durability
对发生故障后,对已经成功的结果进行恢复,保持原子性和持久性
故障类型及原因
1.事务故障:执行过程中发生错误,导致事务夭折
2.系统故障
3.介质故障:使数据存储介质 完全毁坏的硬故障
比如数据库服务器毁坏,瞬时磁场干扰,磁头碰撞损坏,等
破坏持久性(已经更新的结果不能持久写入磁盘中)
4.不一致错误
窃取/不强制的缓冲区管理策略【缓冲器置换算法比如LRU】
1.为了从磁盘上输入事务B 所需要的数据放到内存,需要先将未提交的事务A所处理的数据输出到磁盘上,以腾出空间给B,即B窃取A的空间
2.事务提交狗,更新结果没有及时到磁盘上,即不强制的执行output操作
窃取/不强制的缓冲区管理策略导致:
1.事务提交之前, 部分执行结果可能已经被更新到数据库中
2.事务提交后,事务执行结果并没有立即更新到磁盘中
问:如果缓冲区管理器不采用课件里的窃取/不强制的管理策略,而采用延迟更新策略,即直到事务提交,更新结果才会被写到数据库中,那么发生故障后,系统中不一致错误状态表现是什么?
答:已提交的事务对数据库的更新结果有一部分甚至全部还在缓冲区中,尚未写到磁盘上的数据库中,破坏了事务的持久性。或者已提交的事务对数据库的更新结果不能持久的保存在磁盘上,破坏了事务的持久性
问:更新日志记录:如果缓冲区管理器不采用课件里的窃取/不强制的管理策略,而采用延迟更新策略,即直到事务提交,更新结果才会被写到数据库中,那么更新日志记录里是否仍需要包括更新前的值?
答:不需要,如果磁盘上的数据库中有更新后的值,说明事物已成功提交,因此在恢复时不需要做UNDO操作,也不需要存储更新前的值
每个数据库维护一个日志,记录所有事务对该数据更新操作
格式:1. 以记录为单位的日志文件(事务标识,操作类型,操作对象,更新前数据的旧值,更新后数据的新值);
2.以数据表为单位的日志文件(事务标识,被更新的数据块)
内容:1 各个事务的开始标记;2 各个事务的结束标记;3 各个事务所有的更新操作
作用:1 进行事务故障恢复;2 进行系统故障恢复;3 协助后备副本进行介质故障恢复
注:日志记录的登记社会魂虚必须严格按照各个事务的操作执行的先后
注:更新之前,需要将日志记录先写入
转储是指DBA将整个数据库复制到磁带或另一个磁盘上保存起来的过程,
备用的数据成为后备副本或后援副本
数据转储的使用
数据库遭到破坏后可以将后备副本重新装入;重装后备副本只能将数据库恢复到转储时的状态
转储方法
1.静态转储
优点:实现简单
缺点:1.降低了数据库的可用性 2.转储必须等待正运行的用户事务结束 3.新的事务必须等转储结束
2.动态转储
优点:1.不用等待正在进行的事务结束 2.转储期间允许对数据库进行存取或修改
缺点:不能保证副本中的数据正确有效
**动态转储进行故障恢复,**需要把动态转储期间各事务对数据库的修改活动登记下来,建立日志文件;后备副本加上日志文件才能把数据库恢复到某一时刻的正确状态
海量转储:每次转储全部数据库,又称完全转储
增量转储:只转储上次转储之后,更新过的数据
海量转储与增量转储的比较
1.从恢复角度看,使用海量转储得到的后备副本进行恢复往往更方便
2.如果数据库很大,事务处理又十分频繁,则增量转储更有效
利用静态转储副本和日志文件进行恢复
对上图进行说明:
数据的不一致错误导致
事务故障:事务运行至正常终止点前被终止
恢复方法:由恢复子系统利用日志文件撤销(UNDO)此事务已对数据库进行的修改
注:事务故障的恢复由系统自动完成,对用户是透明的,不需要用户干预
恢复步骤
系统故障:
恢复方法:
注:系统故障的恢复由系统在重新启动时自动完成,不需要用户干预
恢复步骤:
恢复子系统可以定期或不定期地建立检查点,保存数据库状态
T1:在检查点之前提交
T2:在检查点之前开始执行,在检查点之后故障点之前提交
T3:在检查点之前开始执行,在故障点时还未完成
T4:在检查点之后开始执行,在故障点之前提交
T5:在检查点之后开始执行,在故障点时还未完成
数据库镜像
DBMS自动把整个数据库或其中的关键数据复制到另一个磁盘上
DBMS自动保证镜像数据与主数据库的一致性,每当主数据库更新时,DBMS自动把更新后的数据复制过去,如图
出现介质故障时
没有出现故障时
问:系统故障的恢复
如果缓冲区管理器不采用课件里的窃取/不强制的管理策略,而采用延迟更新策略,即直到事务提交,更新结果才会被写到数据库中,那么发生系统故障后,如何进行恢复?
答:已提交的事务用REDO, 未提交的事务用UNDO
多个事务如何一起执行呢?
事务是并发控制的基本单位,如果不考虑隔离性,会发生什么事呢?
丢失数据:两个事务T1、T2同时读入同一数据并修改,T2提交的结果破坏了T1提交的结果,导致T1的修改被丢失
不可重复读:对于数据库中的某个数据,一个事务范围内的多次查询却返回了不同的结果,这是由于在查询过程中,数据被另外一个事务修改并提交了。
脏读:一个事务在处理数据的过程中,读取到另一个为提交事务的数据。
注意:不可重复读和脏读的区别是,脏读读取到的是一个未提交的数据,而不可重复读读取到的是前一个事务提交的数据。
注意:而不可重复读在一些情况也并不影响数据的正确性,比如需要多次查询的数据也是要以最后一次查询到的数据为主。
注意:丢失数据和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而丢失数据针对的是一批数据整体(比如数据的个数)。
不可重复读和幻读是初学者不易分清的概念,我也是看了详细的解读才明白的,总的来说,解决不可重复读的方法是 锁行,解决丢失数据的方式是 锁表。
两个事务T1、T2同时读入同一数据并修改,T2提交的结果破坏了T1提交的结果,导致T1的修改被丢失
事务T1读取某一个数据后,事务T2执行更新操作,使T1无法再现前一次读取结果,包括三种情况:
⑴. T2执行修改操作,T1再次读数据时,得到与前一次不同的值
⑵. T2执行删除操作,T1再次读数据时,发现某些记录神秘的消失了
⑶. T2执行插入操作,T1再次读数据时,发现多了一些记录
(2)(3)发生的不可重复读有时也称为幻影现象。
事务T1修改某一数据并将其写回磁盘,事务T2读取同一数据后,T1由于某种原因被撤销,这时被T1修改过的数据恢复原值,T2读到的数据就与数据库中的数据不一致,则T2读到的数据就为“脏”数据,即不正确的数据。
如何避免发生这种数据不一致的现象?——DBMS必须提供并发控制机制
并发控制机制的任务:对并发操作进行正确调度、保证事务的隔离性、保证数据库的一致性。
并发控制的主要技术有:封锁、时间戳、乐观控制法、多版本并发控制。
问:数据不一致问题:对于“更新丢失”、“脏读”和“不可重复读”三种数据不一致问题,认为哪种是绝不允许发生的?给出理由。
答:应该绝不允许“更新丢失”发生,因为“脏读”和“不可重复读”的后果虽然有时很严重,但有时也是无关紧要的,可以通过控制事务的隔离级别来避免,但是“更新丢失”一旦发生,后面所有需要对丢失的数据进行操作的事务都会受影响,必须重做或者利用数据备份来恢复。
封锁是实现并发控制的一个有效措施,那么什么是封锁呢?
封锁是事务T在对某个数据对象(例如表、记录等操作时)。先向系统发出请求,对其加锁。加锁后事务T就对该数据对象有了一定的控制,在事务T释放它的锁之前,其他事务不能更新此数据对象。
封锁有哪些类型呢?
排他锁:简称X锁(又称写锁),若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何锁。直到T释放A上的锁。
共享锁:简称S锁(又称读锁),若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁为止。
有了封锁的类型,如何加锁才能使并发操作不会出现数据不一致现象呢?
封锁协议:约定了对数据对象何时申请X锁或S锁,持续时间、何时释放等一系列规则。
三级封锁见yaunwen
问:封锁技术实现冲突可串行化:封锁技术是如何实现并发事务的冲突可串行化的?
答:封锁技术通过在数据对象上维护“锁”以防止非可串行化的行为。基于锁的并发控制的基本思想是:当一个事务在对其需要访问的数据对象(例如关系、元组)进行操作之前,先向系统发出请求,获得在它所访问的数据库对象上的锁,以防止其他事务几乎在同一时间访问这些数据并因此引入非可行化的可能。封锁技术可实现冲突可串行化。
封锁会带来哪些问题呢?
避免活锁的简单方法是:采用先来先服务策略
于是T1等待T2释放R2上的锁;接着T2又请求封锁R1,因T1封锁了R1,于是T2等待T1释放R1上的锁。这样就出现了T1在等待T2,而T2又在等待T1,的局面,T1、T2两个事务永远不能结束,形成死锁
解决死锁的方法:有两种思路
① 一次封锁法:一次性将所有要使用的数据全部加锁,否则就不能继续执行
存在的问题:扩大了封锁范围,降低了系统的并发度;
② 顺序封锁法:预先对数据对象规定一个封锁顺序,所有事务都按照这个顺序实施封锁。
存在的问题:
1.数据库在动态地不断变化,要维护这样的资源的封锁顺序非常困难,成本很高。
2.事务的封锁请求可以随着事务的执行而动态地决定,很难实现确定每一个事务要封锁哪些对象,因此很难按规定的顺序去施加封锁。
① 超时法:如果一个事务的等待时间超过了规定的时限,就认为发生了死锁
存在的问题:
1.时间设置太短,有可能误判死锁
2.时间设置太长,死锁发生后不能及时发现
② 等待图法:事务等待图是一个有向图G=(T,U),T为结点的集合,每个结点表示正在运行的事务;U为边的集合,表示事务等待情况,若事务T1等待T2,则在T1、T2之间画一条有向边,从T1指向T2。
事务等待图动态地反映了所有事务的等待情况。并发控制子系统周期性地(如每隔数秒)生成事务等待图,并进行检测。如果发现图中存在回路,则表示系统中出现了死锁。并发控制子系统一旦检测到系统中存在死锁,就要设法解除。通常采用的方法是选择一个处理死锁代价最小的事务,将其撤销,释放此事务持有的所有的锁,使其他事务得以运行下去
问:“若事务等待图出现环路,则该并发调度不是冲突可串行化的。”你认为这个判断是否正确?理由
答:不正确,事务等待图出现环路表明系统中存在死锁,即事务在申请数据对象上的锁时互相等待,但这并不能说明该并发调度不是和另一个串行调度冲突等价的
在一个系统中同时支持多种封锁粒度供不同的事务选择是比较理想的,这种方法称为多粒度封锁。
物理单元:页(数据页或索引页)、物理记录等
逻辑单元:属性值、属性值的集合、元组、关系、索引项、整个索引、整个数据库
如何进行封锁的?
多粒度树:根节点是整个数据库,表示最大的数据粒度,叶节点表示最小的封锁粒度。
多粒度封锁协议
允许多粒度树中的每个结点被独立的加锁,对每一个结点加锁(显式封锁)意味着这个结点的所有后裔结点也被加以同样类型的锁(隐式封锁)。
对某个数据加锁时,系统要检查该数据对象上有无显示封锁与之冲突,同时还要上下检查是否存在隐式封锁,这样的检查效率太低,因此提出了——意向锁
意向锁:如果对一个结点加意向锁,则说明该结点的下层结点正在被加锁;对任意一结点加锁时,必须先岁它的上层结点加意向锁。
意向锁有哪些种类?
IS锁(意向共享锁):如果对一个数据对象加IS锁,表示它的后裔结点拟(意向)加S锁。
IX锁(意向排他锁):如果对一个数据对象加IX锁,表示它的后裔结点拟(意向)加X锁。
SIX锁(共享意向排他锁):如果对一个数据对象加SIX锁,表示对它加S锁,再加IX锁,即SIX=S+IX。
申请封锁时应该按自上而下的次序进行,释放封锁时应该按自下而上的次序进行
问:基于DBMS提供的多粒度封锁技术,在实际应用中,何时使用粗粒度锁(如关系锁),何时使用细粒度锁(如元组锁)?
答:需要处理少量元组的事务应采用细粒度锁,需要处理大量元组的事务应采用粗粒度锁 细粒度锁比粗粒度锁有更好的并发度,但是开销较大,并发控制管理器需要更多的空间来保持关于细粒度锁的信息,选择时需同时考虑等所开销和并发度两个因素
什么是事务的隔离性(Isolation)呢?
隔离性是指,多个用户的并发事务访问同一个数据库时,一个用户的事务不应该被其他用户的事务干扰,多个并发事务之间要相互隔离。
四种事务隔离级别解决了上述问题;大部分都适用中间两种
读未提交(Read uncommitted):【隔离级别最低】**
读已提交(Read committed):【常用】
可重复读(Repeatable read):【常用】
串行化(Serializable ):【隔离级别最高】
当然级别越高,执行效率就越低。像 Serializable 这样的级别,就是以 锁表 的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。
问:默认隔离级别的选择:SQLSever 和Oracle都把默认隔离级别设置为Read Committed(读已提交),MySQL把默认隔离级别设置Repeatable Read(可重复读),而不是设置为更高的或者更低隔离级别, 你觉得这样设置是基于什么考虑呢?
d):【隔离级别最低】**
读已提交(Read committed):【常用】
可重复读(Repeatable read):【常用】
串行化(Serializable ):【隔离级别最高】
当然级别越高,执行效率就越低。像 Serializable 这样的级别,就是以 锁表 的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。
问:默认隔离级别的选择:SQLSever 和Oracle都把默认隔离级别设置为Read Committed(读已提交),MySQL把默认隔离级别设置Repeatable Read(可重复读),而不是设置为更高的或者更低隔离级别, 你觉得这样设置是基于什么考虑呢?
答:通过设置数据库的事务隔离级别可以解决多个事务并发情况下出现的脏读、不可重复读和幻读问题,数据库事务隔离级别由低到高依次为Read uncommitted、Read committed、Repeatable read和Serializable等四种。数据库不同,其支持的事务隔离级别亦不相同:MySQL数据库支持上面四种事务隔离级别,默认为Repeatable read;Oracle 数据库支持Read committed和Serializable两种事务隔离级别,默认为Read committed