书城计算机数据库原理及Oracle应用
31848700000022

第22章 数据库的保护(6)

8.2.2.4 Oracle的恢复方法

与备份方法对应的恢复方法也可以分为物理恢复与逻辑恢复。物理恢复是针对物理文件的恢复,逻辑恢复是用Oracle系统提供的IMPORT工具将EXPORT工具存储在一个专门的操作系统文件中的内容按逻辑单元(表、用户、表空间、数据库)进行恢复。IMPORT工具和EXPORT工具必须配套使用。

1.物理恢复

物理恢复又可分数据库运行在非归档方式下脱机恢复、数据库运行在归档方式下的联机恢复。

(1)非归档模式下的物理恢复

非归档方式下脱机恢复是指一旦组成数据库的物理文件中有一个文件遭到破坏,必须在数据库关闭的情况下将全部物理文件装入到对应的位置上,进行恢复。上次备份后所做的所有操作都丢失,这些操作必须用手工方法进行恢复。这种恢复方法适合数据库运行于非归档模式,只能恢复到最后一次备份点,并且要求DBA勤做备份,否则损失很严重。

假如组成数据库物理文件中的任何一个文件遭到破坏,在非归档模式下只能按下列方法进行整个数据库的恢复,恢复到上次备份点,上次备份后所做的所有操作必须手工重做。

首先保证数据库运行在非归档模式,接着关闭数据库,然后将非归档模式下物理冷备份时备份出来的所有数据文件、联机日志文件、控制文件按备份时的路径分别装入到数据库,最后重新启动数据库。

下列示例说明了非归档模式下物理恢复的步骤,并说明了备份后所做的操作没有恢复。

打开数据库:

SQL>connect/as sysdba

SQL>startup

在USERS表空间上建立test表,并往test表中插入数据。例如,连接到scott用户,在scott用户中建立test表,并往test表中插入数据。

SQL>connect scott/tiger

SQL>CREATE TABLE test(t1 number,t2 date)TABLESPACE users

SQL>INSERT INTO test VALUES(1,sysdate)

SQL>INSERT INTO test VALUES(2,sysdate)

SQL>INSERT INTO test VALUES(3,sysdate)

SQL>COMMIT

关闭数据库:

SQL>connect/as sysdba

SQL>shutdown

数据库已经关闭。

已经卸载数据库。

Oracle 例程已经关闭。

SQL>EXIT

用操作系统命令删除users01.dbf文件(即删除users表空间所对应的数据文件):

C:\del e:\oracle\oradata\oracle\users01.dbf

启动数据库,观察提示信息:

SQL>connect/as sysdba

SQL>startup

Oracle 例程已经启动。

Total System Global Area 135338868 bytes

Fixed Size 453492 bytes

Variable Size 109051904 bytes

Database Buffers 25165824 bytes

Redo Buffers 667648 bytes

数据库装载完毕。

ORA-01157:不能识别/锁住数据文件9——查看DBWR跟踪文件

ORA-01110:数据文件9:′e:\oracle\oradata\oracle\users01.dbf′

上述提示信息说明没法打开数据库,因为数据文件9找不到。

进行恢复。首先将刚才没有打开的数据库关闭。

SQL>connect/as sysdba

SQL>shutdown

数据库没有打开。

已经卸载数据库。

Oracle 例程已经关闭。

SQL>EXIT

然后用操作系统命令,将8.2.2.3节非归档模式下物理备份中备份出来的整个数据库的所有数据文件、联机日志文件、控制文件按原来的目录路径装入。此时将该目录中的文件全部覆盖。

C:\copy h:\oracle\cold\*.*e:\oracle\oradata\oracle\/*装入整个数据库*/

装入后重新打开数据库。此时所有数据文件、联机日志文件、控制文件都恢复到备份时的状态。

SQL>connect/as sysdba

SQL>startup

Oracle 例程已经启动。

Total System Global Area 135338868 bytes

Fixed Size 453492 bytes

Variable Size 109051904 bytes

Database Buffers 25165824 bytes

Redo Buffers 667648 bytes

数据库装载完毕。

数据库已经打开。

在做了非归档模式下物理冷备份后,测试新建的表test及往表中插入的数据在非归档模式下的物理恢复后是否存在。

SQL>connect scott/tiger

SQL>SELECT*FROM test

SELECT*FROM test

*

error 位于第1 行:

ORA-00942:表或视图不存在

上述测试说明在非归档模式下物理冷备份后,新建的表TEST及往表中插入的数据在非归档模式下物理恢复后不存在。即说明数据库运行于非归档模式时只能恢复到最后一次备份点,备份后所做的操作不能恢复。

