数据库学习笔记(四)
1. 数据库恢复技术
1.1 事务
事务:用户定义的一个数据库操作序列,是一个不可分割的工作单位。这些操作要么全做,要么全不做。是恢复的基本单位,也是并发控制的基本单位
定义事务:
begin transaction
sql语句
commit
# 事务正常结束,提交事务的所有操作
begin transaction
sql语句
rollback
# 事务异常终止,事务回到开始的状态
1.2 事务的特性
ACID 特性
- 原子性 事务是数据库的逻辑工作单位。事务中的操作要么都做,要么都不做
- 一致性 事务执行的结果必须是使数据库从一个一致性状态变为另一个一致性状态 一致性状态:数据库中只包含成功事务提交的结果
- 隔离性 一个事务的执行不能被其他事务干扰
- 持续性 事务一旦提交,它对数据库中数据的改变应该是永久性的
1. 3 故障
对数据库的影响:
- 数据库本身被破坏
- 数据库没有被破坏,但是数据可能不正确
- 事务故障:事务没有达到预期的终点(commit 或显式的 rollback)、数据库可能处于不正确状态 恢复:事务撤销,强行回滚(rollback)该事务
- 系统故障:造成系统停止运转的任何事件,使得系统要重新启动 恢复:需要通过undo/redo所有被中断的事务
- 介质故障:称为硬故障。如硬盘损坏、磁头碰撞、瞬时强磁场干扰 恢复:重装数据库、重做已完成的事务
- 计算机病毒
1.4 恢复的实现技术
恢复操作的基本原理:冗余
- 利用存储在系统中的冗余数据来重建数据库中已经被破坏或不正确的数据
建立冗余数据的方法:
- 数据转储(备份)
- 登记日志文件(logging)
1.4.1 数据转储
转储是指数据库管理员定期将整个数据库复制到其他存储介质上保存下来的过程
- 静态转储:在系统无运行事务时进行的转储操作。转储期间不允许对数据库的任何存取、修改活动
- 动态转储:转储操作与用户事务并发进行。转储期间允许对数据库进行存取、修改
- 海量转储:每次转储全部数据库
- 增量转储:只转储上次转储后更新后的数据
1.4.2 登记日志文件
日志文件:用来记录事务对数据库的更新操作的文件
作用:
- 进行事务故障恢复
- 进行系统故障恢复
- 协助后备副本进行介质故障恢复
为了保证数据库是可恢复的,登记日志文件必须遵循两条原则
- 登记的次序严格按并发事务执行的时间次序
- 必须先写日志文件,后写数据库
2. 并发控制
多事务执行方式:
- 事务串行执行:按事务排队,效率低
- 交叉并发方式:在单处理机系统中。并行事务轮流交叉运行。假并行,但也提高了系统的效率。
- 同时并发方式:多处理机系统中,多个系统可以同时运行多个事务
2.1 简介
事务是并发控制的基本单位。
并发控制机制的任务:
- 对并发操作进行正确调度
- 保证事务的隔离性
- 保证数据库的一致性
并发操作带来的数据不一致性:
- 丢失修改:两个事务 T1 和 T2 读取同一数据并修改,T2 的提交结果可能会破坏 T1 的结果,导致 T1 的修改被丢失
- 不可重复读:事务 T1 读取数据后,T2 对数据进行了修改等操作,事务 T1 再次读该数据时,得到与前一次不同的值
- 读脏数据:nT1 将 C 值修改为 200,T2 读到 C 为 200。T1 撤销,C 恢复原值,但是 T2 读到的 C 为 200,T2 读到的 C 就是脏数据。
2.2 封锁
基本封锁类型:
- **排他锁(又称写锁,X 锁)**:
- 若事务 T 对数据对象 A 加上X 锁,则只允许 T 读取和修改 A,其他任何事务都不能再对 A 加任何类型的锁,直到 T 释放 A 上的锁
- 作用:保证其他事务在 T 释放 A 上的锁之前不能再读取和修改 A
- 共享锁(又称读锁,S 锁)
- 若事务 T 对数据对象 A 加上 S 锁,则事务 T 可以读 A 但不能修改 A,其他事务只能再对 A 加 S 锁,而不能加 X 锁,直到 T 释放 A 上的 S 锁
- 作用:保证其他事务可以读 A,但在 T 释放 A 上的 S 锁之前不能对 A 做任何修改
2.3 封锁协议
2.3.1 一级封锁协议
事务 T 在修改数据 R 之前,必须先对它加上 X 锁,直到事务结束才释放。(结束包括正常结束 commit 和非正常结束 rollback)
作用:防止丢失修改
2.3.2 二级封锁协议
一级封锁协议加上事务 T 在读取数据 R 之前必须先对它加 S 锁,读完后就可以释放 S 锁
作用:防止丢失修改和读脏数据
2.3 三级封锁协议
一级封锁协议加上事务 T 在读取数据 R 之前必须先对它加 S 锁,直到事务结束,才可以释放 S 锁
作用:防止丢失修改、读脏数据和不可重复读
2.4 活锁和死锁
2.4.1 活锁
- 事务 T1 封锁了数据 R
- 事务 T2 请求封锁 R,等待,
- T3 也请求封锁 R
- T1 释放 R 上的封锁后,系统先批准 T3 的请求,T2 继续等
- T4 请求封锁 R
- T3 释放 R 上的封锁后,系统先批准 T4 的请求,T2 继续等。。。
活锁:T2 有可能永远等待
采用先来先服务的策略可以避免活锁
2.4.2 死锁
- T1 封锁了数据 R1,T2 封锁了数据 R2
- T1 请求封锁 R2,因为 T2 封锁了 R2,所以 T1 等待 T2 释放 R2 上的锁
- T2 请求封锁 R1,因为 T1 封锁了 R1,所以 T2 等待 T1 释放 R1 上的锁
死锁:T1 在等待 T2,T2 在等待 T1,T1 和 T2 两个事务永远不能结束
死锁的诊断:
- 超时法:如果一个事务的等待时间超过了规定的时限,就认为发生了死锁。
- 优点:实现简单
- 缺点:时限若设置的太长,死锁发生后不能及时发现;有可能误判死锁
- 等待图法
解决死锁:
- 选择一个处理死锁代价最小的事务,将其撤销
- 释放此事务持有的所有的锁,使其他事务能继续运行下去