第10章:数据库恢复技术
代码是基于SQLServer学习,与MySQL有略微差别!
10.1、事务的基本概念(重点)
10.1.1、什么是事务?
- 事务(Transaction)是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。
- 事务和程序是两个概念
- 在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句。
- 一个应用程序通常包含多个事务。
事务是恢复和并发控制的基本单位。
10.1.2、如何定义事务
显式定义方式:
begin transaction
SQL 语句1
SQL 语句2
…………
commit | rollback
如果没有显示声明事务 (没有写 begin transaction … commit),DBMS会认为每一条SQL语句都为事务!
- 当事务执行到 commit 时,可能有一部分数据留在缓冲区,尚未写入物理数据库中。
- 当事务执行到 rollback 时,可能有一部分数据已经写入物理数据库中。
隐式定义方式:
当用户没有显式地定义事务时,DBMS按缺省规定自动划分事务,也就是每一条SQL语句就是一个事务!
@@ERROR系统变量
-
如果SQL 语句执行成功,则 @@ERROR 值为 0;如果此语句产生错误,则 @@ERROR 返回错误号。每一个 SQL 语句完成时,@@ERROR 的值都会改变。
-
因为每个SQL 语句执行完毕时,@@ERROR 都会得到一个新的值,@@ERROR 可用以下两种方法处理:
- 在 Transact-SQL 语句后,马上检测或使用 @@ERROR。
举例:
create table count(
name varchar(10),
balance int check(balance >= 10)
)
insert into count values('A', 1900),('B',100)
-- 查看 @@error的值
insert into count values('C', 5)
print @@error
insert into count values('D', 500)
print @@error
-- 把一笔资金从账户A转给账户B,资金数目1000
begin transaction
declare @errSum int
/*等价于 select @errSum = 0 */
set @errSum = 0
update count set balance = balance - 1000 where name = 'A'
set @errSum = @errSum + @@ERROR
update count set balance = balance + 1000 where name = 'B'
set @errSum = @errSum + @@ERROR
if (@errSum <> 0)
begin
rollback
end
else
begin
commit
end
-- 把一笔资金从账户A转给账户B,资金数目1000,用存储过程来做
create procedure transCount @from varchar(10), @to varchar(10), @money int
as
begin transaction
declare @errSum int
set @errSum = 0
update count set balance = balance - @money where name = @from
set @errSum = @errSum + @@ERROR
update count set balance = balance + @money where name = @to
set @errSum = @errSum + @@ERROR
if (@errSum <> 0)
begin
rollback
end
else
begin
commit
end
-- 调用存储过程
transCount 'A', 'B', 10
10.1.3、事务的ACID特性(重点)
原子性(Atomicity)
一致性(Consistency)
隔离性(Isolation)
持续性(Durability )
1、原子性(Atomicity)
-
事务是数据库的逻辑工作单位
-
事务中包括的诸操作要么都做,要么都不做
原子性(Atomicity)是通过数据库的恢复子系统来保证的
原子性示例:
create table sales(
id char(3),
a int,
b int
);
insert into sales values( 'A01', 0, 10 );
select * from sales
-- 没添加事务之前
update sales set a = a - 10 where id = 'A01'
-- 等待30秒后再执行
waitfor delay '00:00:30.000'
update sales set b = b + 10 where id = 'A01'
在30秒之内,点击停止 sqlserver 服务按钮
再次开启 sqlserver 服务,再次查询 sales 表,发现两条update语句,只执行成功了一条
-- 开启事务
begin tran
update sales set a = a - 10 where id = 'A01'
waitfor delay '00:00:30.000'
update sales set b = b + 10 where id = 'A01'
commit
再次停止 sqlserver 服务,开启sqlserver 服务后,再次查询 sales表,发现数据没有发生变化,开启事务后通过数据库的恢复子系统来保证 事务的原子性操作
2、一致性(Consistency)
-
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态
-
一致性状态:数据库中只包含成功事务提交的结果
-
不一致状态:数据库中包含失败事务的结果
第一种情况下,事务在没有提交的前提下,发生断电(举例数据库发生故障的一种),这是一个失败的事务,此时数据库处于一个不一致状态,当再次来电时,数据库恢复子系统会将之前的数据库操作恢复,不会再重新做这个事务;
第二种情况下,有一部分数据以及写入物理数据库,有一部分还在内存缓冲区中,但事务已经提交了,此时发生断电(举例数据库发生故障的一种),当再次来电时,数据库恢复子系统会重做当前这个事务,因为在未断电之前这个事务已经提交了,是一个成功的事务;
恢复子系统保证了数据库事务的原子性和一致性~
并发控制子系统保证了数据库事务的隔离性~
3、隔离性(Isolation)
-
事务是允许并发执行的
-
并发执行的各个事务之间不能互相干扰
begin transaction
读A
更新A,使A减1
commit
begin transaction
读A
更新A,使A减3
commit
在下面这种情况下,一个事务在修改表中数据的时候,另一个事务同时也对同一张表中的同一个数据进行修改,从而产生了修改丢失,破坏了数据库事务的隔离性。
通过并发控制,来加锁,实现在一个事务修改的过程中,另一个事务不能去读同一个要修改的数据,当第一个事务修改完成后,第二个事务才能去读。如果不是读同一个数据,也就不用进行并发控制,直接读即可。
4、持续性(Durability )
-
事务提交之后,它对于系统的影响是永久性的。
-
接下来的其他操作或故障不应该对其执行结果有任何影响。
10.2、数据库恢复概述
故障是不可避免的
- 计算机硬件故障
- 系统软件和应用软件的错误
- 操作员的失误
- 恶意的破坏
故障的影响
-
简单情况下,导致正在运行事务非正常中断,从而破坏数据库的一致性。
-
严重情况下,破坏数据库,导致数据库中的全部或部分数据丢失
DBMS对故障的对策(数据库恢复技术)
-
DBMS提供恢复子系统
- 整个系统代码的百分之十以上。
-
保证故障发生后,能把数据库中的数据从错误状态恢复到某种逻辑一致的状态,保证事务ACID特性。
恢复技术是衡量系统优劣的重要指标
10.3、故障的种类
- 事务内部的故障
- 系统故障(软件故障)
- 介质故障(硬件故障)
- 计算机病毒
10.3.1、事务内部的故障
事务故障的危害
- 发生事务故障时,夭折的事务可能已把对数据库的部分修改写回磁盘。
事务故障的恢复:事务撤消(UNDO)
-
清除该事务对数据库的所有修改,使得这个事务象根本没有启动过一样。
-
强行回滚(ROLLBACK)该事务。
事务故障的恢复由DBMS自动完成。
CREATE TABLE t1
(
a int,
b int check( b>2 )
)
begin tran
INSERT INTO t1 VALUES ( 1, 5 )
INSERT INTO t1 VALUES ( 2, 0 )
commit tran
-- 你会发现一个语句插入成功了,只回滚产生错误的SQL语句
select * from t1
当第二条insert语句执行失败时,你会发现 t1 表中第一条插入语句执行成功了。
这好像并不符合事务的原子性,这是因为在 sqlserver 中对事务有两种设置。
- SET XACT_ABORT
- 当SET XACT_ABORT为OFF时,只回滚产生错误的SQL语句,而事务将继续进行处理(默认为OFF)。
- 当 SET XACT_ABORT为ON 时,如果SQL 语句产生运行时错误,整个事务将终止并回滚。
SET XACT_ABORT on
begin tran
INSERT INTO t1 VALUES ( 1, 5 )
INSERT INTO t1 VALUES ( 2, 0 )
commit tran
-- 你会发现整个事务都回滚了,表中没有数据
select * from t1
10.3.2、系统故障(软故障)
系统故障的危害:
-
所有正在运行的事务都非正常终止
-
内存中数据库缓冲区的信息全部丢失
-
一些已经显示提交的事务,可能有一部分数据或全部数据留在缓冲区,尚未写入物理数据库中。
-
一些没有完成的事务的结果可能已经送入物理数据库
恢复策略:
-
强行撤消(UNDO)所有未完成事务
-
重做(REDO)所有已提交的事务
黄色的线代表发生系统故障,针对恢复策略,会对成功提交的事务,但有部分或者全部数据仍停留在内存中的会进行重做(redo),对失败事务进行撤销(undo)。
10.3.3、介质故障(硬故障)
什么是介质故障
-
硬故障:指外存故障
- 磁盘损坏
- 磁头碰撞
- 瞬时强磁场干扰
-
介质故障使存储在外存中的数据部分丢失或全部丢失。
-
介质故障比前两类故障的可能性小得多,但破坏性大得多。
恢复策略
-
装入数据库发生介质故障前某个时刻的数据副本(备份)
-
重做自此时开始的所有成功事务(从日志中获取),将这些事务已提交的结果重新记入数据库。
10.4、恢复的实现技术
数据库恢复的基本原理:冗余
- 利用存储在系统其它地方的冗余数据来重建数据库中已被破坏或不正确的那部分数据
数据库恢复的技术问题:
-
如何建立冗余数据。
-
如何利用这些冗余数据实施数据库恢复。
如何建立冗余数据
-
数据转储(backup)
-
登录日志文件(logging )
下面主要讲如何建立冗余数据
10.4.1、数据转储(backup)
DBA定期地将整个数据库复制到磁带或另一个磁盘上保存起来的过程。这些备用的数据文本称为后备副本或后援副本。
一般分为两类:
- 静态转储
- 动态转储
静态转储
- 在系统中无运行事务时进行转储。
- 优点:简单
- 缺点:降低了数据库的可用性
- 转储必须等所有事务结束。
- 新的事务必须等转储结束。
动态转储
-
转储操作与用户事务并发进行。
-
优点
- 不用等待正在运行的用户事务结束。
- 不会影响新事务的运行。
-
动态转储的缺点:不能保证副本中的数据正确有效。
-
需要把动态转储期间各事务对数据库的修改活动登记下来,建立日志文件。
-
后备副本 + 日志文件才能把数据库恢复到某一时刻的正确状态。
10.4.2、登录日志文件(logging )
什么是日志文件?
- 日志文件(log)是用来记录事务对数据库的 更新操作 的文件。
日志文件的格式:
-
以记录为单位的日志文件
-
以数据块为单位的日志文件
下面讲以 记录为单位的日志文件,比较好理解
日志文件内容:
- 各个事务的开始标记,(形成一条记录)
- 各个事务的所有更新操作,(形成一条记录)
- 各个事务的结束标记,(形成一条记录)
begin tran
insert into sales values( 'A0002', 0, 10 );
update sales set b=b-3 where id = 'A0001'
commit tran
-- 以记录为单位的日志文件,日志文件的内容:
< T0 start>
< T0, I, sales, null, ('A0002', 0, 10) >
< T0, U, sales b(id='A0001'), 30, 27 >
< T0 commit>
日志文件登记的原则:
(1)登记的次序严格按并发事务执行的时间次序。
(2)必须先写日志文件,后写数据库。
10.5、恢复策略
10.5.1、事务故障的恢复
恢复方法
- 由恢复子系统应利用日志文件撤消(UNDO)此事务已对数据库进行的修改。
事务故障的恢复步骤:
-
反向扫描日志文件(后 → 前),查找该事务的更新操作。
-
将日志记录中“更新前的值”写入数据库。
-
继续1 → 2操作。
-
如此下去,直至读到此事务的开始标记,事务故障恢复就完成了。
事务故障的恢复由系统自动完成,不需要用户干预。
10.5.2、系统故障的恢复
系统故障造成数据库不一致状态的原因
-
一些未完成事务对数据库的更新已写入数据库。
-
一些已提交事务对数据库的更新还留在缓冲区没来得及写入数据库。
恢复方法:
-
Undo 故障发生时未完成的事务
-
Redo 已完成的事务
系统故障的恢复由系统在重新启动时自动完成,不需要用户干预
举例:
系统故障的恢复步骤:
(1)正向扫描日志文件,找出故障发生前已提交的事务,将其事务标识记入重做(REDO)队列(A、B)。同时找出故障发生时未完成的事务,将其事务标识记入撤销(UNDO)队列(C,D)
(2)对撤消队列中的各个事务进行撤消处理
- 反向扫描日志文件,对每个UNDO事务的更新操作,将日志记录中“更新前的值”写入数据库
(3)对重做队列中的各个事务进行重做(REDO)处理
- 正向扫描日志文件,对每个REDO事务更新操作,将日志记录中“更新后的值”写入数据库
10.5.3、介质故障的恢复
介质故障的恢复:
1)装入最新的数据库后备副本(离故障发生时刻最近的转储副本),使数据库恢复到最近一次转储时的一致性状态。
-
静态转储:只需数据库副本
-
动态转储:数据库副本 + 转储开始到转储结束时的日志文件副本
2)装入相应的日志文件副本,重做已完成的事务,对未完成得到事务不用处理
-
首先扫描日志文件,找出故障发生时已提交的事务的标识,将其记入重做队列。
-
然后正向扫描日志文件,对重做队列中的所有事务进行重做处理。即将日志记录中“更新后的值”写入数据库。
介质故障的恢复需要DBA介入
-
重装最近转储的数据库副本和有关的各日志文件副本。
-
执行系统提供的恢复命令
具体的恢复操作仍由DBMS完成