(2)归档模式的物理恢复

归档方式下的联机恢复是指一旦这些数据文件中某一个遭到破坏,将该数据文件的备份装入到对应位置,然后利用上次备份后产生的归档日志文件和联机日志文件进行恢复,可以恢复到失败这一刻。这种恢复方法要求数据库必须运行在归档模式。

要求数据库运行在归档模式,并已成功地进行了8.2.2.3节讲述的归档模式下物理备份。如果数据库没有运行在归档模式,按8.2.2.2节讲述的方法将数据库置于归档模式。按下列方法进行归档模式下的物理恢复。

首先打开数据库,并确认数据库运行于归档模式,然后对数据库进行操作,接着将刚在操作的内容归档到归档文件。此时如果组成数据库的物理文件中某一个数据文件遭到破坏,造成数据库无法启动。需要将被破坏的数据文件以前的备份按原路径装入,但因为此数据文件是以前备份的,其序列号与其他没有被破坏的数据文件的序列号不一致,所以必须利用归档的日志文件进行恢复。此时启动数据库到MOUNT状态,发RECOVER命令,系统自动利用备份后产生的归档日志文件进行恢复,恢复到所有数据文件序列号一致时为止。最后将此数据文件设为ONLINE,并打开数据库到OPEN状态。

下列实例说明了归档模式下物理恢复的步骤,并说明恢复了备份后所做的操作。

启动数据库并确认数据库运行在自动归档模式:

SQL>connect/as sysdba

SQL>startup/*启动数据库并保证运行于归档模式*/

SQL>arhive log list/*验证数据库运行于归档模式*/

数据库日志模式 存档模式

自动存档 启用

存档终点E:\oracle\ora92\RDBMS

最早的概要日志序列58

当前日志序列60

建立新用户test并授权,在test用户中建立test表,并往表中插入数据和提交:

SQL>CREATE USER test IDENTIFIED BY test/*建立新用户*/

DEFAULT TABLESPACE users

TEMPORARY TABLESPACE temp

QUOTA 10m ON USERS

SQL>GRANT CONNECT to test/*给用户授权*/

SQL>CONNECT test/test/*新用户连接*/

SQL>CREATE TABLE test(t1 number,t2 date)/*建表*/

SQL>INSERT INTO test values(1,sysdate)/*往表中插入数据*/

SQL>INSERT INTO test values(2,sysdate)

SQL>INSERT INTO test values(3,sysdate)

SQL>COMMIT

SQL>DISCONNECT

以sysdba权限登录,进行日志切换,以便将刚在所做的操作归档到归档日志文件:

SQL>connect/as sysdba

SQL>ALTER SYSTEM SWITCH LOGFILE/*产生日志切换*/

SQL>ALTER SYSTEM SWITCH LOGFILE

SQL>ALTER SYSTEM SWITCH LOGFILE

假设数据库有三个联机日志文件组,日志切换三次,保证刚在插入的数据已被归档到归档日志文件。

关闭数据库,删除数据文件users01.dbf:

SQL>connect/as sysdba

SQL>shutdown

数据库没有打开。

已经卸载数据库。

Oracle 例程已经关闭。

SQL>host del e:\oracle\oradata\oracle\users01.dbf

执行打开数据库命令,观察现象(数据库无法启动,注意提示信息):

SQL>connect/as sysdba

SQL>startup

Oracle 例程已经启动。

Total System Global Area 135338868 bytes

Fixed Size 453492 bytes

Variable Size 109051904 bytes

Database Buffers 25165824 bytes

Redo Buffers 667648 bytes

数据库装载完毕。

ORA-01157:不能识别/锁住数据文件9——查看DBWR跟踪文件

ORA-01110:数据文件9:′e:\oracle\oradata\oracle\users01.dbf′

上述提示信息说明没法打开数据库,因为数据文件9找不到。

将8.2.2.3节归档模式下物理备份的users01.dbf 文件装入到对应的目录:

SQL>host copy h:\oracle\hot\users01.dbf E:\oracle\oradata\oracle\

执行数据库恢复,并打开数据库:

SQL>RECOVER DATABASE AUTO

介质恢复完成。

由于在8.2.2.3节归档模式下物理备份中备份的users01.dbf文件的序列号与其他数据文件和控制文件的序列号不同,所以系统自动使用备份后产生的归档日志文件执行数据库恢复。恢复完成后所有文件的序列号相同。此时将users01.dbf文件置为online状态,以便执行下一步的查询操作;然后将数据库打开。

