数据库系统实现学习笔记——事务管理

事务

概念

  1. 由一系列操作序列构成的程序执行单元,这些操作要么都做要么都不做,是一个不可分割的工作单位
  2. 开始:begin transaction
  3. 结 束 { commit work:提交事务 rollback work:回滚事务 结束 \begin{cases} \text{commit work:提交事务} \\ \text{rollback work:回滚事务} \end{cases} {commit work:提交事务rollback work:回滚事务

ACID特性

  • Atomicity原子性

    • 要么全做要么全不做
    • 恢复机制实现
  • Consistency一致性

    • 事务开始前数据库处于一致性的状态;事务结束后数据库仍然处于一致性的状态
    • 用户负责,并发控制机制实现
  • Isolation隔离性

    • 事务不受其它并发执行事务的影响
    • 并发控制机制实现
  • Durability持久性

    • 事务提交后对数据库影响是永久的,系统发生故障不能改变事务的持久性
    • 恢复机制实现

事务调度

概念

  1. 事务调度:指令在系统中执行的时间顺序。一组事务的一个调度必须包含一组事务的全部指令,并且必须保持指令在各个事务中出现的顺序。不同的调度使数据库处于不一致状态,DBMS必须保证事务调度执行后数据库总处于一致状态。

  2. 冲突指令:两条是不同事务在相同的数据项进行操作,其中一条为write写指令的时候这两条指令是冲突的。

  3. 冲突等价:调度s可以经过一系列非冲突指令交换成调度s‘,则成调度s与s’是冲突等价的。

  4. 视图等价满足:

    • 事务在调度s中读取了数据项Q的初始值,那么事务在调度s‘也必须读取Q的初始值
    • 事务T在调度s中执行了read(Q),并且读取的值是由事务T产生的,那么T在调度s’中读取的Q值也必须是由T产生的
    • 调度s中有事务执行了最后的write(Q),则在调度s‘中该事务也执行了最后的write(Q)

可恢复性

  1. 事务的恢复:一个事务失败,能够撤销该事务对数据库的影响。如果有其他事务读取了上面的失败事务写入的数据,那么这个其他事务也应该被撤销。
  2. 可恢复调度:如果事务T2读取了事务T1所写的数据,那么事务T1必须在事务T2之前提交。
  3. 无级联调度:如果事务T2读取了事务T1所写的数据,那么事务T1必须在事务T2之前读取。
  4. 级联调度:由于一个事务故障而导致的一系列的事务的回滚。
  5. 无级联调度一定是可恢复调度

可串行化

  1. 概念:若调度s与一个串行调度的执行有相同的效果,称调度s是可串行化的。

  2. 分 类 { 冲突可串行化:调度s与一个串行调度冲突等价 视图可串行化:调度s与一个串行调度视图等价 分类\begin{cases} \text{冲突可串行化:调度s与一个串行调度冲突等价} \\ \text{视图可串行化:调度s与一个串行调度视图等价} \end{cases} {冲突可串行化:调度s与一个串行调度冲突等价视图可串行化:调度s与一个串行调度视图等价

  3. 冲突可串行化一定是视图可串行化,视图可串行化不一定是冲突可串行化

  4. 冲突可串行化判定:优先图是否有回路

    • 优先图G=(V,E)的构造
      • V为顶点集,表示一个个的事务
      • E 为 边 集 , ( T i , T j ) { Tj在执行read(Q)之前,Ti执行write(Q) Tj在执行write(Q)之前,Ti执行read(Q) Tj在执行write(Q)之前,Ti执行write(Q) E为边集,(Ti,Tj) \begin{cases} \text{Tj在执行read(Q)之前,Ti执行write(Q)} \\ \text{Tj在执行write(Q)之前,Ti执行read(Q)} \\ \text{Tj在执行write(Q)之前,Ti执行write(Q)} \end{cases} E(Ti,Tj)Tj在执行read(Q)之前,Ti执行write(Q)Tj在执行write(Q)之前,Ti执行read(Q)Tj在执行write(Q)之前,Ti执行write(Q)
    • 如果调度S的优先图中有环,则调度S是非冲突可串行化的。如果图中无环,则调度S是冲突可串行化的
    • 判定的时间复杂度O( n 2 n^2 n2
  5. 视图可串行化调度的判定是NP-完全问题

恢复系统

故障的分类

  1. 事务故障
    • 逻辑错误:事务由于某些内部条件而无法继续正确执行
    • 系统错误:系统进入不良状态(死锁)而无法继续事务
  2. 磁盘故障(介质故障):由于磁头损坏或故障造成磁盘块上内存丢失
  3. 系统奔溃,硬件或数据库软件或操作系统故障,易失性存储器内容丢失导致事务处理终止

事务执行的地址空间

  1. 保存数据库元素的磁盘块空间
  2. 缓冲区管理器所管理的虚拟内存或主存地址空间
  3. 事务的局部地址空间
    事务的原语操作
  • input(x):将数据库元素x拷贝到主存
  • read(x,t):将数据库元素x拷贝到事务的局部变量t
  • write(x,t):将局部变量t拷贝到主存缓冲区中的数据库元素x
  • output(x):将包含x的缓冲区拷贝到磁盘

故障的恢复

日志

  1. 概念:日志是日志的序列,记录了某个事务已做的事的某些情况,不同事务的日志记录可以在日志文件中交错
  2. 作用:保持事务的原子性和持久性
  3. 类型:
    • undo日志:保证事务的原子性
    • rudo日志:保证事务的持久性
    • undo/rudo日志:保证事务的原子性和持久性
  4. 记录形式
  • &lt; S T A R T T &gt; &lt;START T&gt; <STARTT>表示事务开始
  • &lt; C O M M I T T &gt; &lt;COMMIT T&gt; <COMMITT>表示事务成功完成提交
  • &lt; A B O R T T &gt; &lt;ABORT T&gt; <ABORTT>表示事务T中止
  • 更新记录
  1. 日志存放
  • 在内存中开辟临时保存日志记录的区域,每个缓冲块可以容纳若干个日志记录
  • 根据需要一次将一个或多个缓冲块写入磁盘,从而减少写磁盘的次数
  • 写入磁盘的日志记录顺序必须与写入日志缓冲区的次序完全一致
undo日志
  1. 更新记录 &lt; T , X , v &gt; &lt;T,X,v&gt; <T,X,v>:事务T改变了数据库元素X,X原来的值是v
  2. 规则(立即修改)
  • U1:如果事务T改变了X,那么更新记录必须要在X的新值写入磁盘前写入到磁盘
  • U2:事务提交,那么COMMIT日志记录必须在事务改变数据库元素写到磁盘之后在再写到磁盘
  1. undo日志恢复管理器的工作:
  • 从后往前扫描日志
  • 记住所有有COMMIT或ABORT的事务
  • 扫描到更新记录
    - 如果事务的提交被扫描到,说明事务已经提交而不需要撤销,则什么都不做
    - 如果没有扫描到事务的提交,说明事务是一个未完成的事务或被中止的事务,则必须将X的值还原为v,同事写一个的日志记录
redo日志
  1. 更新记录 &lt; T , X , v &gt; &lt;T,X,v&gt; <T,X,v>:事务T改变了数据库元素X,X的新值是v
  2. 规则(延迟修改)
  • R1:在修改磁盘上的数据库元素X之前,要保证与X修改相关的日志记录:更新记录和 &lt; C O M M I T T &gt; &lt;COMMIT T&gt; <COMMITT>都必须出现在磁盘
  • 与事务相关的内容写到磁盘的顺序为:
    • 说明对元素进行修改的日志记录
    • COMMIT日志
    • 改变的数据库元素本身
  1. redo日志恢复器的工作:
  • 确定已提交的事务
  • 从首部开始扫描日志
    - 如果事务未提交,则什么都不做
    - 如果事务已提交,则为数据库元素X写入新值v
  • 对未完成的事务T,写入 &lt; A B O R T T &gt; &lt;ABORT T&gt; <ABORTT>记录并刷新日志
undo/redo日志
  1. 更新记录 &lt; T , X , v , w &gt; &lt;T,X,v,w&gt; <T,X,v,w>:事务T改变了数据库元素X,X的旧值是v,新值是w
  2. 规则(立即修改)
  • UR1:事务修改磁盘上的数据库元素X前,更新记录必须先出现在磁盘上
  1. undo/redo日志的恢复
  • 从后往前扫描日志,构造undo-list和redo-list
  • 加入redo-list;加入undo-list
  • 从后往前重新扫描日志,对undo-list的每个事务的每一个日志记录执行undo(提交恢复旧值)
  • 从前往后重新扫描日志,对redo-list的每个事务的每一个日志记录执行redo(提交赋予新值)
  1. 问题:事务提交,但是COMMIT记录未刷新到磁盘,后来的一次崩溃使得事务被撤销而不是重做;解决方法是COMMIT记录一旦出现就必须刷新到磁盘上
检查点
  1. 解决的问题
  • 故障恢复时扫描日志耗费时间长
  • 重做所有已提交事务耗费时间长(一些事务对数据库的修改已经写到磁盘不需要再重做)
  1. 方法:周期性地对日志做检查点,以避免故障恢复时检查整个日志
  2. 步骤
  • 找到最后一个检查点记录在日志文件中的地址,由该地址在日志文件中找到最后一个检查点记录
  • 由该检查点记录得到检查点建立时刻所有正在执行的事务清单ACTIVE-LIST,把ACTIVE-LIST暂时放入UNDO-LIST,REDO-LIST暂为空
  • 从检查点开始正向扫描日志文件。如有新开始的事物Ti,把Ti暂时放入UNDO-LIST;如有提交的事务Tj,把Tj从UNDO-LIST移到REDO-LIST,直到日志文件结束
  • 对UNDO-LIST中的每个事务执行UNDO操作,对REDO-LIST中的每个事务执行REDO操作

并发控制

  1. 为什么使用并发控制:通过并发控制可以使得对事务的调度是可串行化的
  2. 方法:
  • 基于封锁的方法
  • 基于时间戳的方法
  • 基于有效性确认的方法

基于封锁的方法

  1. 任何事务在对数据对象(表,记录,数据项)进行读写操作时必须先对数据加密

  2. 锁 { 共享锁:读锁、s锁 排它锁:写锁、x锁 锁\begin{cases} \text{共享锁:读锁、s锁} \\ \text{排它锁:写锁、x锁} \end{cases} {共享锁:读锁、s排它锁:写锁、x

  3. 封锁协议:限制了可能调度的数目(何时加锁何时解锁的一组规则)

  • 两阶段封锁协议(要求事务分两个阶段提出加锁和解锁申请)
    - 增长阶段:事务可以获得锁,但是不能释放锁(需要可以将S锁提升为X锁)
    - 缩减阶段:事务可以释放锁,但是不能获得新锁(需要可以将X锁降级为S锁)
    - 不保证不发生死锁,不保证不发生级联回滚,但是保证冲突可串行化
  • 基于图的协议
  1. 死锁:系统中存在在一个事务集,集合中的每个事务在等待该集合中的另一个事务所锁住的数据项,则系统就处于死锁状态
  • 死锁预防:采用死锁预防协议保证系统永不进入死锁状态
    - 方法1:对加锁请求进行限制:每个事务开始前封锁他要访问的所有数据或者按照某个偏序规定的顺序封锁数据项
    - 方法2:强占与事务回滚:如果事务T2所申请的锁已被事务T1持有,那么授予T1的锁可能通过回滚事务T1被强占后授予T2或者回滚事务T2不让他等待事务T1的锁
    - 基于时间戳的两种不同的死锁预防机制:(1)wait-die机制,基于非抢占技术。当事务T1申请的数据项当前被Tj持有,当且仅当Ti的时间戳小于Tj的时间戳,允许Ti等待否则Ti回滚;(2)wound-wait机制,基于抢占技术。当事务Ti申请的数据项当前被Tj持有,但且仅当Ti的时间戳大于Tj的时间戳允许Ti等待否则Tj回滚
  • 死锁检测和死锁恢复:允许你系统进入死锁的状态,如何进行检测和恢复
    - 死锁检测的方法:等待图包含环
    - 等待图G=(V,E)
    - V:节点集由所有的事务组成
    - E:边集,(Ti,Tj)表示事务Ti在等待Tj释放所需的数据项
    - 事务Ti申请的数据项被Tj持有时,(Ti,Tj)插入等待图中
    - 事务Tj不在拥有Ti所需数据项时,(Ti,Tj)从等待图中删除
    - 死锁恢复
    - 选择牺牲者,但应使事务回滚带来的代价最小
    - 彻底回滚或只回滚到可以解除死锁处
  • 基于超时的一种机制(介于死锁预防和死锁检测折中的机制)
    - 申请锁的事务至多等待一个给定的时间,在这段时间内锁没有授予该事务,则该事务超时,事务自己回滚并重启
    - 存在死锁,卷入死锁的一个或多个事务将超时并回滚,允许其他事务继续
  1. 饿死:某事务等待对数据项加写锁,而另一个事务序列中的每一个事务都对该数据项加读锁,则事务T永远得不到进展
  • 解决方法:授权加锁,即当事务T申请对数据项Q加M(s或x)锁时,授权加锁的条件是
    - 不存在在数据项Q上持有与M型锁冲突的锁的其他事务
    - 不存在等待对数据项Q加锁且先于T申请加锁的事务
  1. 系统自动进行加锁和解锁步骤:
  • 事务进行read(Q),系统产生一个lock-S(Q)指令,后接read(Q)指令
  • 事务进行write(Q),检查事务是都在Q上是否持有共享锁(读锁),如果有则upgrade(Q)指令后接write指令
  • 事务提交或回滚后,该事务持有的所有锁都被释放
  1. 封锁的实现——锁表

(1). 锁表为每个数据项维护一个链表,链表中每一个记录表示对该数据项的一个加锁操作,记录按照请求到达的顺序排序
(2). 每个记录包含了:事务提出的请求,请求锁的类型,请求是否已被授予锁
(3). 有一个数据项名称为索引的散列表作为链表入口

  1. 锁表的工作方式
  • 一个封锁请求到达时,如果相应数据项的链表存在,则在链表末尾增加一个记录,否则新建一个仅包含该请求的链表
  • 当收到一个事务的解锁消息时,将相应的链表记录删除,然后检查随后的记录,如果有正在等待的封锁请求,则看请求能否被授权,如果能则授权该请求并处理其后的记录,如果还有则继续处理
  • 如果一个事务终止,则删除该事务产生的正在等待加锁的所有请求。当数据库系统撤销了该事务,则该中止事务持有的所有锁将被释放
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值