文章目录
- 1. 事务的基本概念
- 2. 数据库的并发控制
- 2.1 事务调度
- 2.2 并发操作带来的问题
- 2.3 并发调度的可串行性
- 2.4 并发控制技术
- 2.5 两段锁协议
- 2.6 多粒度封锁协议
- 3. 数据库的备份与恢复
- 3.1 数据库系统故障
- 3.2 数据库的备份
- 3.3 数据库的恢复
- 4. 数据库的安全性与完整性
- 4.1 数据库的安全性
- 4.2 数据库的完整性
事务管理是对于一系列数据库操作进行管理。在多个事务并发执行的数据库系统中,如果对共享数据的更新不进行控制,就会产生数据的不一致性,导致数据库存储数据错误。运行中的数据库系统很容易受到来自多方面的干扰和破坏。如软、硬件系统故障,合法用户的误操作,非法入侵等等。
数据库的保护就是要排除和防止各种对数据库的干扰破坏,确保数据安全、可靠,以及在数据库已经遭到破坏后如何尽快地恢复正常。数据库的保护是通过对数据库的恢复、安全性控制、完整性控制和并发控制四个方面来实现的。
1. 事务的基本概念
事务(Transaction)是一系列的数据库操作,是数据库应用程序的基本逻辑单位,即应用程序对数据库的操作都应该以事务的方式进行。
事务是一个操作序列,这些操作“要么都做,要么都不做”,是数据库环境中不可分割的逻辑工作单位。事务和程序是两个不同的概念,一般一个程序可包含多个事务。
事务通常由数据库操纵语言或其他高级语言(如SQL、COBOL、C、C++、Java等)书写的用户程序来实现。一个事务由应用程序的一组操作序列组成,它以 BEGIN TRANSACTION 语句开始,以 END TRANSACTION 结束语句。
事务定义的语句如下:
(1)BEGIN TRANSACTION:事务开始。
(2)END TRANSACTION:事务结束。
(3)COMMIT:事务提交。该操作表示事务成功地结束,它将通知事务管理器该事务的所有更新操作现在可以被提交或永久地保留。
(4)ROLLBACK:事务回滚。该操作表示事务非成功地结束,它将通知事务管理器出故障了,数据库可能处于不一致状态,该事务的所有更新操作必须回滚或撤销。
示例1. 银行转账业务,对“从账户A转入账户B金额x元”业务,站在顾客角度来看,转账是一次单独操作;而站在数据库系统的角度它至少是由两个操作组成的,第一步从账户A减去x元,第二步给账户B加上x元。下面是银行转账事务的伪代码:
BEGIN TRANSACTION read(A); /*读账户A的金额*/A=A-x;IF (A<0) THENprint("金额不足,不能转账");ROLLBACK; /*撤销该事务,回到事务执行前的状态*/ELSE write(A); /*写入账户A的金额*/read(B);B=B+x;write(B);COMMIT; /*提交事务*/ENDIF;
END TRANSACTION
SQL 标准规定当一条 SQL 语句被执行,就隐式地开始了一个事务,SQL中的 Commit work 和 Rollback work 语句之一会结束一个事务。
(1)Commit work:提交当前事务。这意味着将该事务所做的更新在数据库中永久保存。一旦事务被提交后,一个新的事务自动开始。
(2)Rollback work:回滚当前事务。这意味着将撤销该事务对数据库的更新。这样,数据库恢复到该事务执行第一条语句之前的状态。
若事务已执行了 Commit work,就不能用 Rollback work 来撤销。数据库系统能保证在发生诸如某条 SQL 语句错误、断电、系统崩溃的情况下,若事务还没有执行 Commit work,则所造成的影响将被回滚。对断电、系统崩溃的情况,回滚是在系统重新启动时进行。
事务具有四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),简称ACID特性。
- 原子性:事务的所有操作在数据库中要么全做要么全都不做。如银行转账中的两个操作必须做为一个单位来处理,不能只执行部分操作。
- 一致性:一个事务独立执行的结果,将保持数据的一致性,即数据不会因为事务的执行而遭受破坏。
- 隔离性:一个事务的执行不能被其他事务干扰。并发事务在执行过程中可能会对同一数据进行操作,这些事务的操作应该不会相互干扰,是相互隔离的。如事务执行中数据不一致性状态出现时不能让其他事务读取到不一致的数据。
- 持久性:一个事务一旦提交,它对数据库的改变必须是永久的,即便系统出现故障时也是如此。
如果不出现故障,那么所有事务都能执行完成。一旦在执行过程中发生故障,不能执行完成的事务称为中止事务;将中止事务对数据库的更新撤销称为事务回滚;成功执行完成的事务称为已提交事务。
中止的事务是可以回滚的,通过回滚恢复数据库,保持数据库的一致性,这是 DBMS 的责任。已提交的事务是不能回滚的,必须由程序员或 DBA 手工执行一个“补偿事务”才能撤销提交的事务对数据库的影响。
事务一旦提交,就不能中止它,而要撤销已提交事务所造成影响的唯一方法是执行一个补偿事务(Compensating Transaction)。
事务的五种状态:
- 活动状态:事务的初始状态,事务执行时处于这个状态。
- 部分提交状态:当操作序列的最后一条语句自动执行后,事务处于部分提交状态。这时,事务虽然已经完全执行,但由于实际输出可能还临时驻留在内存中,在事务成功完成前仍有可能出现硬件故障,事务仍有可能不得不中止。因此,部分提交状态并不等于事务成功执行。
- 失败状态:由于硬件或逻辑等错误,使得事务不能继续正常执行,事务就进入了失败状态。处于失败状态的事务必须进行回滚(ROLLBACK)。这样,事务就进入了中止状态。
- 中止状态:事务回滚并且数据库恢复到事务开始执行前的状态。
- 提交状态:当事务成功完成后,称事务处于提交状态。只有事务处于提交状态后,才能说事务己经提交。
事务的状态转换:
事务进入中止状态后,系统一般有如下两种选择:
- 重启事务。当事务中止的原因是软、硬件错误而不是事务内部逻辑错误时,一般采用重启事务的方法。重启事务可以被看成一个新事务。
- 杀死事务。这样做通常是因为事务中止的原因是事务内部的逻辑错误,或者是输入错误,也可能是所需数据在数据库中没找到等原因。
2. 数据库的并发控制
并发操作,是指在多用户共享的系统中,许多用户可能同时对同一数据进行操作。并发操作带来的问题是数据的不一致性,主要有三类:丢失修改、不可重复读和读脏数据
。其主要原因是事务的并发操作破坏了事务的隔离性。DBMS 的并发控制子系统负责协调并发事务的执行,保证数据库的完整性不受破坏,避免用户得到不正确的数据。
2.1 事务调度
1) 串行调度
串行调度(serial schedule)是指多个事务依次串行执行,且只有当一个事务的所有操作都执行完后才执行另一个事务的所有操作。
示例2. 设每个账号在数据库中具有一条数据库记录,用以记录这个账号的存款数量和其他信息。设有两个事务 T 0 T_0 T0和事 T 1 T_1 T1。事务 T 0 T_0 T0从账号A转2000元到账号B;事务 T 1 T_1 T1从A转20%的款到账号B。 T 0 T_0 T0和 T 1 T_1 T1的定义如下图:
假设用A和B表示账号A和账号B的存款数量;A、B的初值为10000和20000。如果这两个事务串行执行,可以有两种调度方案。
(a)调度S1是先执行 T 0 T_0 T0后执行 T 1 T_1 T1,运行结束时,A和B的最终值分别时6400和23600。
(b)调度S1是先执行 T 1 T_1 T1后执行 T 0 T_0 T0,运行结束时,A和B的最终值分别时6000和24000。
无论采用哪一种方案,A+B在两个事务执行结束时仍然是10000+20000=30000。因此,只要是串行调度,执行的结果都是稳定的和正确的。对于N个事务,最多有N!种正确串行调度。
2)并发调度
并发调度(concurrent schedule):利用分时的方法同时处理多个事务。
对于N个事务进行并发调度,情况会变得复杂得多,它的调度方案远大于N!个,而且并发调度的结果有可能是错误的。
(a)最终结果A+B与原来一致,则这个并发调度是正确的。
(b)最终A+B结果为32000$\ne$30000,这个调度是错误的。
3)可恢复调度
若事务 T i T_i Ti提交失败,则应当撤销 T i T_i Ti的影响以保证其原子性。在允许并发执行的系统中,还必须确保依赖于 T i T_i Ti的任何事务 T j T_j Tj也中止。例如, T j T_j Tj要读 T i T_i Ti写的数据,则称 T j T_j Tj依赖于 T i T_i Ti。
可恢复调度(recoverable schedule)应满足:当事务 T j T_j Tj要读事务 T i T_i Ti写的数据时,事务 T i T_i Ti必须要先于事务 T j T_j Tj提交。
示例3. 某银行信息系统有两项业务对应的事务T1、T2与存款关系有关。其中,转账业务:T1(A,B,50),从账户A向账户B转50元;计息业务:T2,对当前所有账户的余额计算利息,余额为Xx1.01。针对上述业务流程,回答下列问题:
(1) 若当前账户A余额为100元,账户B余额为200元。有两个事务分别为T1 (A,B,50),T2。可能的串行执行为:T1 -> T2 或 T2 -> T1,请计算串行执行结果。
T1 -> T2:A=50.5,B=252.5,A+B=303
T2 -> T1:A=51,B=252,A+B=303
(2) 若上述两个事务的一个并发调度顺序如下图所示,请问调度是否正确,为什么?
调度不正确,因为根据A、B 的初值,按照给定的调度,获得执行结果为:A=50.5、B=252,与任何一个串行执行的结果都不同,故为错误的调度,事实上会造成储户的无端损失。
2.2 并发操作带来的问题
并发操作带来的数据不一致性有三类:丢失修改、不可重复读和读脏数据
。
1)丢失修改
(a) 事务 T 1 T_1 T1、 T 2 T_2 T2都是对数据A做减1操作。事务 T 1 T_1 T1在时刻 t 5 t_5 t5把A修改后的值15写入数据库,但事务 T 2 T_2 T2在时刻 t 7 t_7 t7再把它对A减1后的值15写入。两个事务都是对A的值进行减1操作并且都执行成功,但A中的值却只减了1。现实的例子如售票系统,同时售出了两张票,但数据库里的存票却只减了一张,造成数据的不一致。原因在于 T 1 T_1 T1事务对数据库的修改被 T 2 T_2 T2事务覆盖而丢失了,破坏了事务的隔离性。
2)不可重复读
(b) 事务 T 1 T_1 T1读取A、B的值后进行运算,事务 T 2 T_2 T2在 t 5 t_5 t5时刻对B的值做了修改以后,事务 T 1 T_1 T1又重新读取A、B的值再运算,同一事务内对同一组数据的相同运算结果不同,显然与事实不相符。同样是事务 T 2 T_2 T2干扰了事务 T 1 T_1 T1的独立性。
3)读脏数据
© 事务 T 1 T_1 T1对数据C修改之后,在 t 4 t_4 t4时刻事务 T 2 T_2 T2读取修改后的C值做处理,之后事务 T 1 T_1 T1回滚,数据C恢复了原来的值,事务 T 2 T_2 T2对C所做的处理是无效的,它读的是被丢掉的垃圾值。
通过以上三个例子,在事务并行处理的过程中,因为多个事务对相同数据的访问,干扰了其他事务的处理,产生了数据的不一致性,这是因事务的隔离性被破坏。
问题的焦点在于事务在读写数据时不加控制而相互干扰。解决问题的方法是从如何保证事务的隔离性入手。
2.3 并发调度的可串行性
数据库系统必须控制事务的并发执行以保证数据库处于一致性状态。
1)可串行化的调度
多个事务的并发执行是正确的,当且仅当其结果与某一次序串行地执行它们时的结果相同,称这种调度策略是可串行化的调度(serializability schedule)。
可串行性是并发事务正确性的准则,按这个准则规定,一个给定的并发调度,当且仅当它是可串行化的才认为是正确调度。
2)冲突可串行化
冲突(conflict):当 I i I_i Ii和 I j I_j Ij是不同事务在相同的数据项上操作的命令,且至少有一个是 write命令时,则称 I i I_i Ii与 I j I_j Ij是冲突的。
考虑某调度S中含有分别属于事务 T i T_i Ti、 T j T_j Tj的两条命令 I i 、 I j ( i ≠ j ) I_i、I_j(i\ne j) Ii、Ij(i=j)。若 I i 、 I j I_i、I_j Ii、Ij分别访问不同的数据项,则交换 I i 、 I j I_i、I_j Ii、Ij的执行次序不会影响调度执行的结果。若 I i 、 I j I_i、I_j Ii、Ij访问相同的数据项,则 I i 、 I j I_i、I_j Ii、Ij的执行次序可能会影响调度执行的结果。在此只讨论 read 和 write 命令在以下四种情况时 I i 、 I j I_i、I_j Ii、Ij是否可交换:
(1) I i = r e a d ( D ) , I j = r e a d ( D ) I_i=read(D),I_j=read(D) Ii=read(D),Ij=read(D)。在调度S中, I i I_i Ii与 I j I_j Ij读到的是相同的数据D值,交换 I i 、 I j I_i、I_j Ii、Ij的执行次序不会影响执行的结果。
(2) I i = r e a d ( D ) , I j = w r i t e ( D ) I_i=read(D),I_j=write(D) Ii=read(D),Ij=write(D)。在调度S中,若 I i I_i Ii先于 I j I_j Ij执行,那么 I i I_i Ii没有读到 I j I_j Ij写回的D值,可能结果不一样,可见 I i 、 I j I_i、I_j Ii、Ij的执行次序是非常重要的。
(3) I i = w r i t e ( D ) , I j = r e a d ( D ) I_i=write(D),I_j=read(D) Ii=write(D),Ij=read(D)。在调度S中, I i 、 I j I_i、I_j Ii、Ij的执行次序是非常重要的。
(4) I i = w r i t e ( D ) , I j = w r i t e ( D ) I_i=write(D),I_j=write(D) Ii=write(D),Ij=write(D)。由于 I i 、 I j I_i、I_j Ii、Ij都是写操作, I i 、 I j I_i、I_j Ii、Ij的执行次序对 I i 、 I j I_i、I_j Ii、Ij没有影响,但是,若调度S中的下一条命令是read(D),那么读取的值会受到影响,因为数据库中只保留了后一条 write 命令写入的值。
等价调度:设 I i I_i Ii与 I j I_j Ij是调度S的两条连续的命令,若 I i I_i Ii与 I j I_j Ij是不同事务的命令且不冲突,则可以交换 I i I_i Ii与 I j I_j Ij的顺序得到一个新的调度 S ∗ S^* S∗。我们称 S S S与 S ∗ S^* S∗是等价的。
冲突等价(conflicte quivalent):如果 S S S调度经过一系列非冲突命令交换成 S ∗ S^* S∗,则称S与 S ∗ S^* S∗是冲突等价的。
冲突可串行化(confict serializable):若调度 S S S与一个串行调度 S ∗ S^* S∗冲突等价,则 S S S是冲突可串行化的。
冲突可串行化判定
在设计并发控制机制时,必须证明该机制产生的调度是否为可串行化的。
为了确定一个调 度S是否可串行化可以通过S构造一个有向图,也称优先图(precedence graph)。该图由G=(V, E)组成,其中,V是一个顶点集,由所有事务组成;E是一个边集,由满足下述三个条件的边 T i → T j T_i\rightarrow T_j Ti→Tj,组成:
(1) 在 I j I_j Ij执行Read(A)之前, T i T_i Ti执行Write(A)。
(2) 在 I j I_j Ij执行Write(A)之前, T i T_i Ti执行Read(A)。
(3) 在 I j I_j Ij执行Write(A)之前, T i T_i Ti执行Write(A)。
如果优先图中存在边 T i → T j T_i\rightarrow T_j Ti→Tj,则任何等价于S的串行调度S中 , T i T_i Ti必出现在 T j T_j Tj之前。
如果调度S的优先图中有环,则调度S是冲突不可串行化的;如果图中无环,则调度S 是冲突可串行化的。要判定冲突是否可串行化,首先需要构造有向图,然后调用环检测算法进行判定。
2.4 并发控制技术
并发事务如果对数据读写时不加以控制,会破坏事务的隔离性和一致性。为了保持事务的隔离性,系统必须对事务之间的相互作用加以控制,最典型的方式 是要求对数据对象以互斥的方式进行访问,即当一个事务访问某个数据对象时,其他事务都不能更新该数据对象。最常用的控制的手段就是加锁,该方法是只允许事务访问当前持有锁的数据项。
给数据对象加锁的方式有多种,主要有排它锁和共享锁。
- 排它锁 (Exclusive Locks,简称X锁)也称为写锁,用于对数据进行写操作时进行锁定。 如果事务T对数据A加上义锁后,就只允许事务T读取和修改数据A,其他事务对数据A
不能再加任何锁
,从而也不能读取和修改数据A,直到事务T释放A上的锁。 - 共享锁(Share Locks,简称S锁)也称为读锁,用于对数据进行读操作时进行锁定。如果事务T对数据A加上了S锁后,事务T就只能读数据A但不可以修改,其他事务可以再对数据A加S锁来读取,只要数据A上有S锁,
任何事务都只能再对其加S锁读取而不能加X锁修改
。
2.5 两段锁协议
通过对数据加锁,可以限制其他事务对数据的访问,但这会降低事务的并发性。如何在保证事务的一致性的前提下尽可能地提高并发性,这需要封锁协议来解决。封锁协议是对数据加锁类型、加锁时间和释放锁时间的一些规则的描述。
1)三级封锁协议
封锁协议有三个级别:一级封锁协议、二级封锁协议和三级封锁协议。
- 一级封锁协议:是事务T在修改数据A之前必须先对其加X锁,直到事务结束才释放X锁。一级封锁协议使得在一个事务修改数据期间,其他事务不能对该数据进行修改,只能等到该事务结束,
解决了丢失修改的问题
。 - 二级封锁协议:是一级封锁协议加上事务T在读取数据A之前必须对其加上S锁,读完后即可释放S锁。二级封锁协议使得一个事务不能读取被其他事务修改中的数据。
解决了读脏数据的问题
。但是,如果事务T在读取数据A之后,其他事务再对A做完修改,事务T再读取A,还会产生不可重复读的错误
。 - 三级封锁协议:是一级封锁协议加上事务T在读取数据A之前必须对其加上S锁,直到事务结束才释放S锁。三级封锁协议使得一个事务读取数据期间,其他事务只能读取该数据而不能修改,
解决了不可重复读的问题
。
2)两段锁协议
两段锁协议是指对任何数据进行读写之前必须对该数据加锁;在释放一个封锁之后,事务不再申请和获得任何其他封锁。
所谓“两段”锁的含义是:事务分为两个阶段。第一阶段是获得封锁,也称为扩展阶段;第二阶段是释放封锁,也称为收缩阶段
。
为了确保事务并行执行的正确性,许多系统采用两段锁协议。同时系统设有死锁检测机制。发现死锁后按一定的算法解除死锁。
如果事务都遵循两段锁协议,那么它们的并发调度是可串行化的。两段锁是可串行化的充分条件,但不是必要条件。即如果事务不遵循两段锁协议,那么它们的并发调度可能是可串行化的,也可能是不可串行化的。
需要注意的是采用两段锁协议也有可能产生死锁,这是因为每个事务都不能及时解除被它封锁的数据,可能会导致多个事务互相都要求对方已封锁的数据不能继续运行。
3)活锁与死锁
活锁是是指当事务 T 1 T_1 T1,封锁了数据R,事务 T 2 T_2 T2请求封锁数据R,于是 T 2 T_2 T2等待,当 T 1 T_1 T1释放了R上的封锁后,系统首先批准了 T 3 T_3 T3请求,于是 T 2 T_2 T2仍等待,当 T 3 T_3 T3释放了R上的封锁后,又批准了 T 4 T_4 T4请求,依此类推,使得 T 2 T_2 T2可能永远等待的现象。
死锁是指两个以上的事务分别请求封锁对方已经封锁的数据,导致长期等待而无法继续运行下去的现象。
2.6 多粒度封锁协议
封锁对象的大小称为封锁的粒度。
封锁的对象可以是逻辑单元(如属性、元组、关系、索引项、整个索引直至整个数据库),也可以是物理单元(如数据页或索引页)。
封锁粒度与系统的并发度和并发控制的开销密切相关。封锁的粒度越大,并发度越小,但系统开销也就越小;封锁的粒度越小,并发度越高,但系统开销也就越大。
选择封锁粒度时必须同时考虑封锁对象和并发度两个因素,对系统开销与并发度进行权衡,以求得最优的效果。一般说来,需要处理大量元组的用户事务可以以关系为封锁对象;需要处理多个关系的大量元组的用户事务可以以数据库为封锁对象;而对于一个处理少量元组的用户事务,可以以元组为封锁对象以提高并发度。
多粒度(multipl egranularity)机制是指通过允许各种大小的数据项并定义数据粒度的层次结构,其中小粒度数据项嵌套在大粒度数据项中。对此,可以构造一个粒度层次图,粒度层次图像一棵倒置的树,故也称多粒度树。
采用多粒度树的好处是:减少对后代结点加锁的系统代价
意向锁是与共享锁和排它锁相关联的另一种锁。
多粒度封锁协议(multiple-granularity looking protocol)允许多粒度树中的每个结点被独立地加锁,对某结点加锁意味着该结点的所有后代结点也被加了同类型的锁。
3. 数据库的备份与恢复
数据库的运行过程中,难免会出现计算机系统的软、硬件故障,这些故障会影响数据库中数据的正确性,甚至破坏数据库,使数据库中全部或部分数据丢失。因此,数据库的关键技术在于建立冗余数据,即备份数据
。如何在系统出现故障后及时使数据库恢复到故障前的正确状态,就是数据库恢复技术。
3.1 数据库系统故障
数据库系统中可能发生的故障有很多,主要有事务故障、系统故障和介质故障。
1)事务故障
事务故障(transaction failure)是由于程序执行错误而引起事务非预期的、异常终止的故障。
主要有如下两类错误:
(1)逻辑错误。如非法输入、找不到数据、溢出、超出资源限制等原因引起的事务执行失败。
(2)系统错误。系统进入一种不良状态(如死锁),导致事务无法继续执行。
对于不可以预期的错误应用程序无法处理,是由 DBMS 系统实现故障恢复的。
事务故障意味着事务没有达到预期的终点(COMMIT 或者显示 ROLLBACK),因此数据库可能处于不正确状态。恢复程序要在不影响其他事务运行的情况下,强行回滚该事务,即撤销该事务已经做出的任何对数据库的修改,这类恢复操作称为事务撤销(UNDO)。
2)系统故障
系统故障是指硬件故障、软件(如 DBMS、OS 或应用程序)漏洞的影响,导致丢失了内存中的信息,影响正在执行的事务,但未破坏存储在外存上的信息。这种情况称为故障-停止假设(fail-stop assumption)。
系统故障中止了事务的执行过程,破坏了事务的原子性,由于缓冲区中的内容可能部分已写入数据库,系统重启后数据库可能处于不一致状态。
3)介质故障
介质故障是指数据库的存储介质发生故障,如磁盘损坏、瞬间强磁场干扰等。这种故障直接破坏了数据库,会影响到所有正在读取这部分数据的事务。
3.2 数据库的备份
数据是资产,备份最重要。数据转储是将数据库自制到另一个磁盘或磁带上保存起来的过程,又称为数据备份。数据的备份分为静态转储和动态转储、海量转储和增量转储。
(1)静态转储和动态转储。静态转储是指在转储期间不允许对数据库进行任何存取、修改操作;动态转储是在转储期间允许对数据库进行存取、修改操作,因此,转储和用户事务可并发执行。数据转储可以由系统管理员(DBA)来操作,如静态转储,可以设定时间计划由 DBMS 定时执行,可以在事务程序中增加功能实现动态转储,也可以通过硬件系统的冗余磁盘阵列来实现。
(2)海量转储和增量转储。海量转储是指每次转储全部数据;增量转储是指每次只转储上次转储后更新过的数据。
(3)日志文件。在事务处理的过程中,DBMS把事务开始、事务结束以及对数据库的插入、删除和修改的每一次操作写入日志文件。每条记录包括的主要内容有执行操作的事务标识、操作类型、更新前数据的旧值(插入操作此项为空)、更新后的数据值(删除操作此项为空)、更新日期和更新时间。一旦发生故障,DBMS 的恢复子系统利用日志文件撤销事务对数据库的改变,回退到事务的初始状态。因此,DBMS 利用日志文件来进行事务故障恢复和系统故障恢复,并可协助后备副本进行介质故障恢复。
登记日志文件时必须严格
按照并发事务执行的时间次序来记录,且要先写日志文件后写数据库
。
(4)数据库镜像。为了避免磁盘介质出现故障影响数据库的可用性,许多 DBMS 提供数据库镜像功能用于数据库恢复。需要说明的是,数据库镜像是通过复制数据实现的,但频繁地复制数据会降低系统的运行效果。因此实际应用中往往对关键的数据和日志文件镜像。
3.3 数据库的恢复
要使数据库在发生故障后能够恢复,必须建立冗余数据,在故障发生后利用这些冗余数据实施数据库恢复。建立冗余数据常用的技术是数据转储和建立日志文件。在一个数据库系统中,这两种方法一般是同时被采用的。
故障恢复有撤销事务(UNDO)和重做事务(REDO)两个操作:
- 撤销事务 (UNDO) 是将未完成的事务撤销,使数据库恢复到事务执行前的正确状态。
- 撤销事务的过程:反向扫描未完成的事务日志 (由后向前扫描),查找事务的更新操作;对该事务的更新操作执行逆操作,用日志文件记录中更新前的值写入数据库,插入的记录从数据库中删除,删除的记录重新插入数据库中;继续反向扫描日志文件,查找该事务的其他更新操作并执行逆操作直至事务开始标志。
- 重做事务 (REDO) 是将已经提交的事务重新执行。
- 重做事务的过程:从事务的开始标识起,正向扫描日志文件,重新执行日志文件登记的该事务对数据库的所有操作,直至事务结束标识。
对于不同的故障,采取不同的恢复策略。
1)事务故障恢复
事务故障是事务在运行至正常终止点(SUMIMIT 或 ROLLBACK)前终止,日志文件只有该事务的开始标识而没有结束标识。对这类故障的恢复是通过撤销(UNDO)产生故障的事务,使数据库恢复到该事务执行前的正确状态来完成的。事务恢复有如下四个步骤:
- 步骤1:反向扫描日志文件(即从最后向前扫描日志文件),查找该事务的更新操作。
- 步骤2:对事务的更新操作执行逆操作,也就是将日志记录更新前的值写入数据库。
- 步骤3:继续反向扫描日志文件,查找该事务的其他更新操作,并做同样的处理,直到事务的开始标志。
- 步骤4:继续处理下去,直到读到了此事务的开始标记,事务故障恢复完成了。
事务故障的恢复由系统自动完成,对用户是透明的。
2)系统故障的恢复
系统故障会使数据库的数据不一致,原因有两个:
- 一是未完成的事务对数据库的更新可能已写入数据库,撤销(UNDO)未完成的事务。
- 二是已提交的事务对数据库的更新可能还在缓冲区中没来得及写入数据库。因此恢复操作就是要撤销故障发生时未完成的事务,
重做(REDO)已提交的事务
。
系统故障的恢复是在系统重启之后自动执行的
。
3)介质故障的恢复
介质故障时数据库遭到破坏,需要重装数据库,装载故障前最近一次的备份和故障前的日志文件副本,再按照系统故障的恢复过程执行撤销和重做来恢复。
介质故障的恢复需要 DBA 的参与,DBA 只需要重装最近转储的数据库副本和有关的各日志文件副本;然后执行系统提供的恢复命令;具体的恢复操作仍由 DBMS 完成。
4. 数据库的安全性与完整性
4.1 数据库的安全性
除了完整性约束提供保护意外引入的不一致性之外,数据库中存储的数据还要防止未经授权的访问和恶意的破坏或修改。
恶意访问的形式主要包括:未经授权读取数据(窃取信息);未经授权修改数据;未经授权破坏数据。
数据库安全性(databasesecurity)指保护数据库不受恶意访问。需要注意的是绝对杜绝对数据库的恶意滥用是不可能的,但是可以使那些企图在没有适当授权的情况下访问数据库的代价足够高,以阻止绝大多数这样的访问企图。为了保护数据库的安全,可以在以下五个层次上采取安全性措施:
- 数据库系统层次(database system)。数据库系统的某些用户获得的授权可能只允许他访问数据库中有限的部分,而另外一些用户获得的授权可能允许他提出查询,但不允许他修改数据。保证这样的授权限制不被违反是数据库系统的责任。
- 操作系统层次(operatingsystem)。不管数据库系统多安全,操作系统安全性方面的弱点总是可能成为对数据库进行未授权访问的一种手段。
- 网络层次(network)。由于几乎所有的数据库系统都允许通过终端或网络进行远程访问,网络软件的软件层安全性和物理安全性一样重要,不管在因特网上还是在私有的网络内。
- 物理层次(physical)。计算机系统所位于的结点(一个或多个)必须在物理上受到保护,以防止入侵者强行闯入或暗中潜入。
- 人员层次(human)。对用户的授权必须格外小心,以减少授权用户接受贿赂或其他好处而给入侵者提供访问机会的可能性。
为了保证数据库安全,用户必须在上述所有层次上进行安全性维护。如果较低层次上(物理层次或人员层次)安全性存在缺陷,高层安全性措施即使很严格也可能被绕过。
数据库安全机制:用户标识与鉴别、访问控制(自主存取控制(授权和角色)、强制存取控制、基于角色的存取控制)、视图机制、数据加密、审计、备份和恢复、聚合、推理和多实例。
1)通过权限操作控制
通过 DBMA 提供的授权功能赋予用户在数据库各个部分上的几种形式的授权,其中包括:
- read 授权允许读取数据,但不允许修改数据。
- insert 授权允许插入新数据,但不允许修改已经存在的数据。
- update 授权允许修改数据,但不允许删除数据。
- delete 授权允许删除数据。
可以赋予用户获得上面的所有授权类型或其中一部分的组合,也可以根本不获得任何授权。除了以上几种对数据访问的授权外,用户还可以获得修改数据库模式的授权:
- index 授权允许创建和删除索引。
- resource 授权允许创建新关系。
- alteration 授权允许添加或删除关系中的属性。
- drop 授权允许删除关系。
drop 授权和 delete 授权的区别在于 delete 授权只允许对元组进行删除。如果用户删除了关系中的所有元组,关系仍然存在,只不过是空的。如果关系被删除,那么关系就不再存在了。系中的所有元组,关系仍然存在,只不过是空的。如果关系被删除,那么关系就不再存在了。可以通过 resource 授权来控制创建新关系的能力。具有 resource 授权的用户在创建新关系后自动获得该关系上的所有权限。
index 授权看起来似乎是不必要的,因为索引的创建和删除不会改变关系中的数据。事实上,索引是提高性能的一种结构。但是,索引也会消耗空间,并且所有数据库的修改都需要更新索引。如果 index 授权被授予所有用户,那么执行更新操作的用户倾向于删除索引,而提出查询的用户倾向于创建大量索引。为了使数据库管理员能够管理系统资源的使用,我们有必要将索引的创建作为一种权限来看待。
最大的授权形式是给数据库管理员的。数据库管理员可以给新用户授权,可以重构数据库,等等。这一授权形式类似于操作系统中提供给超级用户或操作员的权限。
2)通过视图控制权限
视图是给用户提供个性化数据库模型的一种手段,而且可以隐藏用户不需要看见的数据。视图隐藏数据的能力既可以用于简化系统的使用,又可以用于实现安全性。由于视图只允许用户关注那些感兴趣的数据,它简化了系统的使用。尽管用户可能不被允许直接访问某个关系,但用户可能被允许通过一个视图访问该关系的一部分。因此,关系级的安全性和视图级的安全性可以结合起来,用于限制用户只能访问所需数据。
3)注意权限的授予
获得了某种形式授权的用户可能被允许将此授权传递给其他用户。但是,我们对于授权可能会在用户间怎样传递必须格外小心,以保证这样的授权在未来的某个时候可以被收回。
4)通过角色控制
考虑一个有很多出纳的银行。每一个出纳必须对同一组关系具有同种类型的权限。无论何时指定一个新的出纳,他都必须被单独授予所有这些授权。一个更好的机制是指明所有出纳应该有的授权,并单独标示出哪些数据库用户是出纳。系统可以用这两条信息来确定每一个有出纳身份的人的权限。当一个人被新雇佣为出纳时,必须给他分配一个用户标识符,并且必须将他标示为一个出纳,而不需要重新单独给予出纳权限。
在数据库中建立一个角色集,和授予每一个单个用户一样,可将权限授予角色。分配给每个数据库用户一些他(或她)有权扮演的角色(也可能是空的)。
5)通过审计追踪控制
很多安全的数据库应用软件需要维护审计追踪(audittrail)。审计追踪是一个对数据库所有更改(插入/删除/更新)的日志,还包括一些其他信息,如哪个用户执行了更改和什么时候执行的更改等。
可以在关系更新操作上定义适当的触发器来建立一个审计追踪(利用标示用户名和时间的系统变量)。然而,很多的数据库系统提供了内置机制来建立审计追踪,用起来会更加方便。
6)通过数据加密控制
目前数据加密仍是计算机系统对信息进行保护的一种最可靠的办法。它利用密码技术对信息进行加密,实现信息隐蔽,从而起到保护信息的安全的作用。对数据库中的数据进行加密,可以防止数据在存储和传输过程中失密。
按照作用的不同,数据加密技术可分为数据传输加密技术、数据存储加密技术、数据完整性的鉴别技术和密钥管理技术。
- 数据传输加密技术的目的是对传输中的数据流加密,通常有线路加密与端一端加密两种。线路加密侧重在线路上而不考虑信源与信宿,是对保密信息通过各线路采用不同的加密密钥提供安全保护。端一端加密指信息由发送端自动加密,并且由 TCP/IP 进行数据包封装,然后作为不可阅读和不可识别的数据穿过互联网,当这些信息到达目的地,将被自动重组、解密,从而成为可读的数据。
- 数据存储加密技术的目的是防止在存储环节上的数据失密,数据存储加密技术可分为密文存储和存取控制两种。前者一般是通过加密算法转换、附加密码、加密模块等方法实现;后者则是对用户资格、权限加以审查和限制,防止非法用户存取数据或合法用户越权存取数据。
- 数据完整性鉴别技术的目的是对介入信息传送、存取和处理的人的身份和相关数据内容进行验证,一般包括又令、密钥、身份、数据等项的鉴别。系统通过对比验证对象输入的特征值是否符合预先设定的参数,实现对数据的安全保护。
- 密钥管理技术包括密钥的产生、分配、保存、更换和销毁等各个环节上的保密措施。
4.2 数据库的完整性
数据库的完整性是指数据的正确性和相容性。如学生的性别只能是男或女,百分制的成绩只能取0到100的整数值等。为防止错误数据进入数据库,DBMS提供了完整性约束机制,通过对数据库表结构进行约束,当对数据进行修改时由系统对修改数据进行完整性检查,将错误数据拒绝于数据库之外。
完整性约束条件作用的对象可以是表、行和列三种。
- 列级约束主要是对列的类型、取值范围、精度、非空值、值不可重复等的约束条件。- 行级约束是记录字段值之间联系的约束条件,如余额应该等于存入金额减去支出金额的差值。
- 表级约束是表的主码约束、表与表间的参照完整性约束、表中记录间的联系约束,如部门最高工资不能大于本部门平均工资的5倍。
列级约束、主码约束、参照完整性约束是在数据库定义过程中定义的,并和数据库定义的其他信息存储在数据字典中。标准SQL的DDL语言提供了这种功能,其他的相对复杂的约束需要编写触发器(trigger)程序实现。
在事务程序对数据库进行修改时,对于数据库定义的约束,由DBMS提供的完整性约束 机制来检查,如果不符合约束条件则拒绝修改并给出提示。对于触发器程序编制的约束,由触发器机制执行程序来实现约束。