SQL>ALTER DATABASE DATAFILE′E:\Oracle\ORADATA\Oracle\users01.dbf′ONLINE

SQL>ALTER DATABASE OPEN

数据库已经打开。

上述测试说明在归档模式下物理备份后,新建的表TEST及往表中插入的数据在归档模式下物理恢复后是存在的。即说明数据库运行于归档模式时可以恢复到最后失败点,备份后所做的操作可以通过归档日志文件进行恢复。

2.逻辑恢复

IMPORT工具从EXPORT工具卸出的专门操作系统文件中读取数据,将其插入到数据库中。根据卸出的四种模式(整个数据库模式、用户模式、表模式和表空间模式)可以分别装入整个数据库对象、某一用户的对象、某一张表上的对象或表空间上的对象。拥有IMPFULLDATABASE角色权限的用户才能执行整个数据库的装入。通过在操作系统提示符下运行IMP HELPY命令可以获得IMPORT工具的联机帮助。

装入运行方式有三种:交互方式、命令行方式和参数文件方式。

(1)交互方式

交互方式装入时首先在操作系统提示符E:\下输入imp,然后IMPORT工具会一步一步提示用户回答系统提出的问题,根据用户的回答,IMPORT工具一步一步往下执行装入相应的内容。

下列命令将e:\oracle\ff.dmp文件中所包含的数据,根据需要将DEPT和emp 表装入到scott用户中。为避免出错,应该事先删除DEPT和emp 表,或者删除这两张表中的数据。

E:\>imp

Import:Release 9.2.0.1.0-Production on 星期三1 月19 12:02:18 2005

(c)Copyright 1982,2002,Oracle Corporation.All rights reserved.

用户名:scott/tiger

连接到:Oracle9i Enterprise Edition Release 9.2.0.1.0-Production

With the Partitioning and Java options

PL/SQL Release 9.2.0.1.0-Production

导入文件:EXPDAT.DMP>e:\oracle\ff.dmp

输入插入缓冲区大小(最小为8192)30720>

只列出导入文件的内容(yes/no):no>

由于对象已存在,忽略创建错误(yes/no):no>yes

导入权限(yes/no):yes>

导入表数据(yes/no):yes>

导入整个导出文件(yes/no):no>

用户名:scott

输入表(T)或分区(T:P)名称。空列表表示用户的所有表>dept

输入表(T)或分区(T:P)名称。空列表表示用户的所有表>emp

……

(2)命令行方式

命令行方式就是将交互方式中所有用户回答的内容全部写在命令行上,每一个回答的内容作为某一关键字的值。IMPORT工具命令行的语法如下:

IMP[KEYWORD]{VALUE|(VALUE,VALUE……)}

[[[,]KEYWORD]{VALUE|(VALUE,VALUE……)}]……

其中:KEYWORD是关键字,VALUE是为关键字赋的值。

IMPORT工具常用的关键字包括:USERID、TABLES、FILE、IGNORE、FULL、FROMUSER和TOUSER。其中USERID、TABLES、FULL关键字的含义与EXPORT工具中一致,这里不再解释。

FILE指定在EXPORT工具卸出时存储卸出内容的专门操作系统文件。

FROMUSER指定卸出对象的用户列表。

TOUSER指定需要装入对象的用户列表。只有拥有IMPFULLDATABASE角色权限的用户才能使用此关键字,将对象装入到其他用户账号中。

IGNOREY表示在装入过程中建立对象时忽略错误。在这种情况下,装入会继续,没有报告错误。对表来说,IGNOREY可以将行装入到已经存在的表中,没有给出任何消息。假如该对象已经存在时,如果IGNOREN会产生一个错误。

例8-49 用命令行方式导入C:\oracle\emp.dmp文件中的dept和emp表。用户scott按表模式从卸出文件emp.dmp中读取dept和emp两张表的数据装入到数据库中,并且忽略对象建立时的错误。

C:\IMP USERIDscott/tiger TABLES(dept,emp)FILEc:\oracle\emp.dmp

LOGexp.log IGNOREY

(3)参数文件方式

参数文件就是存放上述关键字和相应值的一个文件,该文件名作为命令行的PARFILE关键字的值。如果在参数文件中没有列出的关键字,该关键字就采用其默认值。

例8-50 用参数文件方式导入scott用户的dept和emp表。先编辑一个参数文件,名为C:\PAR2.TXT,内容如下:

USERIDscott/tiger

TABLES(dept,emp)

