1.3 使用模板创建数据库
前面提到,除了定制数据库之外,还可以使用模板来创建数据库,接下来就让我们一起来了解一下使用模板创建数据库的过程。
1.3.1 启动创建
在1.1节中我们提到可以通过命令行启动DBCA工具,可能更多的朋友是通过“开始”菜单中Oracle创建的快捷项里来启动DBCA的,如图1-20所示,右图则是快捷方式的具体指向:
图1-20 DBCA 及其 快捷方式
注意到这个快捷项的目标执行的是以下命令:
C:\Oracle\10.2.0\BIN\launch.exe C:\oracle\10.2.0\assistants\dbca dbca.cl
此处的dbca.cl文件和前面的dbca.bat批处理文件执行的功能是一致的:
Command=("C:\oracle\10.2.0\jdk\jre\BIN\JAVA" -Dsun.java2d.font.DisableAlgorithmicStyles=true
-DORACLE_HOME="C:\oracle\10.2.0" -DJDBC_PROTOCOL=thin
-mx128m –classpath …..OraInstaller.jar" oracle.sysman.assistants.dbca.Dbca)
那么DBCA为什么指向这个目录呢?这个目录又是做什么用的呢?
实际上这个目录是Oracle的缺省模板目录,当使用模板来创建数据库时,就用到了这个目录下的文件。
1.3.2 数据库创建模板
下面来看一下使用模板创建数据库的过程。
使用模板和前面的过程主要不同之处在于第二个步骤,在这里选择“定制数据库”之外的选项,就都使用了模板,并且包含了数据文件(eygle模板是我们之前保存的),如图1-22左图所示;使用模板创建数据库通常速度都会很快,原因就在于数据文件是从种子数据库中恢复出来的,而不需要创建文件及字典对象等信息,右图展示创建过程的第一个步骤就是“复制数据库文件”:
图1-22 选择模板
这里通过脚本说明一下通过模板创建数据库和定制数据库的不同。
首先eygle.sql脚本记录如下内容:
……
host C:\oracle\10.2.0\bin\orapwd.exe
file=C:\oracle\10.2.0\database\PWDeygle.ora password=&&sysPassword force=y
@C:\oracle\admin\eygle\scripts\CloneRmanRestore.sql
@C:\oracle\admin\eygle\scripts\cloneDBCreation.sql
@C:\oracle\admin\eygle\scripts\postScripts.sql
host "echo SPFILE='C:\oracle\10.2.0/dbs/spfileeygle.ora'
> C:\oracle\10.2.0\database\initeygle.ora"
@C:\oracle\admin\eygle\scripts\postDBCreation.sql
该脚本首先调用的是CloneRmanRestore.sql脚本,该脚本记录如下内容:
C:\Oracle\admin\eygle\scripts>type CloneRmanRestore.sql
connect "SYS"/"&&sysPassword" as SYSDBA
set echo on
spool C:\oracle\admin\eygle\scripts\CloneRmanRestore.log
startup nomount pfile="C:\oracle\admin\eygle\scripts\init.ora";
@C:\oracle\admin\eygle\scripts\rmanRestoreDatafiles.sql;
这个脚本首先启动实例到Nomount模式,然后调用rmanRestoreDatafiles.sql来恢复文件。
1.3.3 Rman的引入
rmanRestoreDatafiles.sql脚本是通过系统包dbms_backup_restore来恢复备份集中的文件,从而实现数据恢复,其主要内容如下:
set echo off;
set serveroutput on;
select TO_CHAR(systimestamp,'YYYYMMDD HH:MI:SS') from dual;
variable devicename varchar2(255);
declare
omfname varchar2(512) := NULL;
done boolean;
begin
dbms_output.put_line(' ');
dbms_output.put_line(' Allocating device.... ');
dbms_output.put_line(' Specifying datafiles... ');
:devicename := dbms_backup_restore.deviceAllocate;
dbms_output.put_line(' Specifing datafiles... ');
dbms_backup_restore.restoreSetDataFile;
dbms_backup_restore.restoreDataFileTo(1,
'C:\oracle\oradata\eygle\SYSTEM01.DBF', 0, 'SYSTEM');
dbms_backup_restore.restoreDataFileTo(2,
'C:\oracle\oradata\eygle\UNDOTBS01.DBF', 0, 'UNDOTBS1');
dbms_backup_restore.restoreDataFileTo(3,
'C:\oracle\oradata\eygle\SYSAUX01.DBF', 0, 'SYSAUX');
dbms_backup_restore.restoreDataFileTo(4,
'C:\oracle\oradata\eygle\USERS01.DBF', 0, 'USERS');
dbms_output.put_line(' Restoring ... ');
dbms_backup_restore.restoreBackupPiece(
'C:\oracle\10.2.0\assistants\dbca\templates\Seed_Database.dfb', done); --种子数据库
if done then
dbms_output.put_line(' Restore done.');
else
dbms_output.put_line(' ORA-XXXX: Restore failed ');
end if;
dbms_backup_restore.deviceDeallocate;
end;
/
select TO_CHAR(systimestamp,'YYYYMMDD HH:MI:SS') from dual;
关于RMAN的有关知识,我们会在后面的章节详细介绍,但是关于dbms_backup_restore包这里有必要提前介绍一下。
当通过RMAN进行数据库备份时,RMAN会将多个数据文件写出到一个或多个备份文件(称为备份集)中,RMAN的相关的备份信息或者存储在控制文件中,或者存储在RMAN的专用目录数据库(Catalog)中,如果RMAN的备份信息丢失,那么通常备份集中的文件是没有办法读取出来的,其他工具无法识别RMAN的备份集文件;而dbms_backup_restore就是针对这种情况提供的一种解决方案,dbms_backup_restore可以在数据库nomount状态下调用,直接从备份集中读取数据文件,功能十分强大。
DBMS_BACKUP_RESTORE包由dbmsbkrs.sql和prvtbkrs.plb这两个脚本创建,创建数据库时执行的catproc.sql 脚本会调用这两个脚本以创建包,这些脚本文件可以在$ORACLE_HOME/rdbms/admin目录下找到,脚本文件中对包的内容有详细的介绍。
下面通过具体的例子来介绍一下这个工具的用法,以下是一次真实的恢复案例,由于控制文件丢失,只能通过DBMS_BACKUP_RESTORE包从备份集中恢复数据文件,当然恢复之前我们需要知道一些数据库的相关信息,了解备份集中包含了哪些文件。
首先启动数据库到nomount状态:
[oracle@jumper conner]$ sqlplus "/ as sysdba"
Connected to an idle instance.
SQL> startup nomount;
ORACLE instance started.
然后可以执行脚本,将数据文件恢复到指定目录:
SQL> DECLARE
2 devtype varchar2(256);
3 done boolean;
4 BEGIN
5 devtype:=sys.dbms_backup_restore.deviceAllocate (type=>'',ident=>'t1');
6 sys.dbms_backup_restore.restoreSetDatafile;
7 sys.dbms_backup_restore.restoreDatafileTo(dfnumber=>01,
toname=>'/opt/oracle/oradata/conner/system01.dbf');
8 sys.dbms_backup_restore.restoreDatafileTo(dfnumber=>02,
toname=>'/opt/oracle/oradata/conner/undotbs01.dbf');
9 sys.dbms_backup_restore.restoreDatafileTo(dfnumber=>03,
toname=>'/opt/oracle/oradata/conner/users01.dbf');
10 sys.dbms_backup_restore.restoreBackupPiece(done=>done,
handle=>'/opt/oracle/product/9.2.0/dbs/0ggmiabq_1_1', params=>null);
11 sys.dbms_backup_restore.deviceDeallocate;
12 END;
13 /
PL/SQL procedure successfully completed.
至此,从备份集中读取文件完毕,但是由于没有控制文件,就需要重建一个控制文件用于恢复,创建控制文件的脚本可以自己根据经验编写,也可以根据备份的文本进行修改,当然也可以从其他数据库中转储一个控制文件脚本,仿照改写。
正常情况下,可以通过如下的命令将控制文件的创建语句转储到跟踪文件中(位于udump目录中):
SQL> alter database backup controlfile to trace;
Database altered.
可以找到trace文件,编辑、执行重建控制文件的需要部分:
SQL> startup nomount;
ORACLE instance started.
SQL> set echo on
SQL> @ctl.sql
SQL> CREATE CONTROLFILE REUSE DATABASE "CONNER" RESETLOGS ARCHIVELOG
2 -- SET STANDBY TO MAXIMIZE PERFORMANCE
3 MAXLOGFILES 5
4 MAXLOGMEMBERS 3
5 MAXDATAFILES 100
6 MAXINSTANCES 1
7 MAXLOGHISTORY 1361
8 LOGFILE
9 GROUP 1 '/opt/oracle/oradata/conner/redo01.log' SIZE 10M,
10 GROUP 2 '/opt/oracle/oradata/conner/redo02.log' SIZE 10M,
11 GROUP 3 '/opt/oracle/oradata/conner/redo03.log' SIZE 10M
12 -- STANDBY LOGFILE
13 DATAFILE
14 '/opt/oracle/oradata/conner/system01.dbf',
15 '/opt/oracle/oradata/conner/undotbs01.dbf',
16 '/opt/oracle/oradata/conner/users01.dbf'
17 CHARACTER SET ZHS16GBK
18 ;
Control file created.
如果存在部分归档日志,创建控制文件之后可以执行恢复:
SQL> recover database using backup controlfile until cancel;
ORA-00279: change 240560269 generated at 06/09/2005 17:33:48 needed for thread 1
ORA-00289: suggestion : /oradata/conner/archive/1_7.dbf
ORA-00280: change 240560269 for thread 1 is in sequence #7
Specify log: {=suggested | filename | AUTO | CANCEL}
AUTO
ORA-00279: change 240600632 generated at 06/10/2005 10:42:26 needed for thread 1
ORA-00289: suggestion : /oradata/conner/archive/1_8.dbf
ORA-00280: change 240600632 for thread 1 is in sequence #8
ORA-00278: log file '/oradata/conner/archive/1_7.dbf' no longer needed for this recovery
Specify log: {=suggested | filename | AUTO | CANCEL}
AUTO
ORA-00279: change 240620884 generated at 06/10/2005 10:45:42 needed for thread 1
ORA-00289: suggestion : /oradata/conner/archive/1_9.dbf
ORA-00280: change 240620884 for thread 1 is in sequence #9
ORA-00278: log file '/oradata/conner/archive/1_8.dbf' no longer needed for this recovery
ORA-00283: recovery session canceled due to errors
ORA-00600: internal error code, arguments: [3020], [4242465], [1], [9], [314], [272], [], []
ORA-10567: Redo is inconsistent with data block (file# 1, block# 48161)
ORA-10564: tablespace SYSTEM
ORA-01110: data file 1: '/opt/oracle/oradata/conner/system01.dbf'
ORA-10560: block type 'DATA SEGMENT HEADER - UNLIMITED'
ORA-01112: media recovery not started
SQL> recover database using backup controlfile until cancel;
ORA-00279: change 240620949 generated at 06/10/2005 10:45:44 needed for thread 1
ORA-00289: suggestion : /oradata/conner/archive/1_9.dbf
ORA-00280: change 240620949 for thread 1 is in sequence #9
Specify log: {=suggested | filename | AUTO | CANCEL}
cancel
Media recovery cancelled.
恢复到最后可用日志后,通过resetlogs方式打开数据库:
SQL> alter database open resetlogs;
Database altered.
SQL> select name from v$datafile;
NAME
------------------------------------------------------------
/opt/oracle/oradata/conner/system01.dbf
/opt/oracle/oradata/conner/undotbs01.dbf
/opt/oracle/oradata/conner/users01.dbf
至此恢复完成。这是一次常规恢复,dbms_backup_restore的功能远不止于此,还可以通过该包恢复备份集中的控制文件、归档日志等文件等。
继续前面的讨论,rmanRestoreDatafiles.sql脚本通过dbms_backup_restore包从种子文件Seed_Database.dfb恢复出数据文件,来看一下模板目录中存放的模板和种子数据库(自定义的模板也存放在这个目录中):
C:\Oracle\admin\eygle\scripts>dir C:\oracle\10.2.0\assistants\dbca\templates
2005-08-30 17:31 5,893 Data_Warehouse.dbc
2005-09-07 13:02 983,040 example.dmp
2005-09-07 13:02 20,897,792 example01.dfb
2005-08-30 17:31 5,770 General_Purpose.dbc
2005-05-16 15:49 12,411 New_Database.dbt
2005-09-07 13:02 7,061,504 Seed_Database.ctl
2005-09-07 13:02 95,543,296 Seed_Database.dfb
2005-08-30 17:31 5,829 Transaction_Processing.dbc
Seed_Database.dfb文件就是包含种子文件的一个备份集。
1.3.4 克隆数据库
数据文件具备了,接下来是通过这些文件“克隆”一个数据库,这个工作由cloneDBCreation.sql脚本继续执行,这个脚本更为复杂,下面分开介绍一下。
首先根据指定的数据库名称(测试数据库指定的名称为eygle)创建一个控制文件:
connect "SYS"/"&&sysPassword" as SYSDBA
set echo on
spool C:\oracle\admin\eygle\scripts\cloneDBCreation.log
Create controlfile reuse set database "eygle"
MAXINSTANCES 8 MAXLOGHISTORY 1 MAXLOGFILES 16 MAXLOGMEMBERS 3 MAXDATAFILES 100
Datafile
'C:\oracle\oradata\eygle\SYSTEM01.DBF',
'C:\oracle\oradata\eygle\UNDOTBS01.DBF',
'C:\oracle\oradata\eygle\SYSAUX01.DBF',
'C:\oracle\oradata\eygle\USERS01.DBF'
LOGFILE GROUP 1 ('C:\oracle/oradata/eygle/redo01.log') SIZE 51200K,
GROUP 2 ('C:\oracle/oradata/eygle/redo02.log') SIZE 51200K,
GROUP 3 ('C:\oracle/oradata/eygle/redo03.log') SIZE 51200K RESETLOGS;
然后通过dbms_backup_restore包清空dbid等信息:
exec dbms_backup_restore.zerodbid(0);
看到这里再次使用到了dbms_backup_restore包,zeroDbid是包中的一个过程,用于清空数据文件头的部分信息,新的dbid在之后的控制文件创建时可以被计算,对于数据库克隆,这是必须的。
zeroDbid有一个输入参数,即文件号:
PROCEDURE zeroDbid(fno IN binary_integer);
当fno==0时,控制文件中包含的所有数据文件头都将被清零,zeroDbid主要用于清除数据文件头的3类信息:Database id信息、Checksum信息和Checksum符号位信息。
继续看这个脚本,清零完成之后,数据库重新启动,控制文件被重新创建,此时新的dbid被计算并最终写入所有数据文件:
shutdown immediate;
startup nomount pfile="C:\oracle\admin\eygle\scripts\initeygleTemp.ora";
Create controlfile reuse set database "eygle"
MAXINSTANCES 8 MAXLOGHISTORY 1 MAXLOGFILES 16 MAXLOGMEMBERS 3 MAXDATAFILES 100
Datafile
'C:\oracle\oradata\eygle\SYSTEM01.DBF',
'C:\oracle\oradata\eygle\UNDOTBS01.DBF',
'C:\oracle\oradata\eygle\SYSAUX01.DBF',
'C:\oracle\oradata\eygle\USERS01.DBF'
LOGFILE GROUP 1 ('C:\oracle/oradata/eygle/redo01.log') SIZE 51200K,
GROUP 2 ('C:\oracle/oradata/eygle/redo02.log') SIZE 51200K,
GROUP 3 ('C:\oracle/oradata/eygle/redo03.log') SIZE 51200K RESETLOGS;
注意,在启动数据库时Oracle使用了一个临时的参数文件initeygleTemp.ora,在这个参数文件的最后一行设置了一个内部参数:
C:\Oracle\admin\eygle.t\scripts>tail -1 initeygleTemp.ora
_no_recovery_through_resetlogs=true
_no_recovery_through_resetlogs这个参数的作用是什么呢?可以从数据库中找到一点说明:
SQL> SELECT x.ksppinm NAME, y.ksppstvl VALUE, x.ksppdesc describ
2 FROM SYS.x$ksppi x, SYS.x$ksppcv y
3 WHERE x.inst_id = USERENV ('Instance')
4 AND y.inst_id = USERENV ('Instance')
5 AND x.indx = y.indx AND x.ksppinm LIKE '%&par%';
Enter value for par: no_reco
NAME VALUE DESCRIB
------------------------------ ----- ----------------------------------------------
_no_recovery_through_resetlogs FALSE no recovery through this resetlogs operation
这个参数用于限制恢复能否跨越resetlogs,对于数据库的恢复来说,resetlogs通常意味着不完全恢复,在数据库resetlogs打开之后,控制文件中的很多信息被改写,在Oracle 10g之前,如果数据库resetlogs打开,那么将不再能够通过当前的控制文件再次进行resetlogs点之前的恢复,而Oracle 10g改变了这个历史。
在Oracle 10g中,即使通过resetlogs方式打开了数据库,Oracle仍然支持再次从resetlogs时间点之前进行恢复;在Clone数据库时,Oracle设置这个参数为True,意思就是不允许再次进行跨越resetlogs时间点的恢复。关于这部分内容,我们将在后面章节进行更为详细的介绍。
继续解读这个脚本,接下来Oracle设置restricted session模式,resetlogs打开数据库:
alter system enable restricted session;
alter database "eygle" open resetlogs;
修改global_name,添加临时文件等:
alter database rename global_name to "eygle";
ALTER TABLESPACE TEMP ADD TEMPFILE 'C:\oracle\oradata\eygle\TEMP01.DBF' SIZE 20480K REUSE AUTOEXTEND ON NEXT 640K MAXSIZE UNLIMITED;
select tablespace_name from dba_tablespaces where tablespace_name='USERS';
select sid, program, serial#, username from v$session;
由于种子数据库的字符集通常与用户要求的不符,接下来Oracle通过内部操作强制更改了字符集、国家字符集(这个内容在后面的章节有详细的介绍):
alter database character set INTERNAL_CONVERT ZHS16GBK;
alter database national character set INTERNAL_CONVERT AL16UTF16;
最后修改用户口令,禁用restricted session模式,这个克隆过程执行完毕:
alter user sys identified by "&&sysPassword";
alter user system identified by "&&systemPassword";
alter system disable restricted session;
至此,种子数据库已经按照用户的意图脱胎换骨得以重生。
1.3.5传输表空间技术
在很多Oracle文档中,可能大家都注意过Oracle用来进行测试的一个表空间,这个表空间中有一系列预置的用户和数据,可以用于数据库或BI的很多测试实验。
这个表空间在使用模板建库时是可以选择的,在如图1-22所示的这个界面中,可以选择建库时包含这个范例表空间(缺省是未选择的)。
图1-22 是否包含示例方案
如果选择了包含示例方案,则cloneDBCreation.sql脚本将会有所改变,主要增加了如下语句:
connect "SYS"/"&&sysPassword" as SYSDBA
@C:\oracle\10.2.0\demo\schema\mkplug.sql &&sysPassword change_on_install change_on_install
change_on_install change_on_install change_on_install change_on_install C:\oracle\10.2.0\assistants\dbca\templates\example.dmp C:\oracle\10.2.0\assistants\dbca\templates\example01.dfb C:\oracle\oradata\eygle\example01.dbf C:\oracle\admin\eygle\scripts\ "'SYS/&&sysPassword as SYSDBA'";
看到这里,再次引用了模板目录中的文件:
C:\>dir C:\oracle\10.2.0\assistants\dbca\templates\ex*
2005-09-07 13:02 983,040 example.dmp
2005-09-07 13:02 20,897,792 example01.dfb
通过mkplug.sql脚本来加载这个范例表空间,来看一下这个脚本的主要内容。
同样,最重要的是通过dbms_backup_restore包从example01.dfb文件中恢复数据文件:
SELECT TO_CHAR(systimestamp, 'YYYYMMDD HH:MI:SS') FROM dual;
variable new_datafile varchar2(512)
declare
done boolean;
v_db_create_file_dest VARCHAR2(512);
devicename varchar2(255);
data_file_id number;
rec_id number;
stamp number;
resetlogs_change number;
creation_change number;
checkpoint_change number;
blksize number;
omfname varchar2(512);
real_file_name varchar2(512);
begin
dbms_output.put_line(' ');
dbms_output.put_line(' Allocating device.... ');
dbms_output.put_line(' Specifying datafiles... ');
devicename := dbms_backup_restore.deviceAllocate;
dbms_output.put_line(' Specifing datafiles... ');
SELECT MAX(file_id)+1 INTO data_file_id FROM dba_data_files;
SELECT value INTO v_db_create_file_dest FROM v$parameter WHERE name ='db_create_file_dest';
IF v_db_create_file_dest IS NOT NULL
THEN
dbms_backup_restore.restoreSetDataFile;
dbms_backup_restore.getOMFFileName('EXAMPLE',omfname);
dbms_backup_restore.restoreDataFileTo(data_file_id, omfname, 0,'EXAMPLE');
ELSE
dbms_backup_restore.restoreSetDataFile;
dbms_backup_restore.restoreDataFileTo(data_file_id,'&data_file_name');
END IF;
dbms_output.put_line(' Restoring ... ');
dbms_backup_restore.restoreBackupPiece('&data_file_backup', done);
SELECT max(recid) INTO rec_id FROM v$datafile_copy;
-- Now get the real file name. It could be also OMF filename
SELECT name, stamp, resetlogs_change#, creation_change#, checkpoint_change#,block_size
INTO real_file_name, stamp,resetlogs_change, creation_change, checkpoint_change, blksize
FROM V$DATAFILE_COPY
WHERE recid = rec_id and file# = data_file_id;
-- Uncatalog the file from V$DATAFILE_COPY. This important.
dbms_backup_restore.deleteDataFileCopy(recid => rec_id,
stamp => stamp,
fname => real_file_name,
dfnumber => data_file_id,
resetlogs_change => resetlogs_change,
creation_change => creation_change,
checkpoint_change => checkpoint_change,
blksize => blksize,
no_delete => 1,
force => 1);
-- Set the bindvariable to the real filename
:new_datafile := real_file_name;
if done then
dbms_output.put_line(' Restore done.');
else
dbms_output.put_line(' ORA-XXXX: Restore failed ');
end if;
end;
/
这个恢复完成之后,接下来最重要的部分就是通过传输表空间技术将example表空间导入到当前的数据库。
考虑一下这种情况,当进行跨数据库迁移时,需要将一个用户表空间中的数据迁移到另外一个数据库,应该使用什么样的方法呢?
最常规的做法可能是通过EXP工具将数据全部导出,然后在目标数据库上IMP导入,可是这种方法可能会比较缓慢。EXP工具同时还提供另外一种技术-可传输表空间技术,可以用于加快这个过程。
在exp –help的帮助中,可以看到这样一个参数:
TRANSPORT_TABLESPACE 导出可传输的表空间元数据 (N)
通过这个选项,我们可以对一组自包含、只读的表空间只导出元数据,然后在操作系统层将这些表空间的数据文件拷贝至目标平台,并将元数据导入数据字典(这个过程称为插入,plugging),即完成迁移。
但是注意,传输表空间技术不能应用于SYSTEM表空间或SYS用户拥有的对象。
对于可传输表空间有一个重要概念:自包含(Self-Contained)。
在表空间传输的中,要求表空间集为自包含的,自包含表示用于传输的内部表空间集没有引用指向外部表空间集。自包含分为两种:一般自包含表空间集和完全(严格)自包含表空间集。
常见的以下情况是违反自包含原则的:
¡ 索引在内部表空间集,而表在外部表空间集(相反地,如果表在内部表空间集,而索引在外部表空间集,则不违反自包含原则)。
¡ 分区表一部分区在内部表空间集,一部分在外部表空间集(对于分区表,要么全部包含在内部表空间集中,要么全不包含)。
¡ 如果在传输表空间时同时传输约束,则对于引用完整性约束,约束指向的表在外部表空间集,则违反自包含约束;如果不传输约束,则与约束指向无关。
¡ 表在内部表空间集,而lob列在外部表空间集,则违反自包含约束。
通常可以通过系统包DBMS_TTS来检查表空间是否自包含,验证可以以两种方式执行:非严格方式和严格方式。
以下是一个简单的验证过程,假定在eygle表空间存在一个表eygle,其上存在索引存储在USERS表空间:
SQL> create table eygle as select rownum id ,username from dba_users;
SQL> create index ind_id on eygle(id) tablespace users;
以SYS用户执行非严格自包含检查(full_check=false):
SQL> connect / as sysdba
SQL> exec dbms_tts.transport_set_check('EYGLE', TRUE);
PL/SQL procedure successfully completed.
SQL> SELECT * FROM TRANSPORT_SET_VIOLATIONS;
no rows selected
执行严格自包含检查(full_check=true):
SQL> exec dbms_tts.transport_set_check('EYGLE', TRUE, True);
PL/SQL procedure successfully completed.
SQL> SELECT * FROM TRANSPORT_SET_VIOLATIONS;
VIOLATIONS
--------------------------------------------------------------------------------------
Index EYGLE.IND_ID in tablespace USERS points to table EYGLE.EYGLE in tablespace EYGLE
反过来对于USERS表空间来说,非严格检查也是无法通过的:
SQL> exec dbms_tts.transport_set_check('USERS', TRUE);
PL/SQL procedure successfully completed.
SQL> SELECT * FROM TRANSPORT_SET_VIOLATIONS;
VIOLATIONS
----------------------------------------------------------------------------------------
Index EYGLE.IND_ID in tablespace USERS points to table EYGLE.EYGLE in tablespace EYGLE
但是可以对多个表空间同时传输,则一些自包含问题就可以得到解决:
SQL> exec dbms_tts.transport_set_check('USERS,EYGLE', TRUE, True);
PL/SQL procedure successfully completed.
SQL> SELECT * FROM TRANSPORT_SET_VIOLATIONS;
no rows selected
表空间自包含确认之后,进行表空间传输就很方便了,一般包含如下几个步骤。
(1)将表空间设置为只读:
alter tablespace users read only;
(2)导出表空间。在操作系统提示符下执行:
exp username/passwd tablespaces=users transport_tablespace=y file=exp_users.dmp
此处的导出文件只包含元数据,所以导出文件很小,导出速度也会很快。
(3)转移。将导出的元数据文件(此处是exp_users.dmp)和传输表空间的数据文件(此处是users表空间的数据文件user01.dbf)转移至目标主机(转移过程如果使用FTP方式,应该注意使用二进制方式)。
(4)传输。在目标数据库将表空间插入到数据库中,完成表空间传输。在操作系统命令提示符下执行下面的语句:
imp username/passwd tablespaces=users transport_tablespace=y file=exp_users.dmp datafiles='users01.dbf'
了解了Oracle的可传输表空间技术后,来看一下example表空间的插入,以下脚本仍然来自mkplug.sql脚本:
--
-- Importing the metadata and plugging in the tablespace at the same
-- time, using the restored database file
--
DEFINE imp_logfile = &log_path.tts_example_imp.log
-- When importing use filename got after restore is finished
host imp "'sys/&&password_sys AS SYSDBA'" transport_tablespace=y file=&imp_file log=&imp_logfile datafiles='&datafile' tablespaces=EXAMPLE tts_owners=hr,oe,pm,ix,sh
完成plugging之后,这个表空间就被包含在了新建的数据库之中。
1.3.6跨平台表空间传输
需要注意的是,在Oracle 10g之前,数据文件是不能够跨平台传输使用的,从Oracle 10g开始,Oracle支持跨平台的表空间传输,这极大地增强了数据迁移的便利性。
1. 字节顺序和平台
数据文件所以不能跨平台,主要是由于不同平台的字节顺序不同,这是计算机领域由来已久的问题之一,在各种计算机体系结构中,由于对于字、字节等的存储机制有所不同,通信双方交流的信息单元(比特、字节、字、双字等)应该以什么样的顺序进行传送就成了一个问题,如果不达成一致的规则,通信双方将无法进行正确的编/译码从而导致通信失败。
目前在各种体系的计算机中通常采用的字节存储机制主要有两种:Big-Endian和Little-Endian 。
一些操作系统(包括Windows)在低位内存地址中存放二进制数据的最低有效字节,因此这种系统被称为Little Endian;一些操作系统(包括Solaris)将最高有效字节存储在低位内存地址中,因此这种系统被称为Big Endian。
举一个简单点的例子,假如1122这样一个数据要存入不同系统,对于Little Endian的系统,存储的顺序就是2211,小头在前;而对于Big Endian的系统来说,存储顺序就是1122,大头在前,显然Big Endian更符合我们通常的语言习惯。
那么跨平台的问题就出现了,当一个Little Endian的系统试图从一个Big Endian的系统中读取数据时,就需要通过转换,否则不同的字节顺序将导致数据不能被正确读取。
说明:据考证,Endian这个词来源于Jonathan Swift在1726年写的讽刺小说《Gulliver's Travels》(《格利佛游记》)。该小说在描述Gulliver畅游小人国时碰到了如下的一个场景。在小人国里的小人因为非常小(身高6英寸)所以总是碰到一些意想不到的问题。有一次因为对水煮蛋该从大的一端(Big-End)剥开还是小的一端(Little-End)剥开的争论而引发了一场战争,并形成了两支截然对立的队伍:支持从Big-End剥开的人Swift就称作Big-Endians,而支持从Little-End剥开的人就称作Little-Endians(后缀ian表明的就是支持某种观点的人)。Endian这个词由此而来。
清楚了这个问题,接下来就可以来看看Oracle是如何处理这种情况的。
2. 源平台和目标平台
首先在迁移之前,需要确认一下源平台和目标平台的平台信息,这些信息可以通过视图v$transportable_platform和v$database视图联合查询得到。
以下是源平台的信息:
SQL> col PLATFORM_NAME for a30
SQL> SELECT d.platform_name, endian_format
2 FROM v$transportable_platform tp, v$database d
3 WHERE tp.platform_name = d.platform_name;
PLATFORM_NAME ENDIAN_FORMAT
------------------------------ --------------
Solaris[tm] OE (64-bit) Big
查询目标数据库平台信息:
SQL> col platform_name for a40
SQL> SELECT d.platform_name, endian_format
2 FROM v$transportable_platform tp, v$database d
3 WHERE tp.platform_name = d.platform_name;
PLATFORM_NAME ENDIAN_FORMAT
---------------------------------------- --------------
Microsoft Windows IA (32-bit) Little
看到Windows平台和Solaris平台的字节顺序是不同的,Windows是Little-Endian,而Solaris是Big-Endian的。
可以通过数据库查询Oracle 10g支持的平台转换:
SQL> col PLATFORM_NAME for a40
SQL> select * from v$transportable_platform;
PLATFORM_ID PLATFORM_NAME ENDIAN_FORMAT
----------- ---------------------------------------- --------------
1 Solaris[tm] OE (32-bit) Big
2 Solaris[tm] OE (64-bit) Big
7 Microsoft Windows IA (32-bit) Little
10 Linux IA (32-bit) Little
6 AIX-Based Systems (64-bit) Big
3 HP-UX (64-bit) Big
5 HP Tru64 UNIX Little
4 HP-UX IA (64-bit) Big
11 Linux IA (64-bit) Little
15 HP Open VMS Little
8 Microsoft Windows IA (64-bit) Little
9 IBM zSeries Based Linux Big
13 Linux 64-bit for AMD Little
16 Apple Mac OS Big
12 Microsoft Windows 64-bit for AMD Little
17 Solaris Operating System (x86) Little
18 IBM Power Based Linux Big
17 rows selected.
3. 源平台的导出及转换
接下来开始我们的测试,创建一个独立的自包含表空间,并创建一个测试表:
SQL> create tablespace trans
2 datafile '/data2/ora10g/oradata/mars/trans.dbf' size 10M;
SQL> create user trans identified by trans default tablespace trans;
SQL> grant connect,resource to trans;
SQL> connect trans/trans
SQL> create table test as select * from dict;
SQL> select count(*) from test;
COUNT(*)
----------
617
将表空间设置为只读:
SQL> connect / as sysdba
Connected.
SQL> alter tablespace trans read only;
Tablespace altered.
导出要传输的表空间:
$ exp \'/ as sysdba\' tablespaces=trans transport_tablespace=y file=exp_trans.dmp
Export: Release 10.2.0.1.0 - Production on Thu Mar 22 16:31:15 2007
Copyright (c) 1982, 2005, Oracle. All rights reserved.
Connected to: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit Production
With the Partitioning, OLAP and Data Mining options
Export done in ZHS16GBK character set and AL16UTF16 NCHAR character set
Note: table data (rows) will not be exported
About to export transportable tablespace metadata...
For tablespace TRANS ...
. exporting cluster definitions
. exporting table definitions
. . exporting table TEST
. exporting referential integrity constraints
. exporting triggers
. end transportable tablespace metadata export
Export terminated successfully without warnings.
使用RMAN的convert命令转换文件格式:
$ rman target /
Recovery Manager: Release 10.2.0.1.0 - Production on Thu Mar 22 16:34:30 2007
Copyright (c) 1982, 2005, Oracle. All rights reserved.
connected to target database: MARS (DBID=1034439893)
RMAN> convert tablespace trans
2> to platform 'Microsoft Windows IA (32-bit)'
3> format '/tmp/%N_%f';
Starting backup at 22-MAR-07
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: sid=140 devtype=DISK
channel ORA_DISK_1: starting datafile conversion
input datafile fno=00005 name=/data2/ora10g/oradata/mars/trans.dbf
converted datafile=/tmp/TRANS_5
channel ORA_DISK_1: datafile conversion complete, elapsed time: 00:00:01
Finished backup at 22-MAR-07
确认导出文件已生成:
$ ls -l /tmp/TRANS*
-rw-r----- 1 oracle dba 10493952 Mar 22 16:37 /tmp/TRANS_5
3. 文件传输
通过FTP获得两个文件,注意应该使用二进制方式传输(bin模式):
D:\oradata\EYGLE\DATAFILE>ftp 172.16.33.50
Connected to 172.16.33.50.
220 testdbserver.hurray.com.cn FTP server (SunOS 5.8) ready.
User (172.16.33.50:(none)): gqgai
331 Password required for gqgai.
Password:
230 User gqgai logged in.
ftp> bin
200 Type set to I.
ftp> mget /export/home/oracle/exp_trans.dmp
200 Type set to I.
mget /export/home/oracle/exp_trans.dmp? y
200 PORT command successful.
150 Binary data connection for /export/home/oracle/exp_trans.dmp (172.16.34.89,5006) (3072 bytes).
226 Binary Transfer complete.
ftp: 收到 3072 字节,用时 0.00Seconds 3072000.00Kbytes/sec.
ftp> mget /tmp/TRANS_5
200 Type set to I.
mget /tmp/TRANS_5? y
200 PORT command successful.
150 Binary data connection for /tmp/TRANS_5 (172.16.34.89,5008) (10493952 bytes).
226 Binary Transfer complete.
ftp: 收到 10493952 字节,用时 1.13Seconds 9270.28Kbytes/sec.
4. 目标数据库的导入
在目标数据库中,也可以使用RMAN对备份文件进行转换,以使数据文件具有更规范的名称:
D:\oradata\EYGLE\DATAFILE>rman target /
恢复管理器: Release 10.2.0.1.0 - Production on 星期四 3月 22 17:18:50 2007
Copyright (c) 1982, 2005, Oracle. All rights reserved.
连接到目标数据库: EYGLE (DBID=1417824532)
RMAN> convert datafile 'D:\oradata\EYGLE\DATAFILE\TRANS_5'
2> db_file_name_convert
3> 'D:\oradata\EYGLE\DATAFILE\TRANS_5','D:\oradata\EYGLE\DATAFILE\TRANS01.DBF';
启动 backup 于 22-3月 -07
使用目标数据库控制文件替代恢复目录
分配的通道: ORA_DISK_1
通道 ORA_DISK_1: sid=144 devtype=DISK
通道 ORA_DISK_1: 启动数据文件转换
输出文件名=D:\ORADATA\EYGLE\DATAFILE\TRANS_5
已转换的数据文件 = D:\ORADATA\EYGLE\DATAFILE\TRANS01.DBF
通道 ORA_DISK_1: 数据文件转换完毕, 经过时间: 00:00:08
完成 backup 于 22-3月 -07
然后需要在目标数据库创建相应的用户:
SQL> create user trans identified by trans;
用户已创建。
SQL> grant connect,resource to trans;
授权成功。
接下来可以执行导入:
D:\oradata\EYGLE\DATAFILE>imp '/ as sysdba' tablespaces=trans transport_tablespace=y file=exp_trans.dmp datafiles=D:\oradata\EYGLE\DATAFILE\TRANS01.DBF
Import: Release 10.2.0.1.0 - Production on 星期四 3月 22 17:34:27 2007
Copyright (c) 1982, 2005, Oracle. All rights reserved.
连接到: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options
经由常规路径由 EXPORT:V10.02.01 创建的导出文件
即将导入可传输的表空间元数据...
已经完成 ZHS16GBK 字符集和 AL16UTF16 NCHAR 字符集中的导入
. 正在将 SYS 的对象导入到 SYS
. 正在将 SYS 的对象导入到 SYS
. 正在将 TRANS 的对象导入到 TRANS
. . 正在导入表 "TEST"
. 正在将 SYS 的对象导入到 SYS
成功终止导入, 没有出现警告。
注意:此处也可以在IMP时通过fromuser/touser参数将数据导入其他用户下。
现在这个表空间已经被插入到新的数据库中,并且数据全部传输过来:
SQL> select name from v$datafile where name like ‘%TRANS%’;
NAME
------------------------------------------------------------
D:\ORADATA\EYGLE\DATAFILE\TRANS01.DBF
SQL> select count(*) from trans.test;
COUNT(*)
----------
617
导入后的表空间还处于read only状态,确认后可以更改为读写模式:
SQL> select tablespace_name,status from dba_tablespaces;
TABLESPACE_NAME STATUS
------------------------------ ---------
SYSTEM ONLINE
UNDOTBS1 ONLINE
SYSAUX ONLINE
TEMP ONLINE
USERS ONLINE
EYGLE ONLINE
TRANS READ ONLY
已选择7行。
SQL> alter tablespace trans read write;
表空间已更改。
同样,传输表空间也可以通过数据泵来完成,以下是Oracle 10gR1中插入表空间的简单示例:
E:\Oracle\oradata\eygle\dpdata>impdp eygle/eygle dumpfile=trans.dmp directory=dpdata transport_datafiles='E:\Oracle\oradata\eygle\EYGLE\DATAFILE\TRANS01.DBF'
Import: Release 10.1.0.2.0 - Production on 星期二, 27 4月, 2004 15:03
Copyright (c) 2003, Oracle. All rights reserved.
连接到: Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
已成功加载/卸载了主表 "EYGLE"."SYS_IMPORT_TRANSPORTABLE_01"
启动 "EYGLE"."SYS_IMPORT_TRANSPORTABLE_01": eygle/******** dumpfile=trans.dmp directory=dpdata transport_datafiles='E:\
Oracle\oradata\eygle\EYGLE\DATAFILE\TRANS01.DBF'
处理对象类型 TRANSPORTABLE_EXPORT/PLUGTS_BLK
处理对象类型 TRANSPORTABLE_EXPORT/TABLE
处理对象类型 TRANSPORTABLE_EXPORT/TTE_POSTINST/PLUGTS_BLK
作业 "EYGLE"."SYS_IMPORT_TRANSPORTABLE_01" 已于 15:03 成功完成
5. 同字节序文件的跨平台
前面说过,当一个Little Endian的系统试图从一个Big Endian的系统中读取数据时,就需要通过转换,否则不同的字节顺序将导致数据不能被正确读取。那么另外一个问题出现了,如果字节序相同的平台进行文件交互,数据能否被正确读取呢?
理论上的确是可以的,但是由于在不同的平台上操作系统会在数据文件头写上系统信息,这部分信息无法跨越平台,所以仍然会导致跨平台的文件无法被数据库正确识别(Oracle10g中同字节序平台数据文件头不再存在跨平台的迁移问题)。
接下来让我们通过Windows和Linux平台来进行一个跨平台测试,相信通过这个测试可以对以上提出的问题作出一个很好的回答。
实验环境:Windows XP + Oracle10g 10.2.0.1:
SQL> select * from v$version where rownum <2;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
Red Hat Enterprise Linux AS release 3 + Oracle 9iR2 9.2.0.4
SQL> select * from v$version where rownum <2;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
看一下Linux平台,文件头被操作系统保留了8192字节:
SQL> select file_name,bytes from dba_data_files
2 where tablespace_name='USERS';
FILE_NAME BYTES
----------------------------------- ----------
/opt/oracle/oradata/eygle/users.dbf 10485760
SQL> !
[oracle@jumper eygle]$ ll users.dbf
-rw-r----- 1 oracle dba 10493952 Mar 23 10:14 users.dbf
[oracle@jumper eygle]$ exit
exit
SQL> select 10493952 -10485760 diff from dual;
DIFF
----------
8192
Windows平台上数据文件头同样保留了8192字节:
SQL> select file_name,bytes from dba_data_files
2 where tablespace_name='USERS';
FILE_NAME BYTES
--------------------------------------------------- ----------
D:\ORADATA\EYGLE\DATAFILE\O1_MF_USERS_2G8OJYYS_.DBF 5242880
SQL> host dir D:\ORADATA\EYGLE\DATAFILE\O1_MF_USERS_2G8OJYYS_.DBF
2007-03-22 17:41 5,251,072 O1_MF_USERS_2G8OJYYS_.DBF
SQL> select 5251072 -5242880 diff from dual;
DIFF
----------
8192
可以通过Linux和Windows平台来进行一个小测试实验,这两个平台都是Little Endian的系统:
SQL> select * from v$transportable_platform;
PLATFORM_ID PLATFORM_NAME ENDIAN_FORMAT
----------- ---------------------------------------- --------------
1 Solaris[tm] OE (32-bit) Big
2 Solaris[tm] OE (64-bit) Big
7 Microsoft Windows IA (32-bit) Little
10 Linux IA (32-bit) Little