FILEC:\oracle\ff.dmp

LOGC:\oracle\ff.log

FROMUSERscott

TOUSERjohn

然后执行IMPORT工具时使用该参数文件(包含路径)。表示按表模式将scott用户卸出的dept、emp表装入john用户中,装入过程中的消息写到ff.log文件中。只有拥有IMPFULLDATABASE角色权限的用户才能将某一用户的对象装入到其他用户中。执行命令如下:

C:\IMP PARFILEC:\PAR2.TXT

8.3 数据库的并发性控制

数据库是一个共享资源,可以供多个用户使用。多个用户的事务可以串行地执行,即每个时刻只有一个事务在运行和存取数据库中的数据。此时许多资源都处于空闲状态。为了充分利用数据库资源,应该允许多个用户程序并发地执行,并发地存取数据库中的数据。

在多用户共享系统中,多个用户同时对同一数据进行操作称为并发操作。并发操作时,由于多个事务的操作是随机调度的,事务之间可能有干扰,破坏了事务的隔离性。DBMS的并发控制子系统负责正确调度并发事务的操作,使事务之间不受干扰,保证数据的一致性不受破坏,避免用户得到不正确的数据。

8.3.1 并发控制的原理和方法

8.3.1.1 并发操作带来的问题

下面先看一个实例,说明并发操作带来的数据不一致问题。

例如,考虑飞机订票系统中的一个活动序列:

①甲售票点(甲事务)读出某航班的机票余额A,设A50;

②乙售票点(乙事务)读出某航班的机票余额A,也为50;

③甲售票点卖出10张机票,修改余额AA-10,所以A 为40,把A写回数据库;

④乙售票点也卖出10张机票,修改余额AA-10,所以A 为40,把A写回数据库;

结果明明卖了20张机票,但数据库中机票余额只减少10。

这种情况称为数据库的不一致性。这种不一致性是由并发操作引起的。在并发操作的情况下,对甲、乙两个事务操作序列的调度是随机的。如果按上述调度序列执行,甲事务的修改被丢失。

仔细分析并发操作带来的数据不一致性包括三类:丢失更新问题、不可重复读问题和读“脏”数据问题。

1.丢失更新问题

丢失更新问题是指两个事务T1和T2读入同一数据并分别更新,T2提交的更新结果破坏了T1提交的更新结果,导致T1的更新被丢失。

事务T1(对应甲事务)和事务T2(对应乙事务)同时对A(机票数)的值更新。A的初值是50,事务T1卖出10张票,对A的值减10。事务T2也卖出10张票,对A的值也减10。结果A的值是40,而不是30,这是错误的。原因是在第8步丢失了事务T1对数据库的更新操作。

2.不可重复读问题

不可重复读问题是指事务T1读取数据后,事务T2执行更新操作,使得T1无法再现前一次读取的结果(即T1两次读取的数据不一样)。

当事务T2按某种条件读取数据库中的某些记录值后,由于另一个事务T1更新这些记录,或者删除这些记录,或者插入了新记录。而导致事务T2仍按原条件读取数据时,发现前后两次读取的数据不一致。这就是不可重复读问题。

3.读“脏”数据问题

读“脏”数据问题是指事务T1修改某一数据,并将其写回磁盘。事务T2读取同一数据后,T1由于某种原因被撤销,这时T1已修改过的数据恢复原值,T2读到的数据就与数据库中的数据不一致,则T2读到的数据就称为“脏”数据,即不正确的数据(即未提交且又被撤销的数据)。

事务T2读取了未提交的A的值40。紧接着,事务T1做了回退(ROLLBACK)操作,把A的值恢复为50。而事务T2仍在使用被撤销前的A的值40。在数据库技术中,未提交随后又被撤销的数据称为“脏数据”。

产生上述三类数据不一致的主要原因是并发操作破坏了事务的隔离性。并发控制就是要用正确的方式调度并发操作,使一个事务的执行不受其他事务的干扰,从而避免造成数据的不一致性。

并发控制的主要技术是封锁(LOCK)技术。

8.3.1.2 封锁的机制和原理

封锁是实现并发控制的一个非常重要的技术。封锁就是事务T在对某一对象操作之前,向系统发请求,请求对该对象加锁。加锁后事务T就对该对象有了一定的控制,在事务T释放它的锁之前,其他事务不能更新此对象。

1.封锁的类型

封锁的类型有两类:排他锁(exclusive,简称X锁)和共享锁(share,简称为S锁)。

(1)排他锁(X锁)

排他锁又称为写锁。如果事务T对数据对象A实现了X封锁,则只允许T读取和修改A的数据。在T解除A上的X封锁之前,其他事务不能对A加任何类型的锁。这就保证其他事务在T释放A上的锁之前不能读取和修改A的数据。只有获得X锁的事务才能对被封锁的对象进行更新。

LOCK X(R)命令表示给对象R加X锁。加锁是否成功取决于R上是否已有其他锁。

(2)共享锁(S锁)

共享锁又称为读锁。若事务T对数据对象A加上S锁,则事务T可以读取A的数据,但不能修改A的数据。在T释放A上的S锁之前,其他事务只能对A加S锁,不能加X锁。这就保证事务T对数据对象A加S锁后,其他事务可以读A的数据,但不能对A进行任何更新操作。

LOCK S(R)命令表示给对象R加S锁。加锁是否成功取决于R上是否已有其他锁。

2.活锁和死锁

和操作系统一样,封锁的方法可能引起活锁和死锁。

(1)活锁

可能存在某个事务永远处于等待状态,得不到执行,这种现象称为活锁。

例如:事务T1封锁对象R,事务T2又请求封锁R,于是T2等待。T3也请求封锁R,当T1释放R上的封锁之后系统首先批准了T3的请求,T2仍然在等待。然后T4又请求封锁R,当T3释放R上的封锁之后系统又批准了T4的请求……,T2有可能永远处于等待,这就是活锁的情形。

避免活锁的方法是采用“先来先服务”的策略。当多个事务请求封锁同一对象时,封锁子系统按请求封锁的先后次序对事务进行排队。对象上的锁一旦释放,就批准申请队列中第一个事务获得锁。

2.死锁

两个或两个以上的事务都处于等待状态,每个事务都在等待另一个事务解除封锁,它才能执行下去,结果任何一个事务都无法执行,这种现象称为死锁。

例如,事务T1封锁对象R1,T2封锁对象R2;然后T1又请求封锁R2,因为R2已被T2封锁,所以T1等待T2释放R2上的锁;接着T2又申请封锁R1,因为R1被T1 封锁,所以T2等待T1释放R1上的锁。这样出现了T1等T2释放锁,T2又等T1释放锁的局面。T1和T2两个事务永远不能结束,形成死锁。

解决死锁的一种方法是采取一定的措施预防死锁的发生。另一种方法是定期检查是否发生死锁,如果发现发生了死锁,那么只能抽出某个事务作为牺牲品,将该事务撤销,做回退操作。撤销时解除该事务所有的锁,恢复到该事务的初始状态。此时其他事务可以获得相应的锁,有可能消除死锁现象。

3.封锁协议

在运用X锁和S锁对数据对象加锁时,需要约定一些规则,这些规则称为封锁协议。封锁协议就是规定什么时候可以申请加锁,锁定多长时间,以及什么时候释放锁等内容。对不同的封锁方式规定不同的规则,形成了各种不同的封锁协议。下面介绍三种封锁协议。这三种封锁协议在不同程度上解决并发操作所引起的丢失更新、不可重复读、读“脏”数据等问题,保证了并发操作的正确调度。

(1)一级封锁协议(即PXC协议)

一级封锁协议指:事务T在修改数据A之前必须先对A加X锁(可用LOCK X(A)命令),直到事务结束才释放X锁;如果得不到锁,则等待。COMMIT和ROLLBACK操作结束事务。(即修改前先获得X锁,得不到锁就等待。此锁只能由COMMIT或ROLLBACK命令释放,不能通过UNLOCK命令释放锁。)

一级封锁协议可防止丢失更新问题,并保证事务T是可恢复的。

当事务T2在时间点4时请求对A加X锁被拒绝,T2只能等待T1释放A上的锁后才能获得对A的X锁。事务T1在时间点7时提交对A的修改,并释放锁,此时数据库中A的值为修改后的值40。事务T2在时间点9时获得对A的X锁,读取的数据A为T1更新后的值40,再对新值40进行运算,并将结果30写入磁盘。这样避免事务T1的更新被丢失问题。

一级封锁协议规定:更新操作之前必须先获得X锁,但读数据是不需要加锁的,所以使用一级封锁协议可以解决丢失更新问题,但不能解决不可重复读、读“脏”数据等问题。

(2)二级封锁协议(即PS协议)

二级封锁协议指:在一级封锁协议的基础上,再加上事务T在读取数据A之前必须先对其加S锁,读完后释放S锁。(即修改之前加X锁,直到事务结束才释放X锁。锁不到,等待。读数据之前加S锁,读完后就释放S锁。锁不到,就等待。)

二级封锁协议除解决丢失更新问题外,还可以解决读“脏”数据问题。

修改A的值之后写回磁盘。这时事务T2在时间点6时请求在A上加S锁,因为T1已在A上加了X锁,T2不能加S锁,所以T2只能等待。T1在时间点8时撤销了刚在的修改操作,此时A的值恢复为50,T1释放A上的X锁。在时间点10时T2获得A上的S锁,读取A值为50。这就避免T2读“脏”数据。

由于二级封锁协议中读完数据后即释放S锁,所以不能解决“不可重复读”问题。

3.三级封锁协议(即PSC协议)

三级封锁协议指:一级封锁协议基础上,再加上事务T在读取数据A之前必须先对其加S锁,直到事务结束才释放锁。(即修改数据之前加X锁,直到事务结束才释放X锁。锁不到,等待。读数据之前加S锁,直到事务结束才释放S锁。锁不到,等待。)

4.并发调度的可串行性

安排事务的执行次序的过程称为调度。对多个事务的调度方式分为串行调度和并行调度两种。串行调度即一次只执行一个事务,当该事务执行完后才执行其他事务。并行调度即利用分时的方法同时执行多个事务,利用封锁技术保证并发操作时数据的一致性。

对于事务集(T1,T2……,Tn),如果一个并发调度的结果与某一个串行调度的执行结果等价,则称此并发调度是可串行化的调度。对于某事务集的一个并发调度结果如果与任一串行调度均不等价,则该调度是不可串行化调度。

只有可串行化的并行操作是正确的,而不可串行化的将破坏数据的一致性(可串行化是并行事务正确性的准则。表8-18给出了两个事务的三种不同的调度策略。

5.封锁的粒度

封锁对象的大小称为封锁粒度。封锁对象可以是数据库、表、数据页、行、列等单元。封锁粒度与系统的并发度和并发控制的开销密切相关。一般情况,封锁的粒度越大,数据库所能封锁的数据单元就越小,并发度越小,系统开销也越小;反之,封锁粒度越小,数据库并发度越大,系统开销也越大。

如果封锁的粒度是行,则事务T1和T2可同时对表L中不同的行加锁,事务间不需要等待,并发度就高。如果事务要读取整个表L,而封锁粒度是行,则必须对表中每一行加锁,显然系统开销很大。

一个系统中应支持多种封锁粒度供不同事务选择。如果需要处理大量元组的事务应该以表为封锁粒度。如果只处理少量元组的事务应该以元组为封锁粒度。

8.3.2 Oracle系统中并发控制技术

8.3.2.1 Oracle的封锁方式

Oracle给表加锁有两种方式:手工封锁和自动封锁。

1.自动封锁

当某一会话对表进行插入、更新、删除操作时,系统会自动分配一个行独占锁(RX锁),并且当插入、更新、删除操作完成并结束事务时,系统会自动释放锁资源。此时允许其他用户获得锁资源。当某一会话执行SELECT FOR UPDATE语句时,系统自动分配一个行共享锁(RS锁),当事务结束时,系统自动释放锁资源。

2.手工封锁

除了RX锁可以自动获得,行共享锁可以通过SELECT FOR UPDATE命令自动获得外,其他的共享锁(S锁)、独占锁(X)都只能通过LOCK TABLE语句获得。通过LOCK TABLE语句还可以获得行共享锁(RS锁)、行独占锁(RX锁)。使用LOCK TABLE语句可以锁定整个表,防止其他会话在表上完成DML操作。LOCK语句的语法如下:

LOCK TABLE[用户名,]表名IN

{SHARE|ROW SHARE|EXCLUSIVE|ROW EXCLUSIVE}

MODE[NOWAIT];

锁定可以采用共享模式或独占模式。共享模式可以防止其他会话获得独占锁,但允许其他会话获得一个共享锁。独占模式既可防止其他会话获得共享锁,又可防止其他会话获得独占锁。例如:

LOCK TABLE scott.emp IN EXCLUSIVE MODE;

对数据进行更新时需要对更新的行加独占锁,可以通过LOCK TABLE语句加独占锁,也可以由系统自动加独占锁。当更新操作完成后,应该释放独占锁。如果是通过LOCK TABLE语句加的独占锁,必须用UNLOCK语句显式地释放锁,否则会增加死锁的概率。如果是系统自动加的独占锁,则事务结束时,系统自动释放独占锁,所以最好采用自动封锁。

8.3.2.2 Oracle封锁类型

Oracle使用DML锁来管理并发操作,即管理多个会话同时修改相同数据的操作。Oracle既使用表级锁,又使用行级锁。行级锁总是独占性的,而表级锁既可以是共享的,也可以是独占性的。共享锁会防止出现其他独占性锁,但允许其他共享锁。独占性锁既会防止出现其他独占性锁,也防止出现其他共享锁。但是任何DML锁都不阻挡读取访问。为了更改数据,Oracle 必须在要进行更改的行中采用独占锁。INSERT、UPDATE、DELETE和SELECT FOR UPDATE语句都会隐式加上必要的行级锁。

(1)行共享锁(RS)

行共享锁是通过一个SELECT FOR UPDATE语句隐式获得的,或者通过一条LOCK TABLE IN ROW SHARE MODE语句显式获得的。行共享锁并不防止对数据进行更新,但可以防止其他会话获得独占锁。

(2)行独占锁(RX)

行独占锁是通过一条INSERT、UPDATE、DELETE语句隐式获得的,或者通过一条LOCK TABLE IN ROW EXCLUSIVE MODE语句显式获得的。独占锁可以防止其他会话获得一个共享锁、独占锁。

(3)共享锁(S)

共享锁是通过一条LOCK TABLE IN SHARE MODE语句显式获得的。共享锁可以防止其他会话获得行独占锁、独占锁,但允许其他会话在表上加共享锁和行共享锁。

(4)独占锁(X)

独占锁是通过一条LOCK TABLE IN EXCLUSIVE MODE语句显式获得的。独占锁可以防止其他会话获得对该表的任何共享锁和独占锁。其他会话只能从被独占锁锁定的表中进行选择操作。

在时间点202时,alan用户执行LOCK TABLE语句,希望获得emp表的X锁,此时显示一个错误,告诉用户资源忙,没有获得X锁,等待molly的会话释放锁资源。

在时间点203时,molly用户执行COMMIT操作结束事务,释放emp表的RX锁。此时alan用户马上获得emp表的X锁。

在时间点204时,molly用户执行UPDATE语句,需要获得RX锁,由于alan用户获得了emp表的X锁,阻止molly用户获得RX锁,所以molly用户等待alan会话释放锁。

在时间点205时,alan用户执行ROLLBACK操作结束事务,释放emp表的X锁。此时molly用户马上获得RX锁,完成更新操作。然后molly用户执行COMMIT操作结束事务,并释放emp表的RX锁。

在时间点206时,molly用户执行LOCK TABLE语句获得emp表的RS锁。

在时间点207时,alan用户执行INSERT INTO 语句,隐式获得emp表的RX锁。因为某一用户给表加了RS锁,并不阻止其他用户给表加RX锁。所以alan用户的INSERT操作允许。

在时间点208时,alan用户执行COMMIT操作结束事务,并释放emp表的RX锁。

在时间点209时,molly用户执行LOCK TABLE语句获得emp表的S锁。

在时间点210时,alan用户执行INSERT INTO 语句,需要隐式获得emp表的RX锁。因为molly用户已给表加了S锁,阻止其他用户再给emp表加RX锁,所以处于等待状态。

在时间点211时,molly用户执行COMMIT操作结束事务,并释放锁资源。

在时间点212时,alan用户执行COMMIT操作结束事务,并释放锁资源。

8.3.2.3 Oracle死锁检测机制

当两个事务都加了锁,并且各自等待着对方会话所锁定的一个资源时,就会出现死锁。

8.4 数据库的完整性控制

数据库的完整性是指数据的正确性和相容性,可以防止不符合语义的数据输入和输出所造成的无效操作和错误结果。DBMS必须提供一种机制来检查数据库中的数据是否满足语义规定的条件,保证数据库中的数据是正确的。这种语义规定的条件称为数据库完整性约束条件。

8.4.1 完整性约束的概念

语义规定的条件称为数据库完整性约束条件,完整性约束条件是DBMS完整性子系统的核心。完整性子系统的主要功能如下。

定义功能:提供定义完整性约束的机制。

检查功能:检查用户发出的操作请求是否违反完整性约束。

处理功能:如有违反现象,则采取一定的动作来保证数据的完整性,如拒绝操作或改正错误等方法来处理。

8.4.1.1 SQL完整性约束的类型

在关系数据库系统中完整性约束分为实体完整性、参照完整性和用户定义的完整性。在SQL语言中,根据约束所完成的功能不同,表达完整性约束的规则有主键约束、外键约束、属性约束等几类。

1.主键约束

主键约束是数据库中最重要的一种约束。主键约束体现了实体完整性。要求某一列的值既不能为空,也不能重复。

例8-52 创建包含主键约束的表。

SQL>CREATE TABLE student(sno char(8)PRIMARY KEY,/*主键约束*/

sname char(20)NOT NULL,/*非空约束*/

*** char(1),

age int,

dept char(15))

2.外键约束

外键约束体现参照完整性。外键的取值或者为空,或者参考父表的主键。

在实际应用中,作为主键的关系称为父表(或被参照表)。作为外键的关系称为子表(或参照表)。

例8-53 sc表中sno和cno是主键。sno和cno分别是外键,参照student表和course表的sno和cno列。

SQL>CREATE TABLE sc(sno char(5),

cno char(8),

grade smallint,

PRIMARY KEY(sno,cno),/*主键约束*/

FOREIGN KEY(sno)references student(sno),/*外键约束*/

FOREIGN KEY(cno)references course(cno),/*外键约束*/

CHECK((grade is null)or(grade between 0 and 100)))

/*CHECK约束*/

3.属性约束

属性约束体现用户定义的完整性。属性约束主要限制某一属性的取值范围。属性约束可分为以下几类。

非空值约束:要求某一属性的值不允许为空值。例如:sname要求非空。

唯一值约束:要求某一属性的值不允许重复。如果某一属性是主键,则该属性的值不能为空值,而且要唯一。例如:SNO要求唯一。

基于属性的CHECK约束:在属性约束中的CHECK约束可以对一个属性的值加以限制。限制就是给某一列设定的条件,只有满足条件的值才允许插入。例如:要求成绩(GRADE列)要么是空,表示还没有成绩,要么在0到100之间。

8.4.1.2 完整性约束的定义

完整性约束的定义可以用DDL语言在建表时描述,也可以使用“ALTER TABLE 表名ADD CONSTRAINT”命令添加新的约束。当表定义完时,一条完整性约束被系统存放在数据字典中。当用户进行操作时,系统就开始检查,如果发现用户操作违反约束条件,系统处理违反情况,而不用用户处理。

例8-54 在DDL语句中定义完整性约束条件。

SQL>CREATE TABLE student(sno char(8)PRIMARY KEY,

sname char(20)NOT NULL,

*** char(1),

age int,

dept char(15))

当建表时系统自动为主键约束命名,并存入数据字典。

例8-55 sc表加一个约束条件,限制sc表的SNO值必须从student表中获取。

SQL>ALTER TABLE sc ADD CONSTRAINT sccheck

CHECK(sno IN(SELECT sno FROM student))

8.4.1.3 完整性约束的命名

为了便于对约束进行管理和操作,最好给每个约束取个名字。取名的方法是在定义时,在约束前加一个关键字CONSTRAINT和约束名即可,此时用用户自定义的约束名替代系统命名的约束名。

例8-56 为约束条件命名。

SQL>CREATE TABLE student(sno char(8)CONSTRAINT spk PRIMARY KEY,

sname char(20)CONSTRAINT snn NOT NULL,

*** char(1),

age int,

dept char(15))

上述语句给主键取名为SPK,给非空约束取名为SNN。

8.4.1.4 完整性约束的撤销

在关系定义后,可以通过某些语句添加新的约束和撤销约束。“ALTER TABLE 表名ADD CONSTRAINT”命令用于添加新的约束,“ALTER TABLE 表名DROP CONSTRAINT”命令用于撤销已有的约束。当撤销约束时,系统从数据字典中删除该约束的定义。

例8-57 删除student表中的主键约束spk。

SQL>ALTER TABLE student DROP CONSTRAINT spk

8.4.2 Oracle中的完整性约束

8.4.2.1 Oracle中保证完整性的方法

在Oracle系统中保证数据完整性有三种主要的方法:应用代码、数据库触发器和定义完整性约束。

1.应用代码

应用代码可以通过存储过程实现,也可以通过运行在客户端的应用程序实现。存储过程和应用程序的编写参见第6章和第7章。

2.数据库触发器

数据库触发器是一段PL/SQL程序,当一张表上插入数据、删除数据或更新表中某一列时就执行这段程序。通常只有在定义完整性约束条件不能满足复杂的业务规则时才建立数据库触发器。触发器的编写参见第7章。

3.定义完整性约束

完整性约束可以改善性能、不需要编写代码、很灵活,可以根据需要让约束条件生效或失效,完整性的文档都记录在数据字典中。由于完整性约束有上述优点,本节主要介绍完整性约束方法。