概述

从Oracle 10g开始,数据库支持跨平台的传输表空间。本文主要探讨如何实现 ASM 数据文件和OS文件系统数据文件的传输表空间。
如果目标数据库是一个不同的字节序平台,以下是如何使用可传输表空间迁移一个数据库到一个新的平台的大致步骤:

  1. 在目标平台上创建一个新的,空的数据库。
  2. 从源库导入传输操作要求的对象到目标库。
  3. 从源库为所有的用户表空间导出可传输的元数据。
  4. 转移用户表空间的数据文件到目标系统。
  5. 使用RMAN转换数据文件到目标系统的字节序格式。
  6. 导入所有用户表空间的可传输元数据到目标数据库。
  7. 从源库导入余下的数据库对象和元数据(传输操作未移动的部分)到目标库。

也可以在源平台转换数据文件,转换完成后转移数据文件到目标平台。

迁移方案

受支持的平台

查询V$TRANSPORTABLE_PLATFORM来查看受支持的平台,并确定每个平台的字节序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SQL> COLUMN PLATFORM_NAME FORMAT A32
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

如果源平台和目标平台是不同的字节序,那么必须在源平台或者目标平台上做一个额外的步骤,来转换被传输的表空间到目标格式。如果它们是同样的字节序,那么不需要做转换,表空间可以像相同平台那样传输。

传输表空间

  1. 传输表空间前的准备工作
    a) 检查表空间是自包含的:
1
2
SQL> execute sys.dbms_tts.transport_set_check('TBS1,TBS2', true);
SQL> select * from sys.transport_set_violations;

注意:在表空间被传输之前,这些违反传输标准的问题必须被解决。

b) 要成功的运行传输表空间导出,表空间必须在 READ ONLY 模式:

1
2
SQL> ALTER TABLESPACE TBS1 READ ONLY;
SQL> ALTER TABLESPACE TBS2 READ ONLY;
  1. 导出元数据
    a) 使用传统导出工具:
    1
    exp userid=\'sys/sys as sysdba\' file=tbs_exp.dmp log=tba_exp.log transport_tablespace=y tablespaces=TBS1,TBS2

b) 使用数据泵导出:
首先创建数据泵使用的目录对象,例如:

1
2
CREATE OR REPLACE DIRECTORY dpump_dir AS '/tmp/subdir' ;
GRANT READ,WRITE ON DIRECTORY dpump_dir TO system;

然后初始化数据泵导出:

1
expdp system/password DUMPFILE=expdat.dmp DIRECTORY=dpump_dir TRANSPORT_TABLESPACES = TBS1,TBS2

如果你想要在执行一个传输表空间操作的同时进行严格的包含关系检查,那么使用 TRANSPORT_FULL_CHECK 参数。

1
expdp system/password DUMPFILE=expdat.dmp DIRECTORY = dpump_dir TRANSPORT_TABLESPACES= TBS1,TBS2 TRANSPORT_FULL_CHECK=Y

如果被传输的表空间集不是自包含的,那么导出会失败。

  1. 使用 V$TRANSPORTABLE_PLATFORM 来确定每个平台的字节序,你可以在每个平台实例执行如下查询:
1
2
3
SELECT tp.platform_id,substr(d.PLATFORM_NAME,1,30), ENDIAN_FORMAT
FROM V$TRANSPORTABLE_PLATFORM tp, V$DATABASE d
WHERE tp.PLATFORM_NAME = d.PLATFORM_NAME;

如果你发现字节序是不同的,那么传输表空间集时必须进行转换:

1
2
RMAN> convert tablespace TBS1 to platform="Linux IA (32-bit)" FORMAT '/tmp/%U';
RMAN> convert tablespace TBS2 to platform="Linux IA (32-bit)" FORMAT '/tmp/%U';

然后复制数据文件和导出的文件到目标环境。

  1. 导入可传输表空间

a) 使用传统导入工具:

1
imp userid=\'sys/sys as sysdba\' file=tbs_exp.dmp log=tba_imp.log transport_tablespace=y datafiles='/tmp/....','/tmp/...'

b) 使用数据泵:

1
2
CREATE OR REPLACE DIRECTORY dpump_dir AS '/tmp/subdir';
GRANT READ,WRITE ON DIRECTORY dpump_dir TO system;

然后执行:

1
impdp system/password DUMPFILE=expdat.dmp DIRECTORY=dpump_dir TRANSPORT_DATAFILES='/tmp/....','/tmp/...' REMAP_SCHEMA=(source:target) REMAP_SCHEMA=(source_sch2:target_schema_sch2)

如果你想要改变传输的数据库对象的属主,可以使用 REMAP_SCHEMA。

  1. 将表空间置于 read/write 模式:
1
2
SQL> ALTER TABLESPACE TBS1 READ WRITE;
SQL> ALTER TABLESPACE TBS2 READ WRITE;

2.3 使用 DBMS_FILE_TRANSFER

我们也可以使用DBMS_FILE_TRANSFER来拷贝数据文件到另外一个主机。
从12c和11.2.0.4开始DBMS_FILE_TRANSFER默认的进行转换。若使用 DBMS_FILE_TRANSFER,当目标数据库收到一个来自不同字节序的平台的文件时,它对每一个块进行转换。作为可传输操作的一部分,在数据文件被移动到目标数据库后,不需RMAN转换,即可导入。
在低于 11.2.0.4 的版本上,对于 ASM 文件同样需要执行上面的步骤。但是如果字节序格式不同,那么你必须在转移文件后,使用 RMAN 转换。文件无法直接在不同平台的两个 ASM 实例间进行拷贝。
对于文件系统的范例如下:

1
2
3
4
5
6
7
8
RMAN> CONVERT DATAFILE
'/hq/finance/work/tru/tbs_31.f',
'/hq/finance/work/tru/tbs_32.f',
'/hq/finance/work/tru/tbs_41.f'
TO PLATFORM="Solaris[tm] OE (32-bit)"
FROM PLATFORM="HP TRu64 UNIX"
DB_FILE_NAME_CONVERT= "/hq/finance/work/tru/", "/hq/finance/dbs/tru"
PARALLELISM=5;

对于ASM 磁盘组的范例如下:

1
2
3
4
5
6
7
8
RMAN> CONVERT DATAFILE
'/hq/finance/work/tru/tbs_31.f',
'/hq/finance/work/tru/tbs_32.f',
'/hq/finance/work/tru/tbs_41.f'
TO PLATFORM="Solaris[tm] OE (32-bit)"
FROM PLATFORM="HP TRu64 UNIX"
DB_FILE_NAME_CONVERT="/hq/finance/work/tru/", "+diskgroup"
PARALLELISM=5;

注意!当使用可传输表空间(TTS)从 Solaris,Linux 或者 AIX 迁移到 HP/UX 时,索引组织表(IOT)可能损坏。

2.4 可传输表空间的使用限制
1) 源库和目标库必须使用相同的字符集和国家字符集。
2) 如果目标库上已经有一个同名的表空间,无法进行传输。然而,你可以在传输之前,重命名要传输的表空间或者目标库上的表空间。
3) 若对象带有下层对象(例如物化视图)或者被包含的对象(例如分区表),则无法被传输。除非所有下层对象或者被包含的对象都在这个表空间集里。
4) 如果表空间对象的所有者在目标库中不存在,则需要在开始可传输表空间导入之前,手动的创建用户名。
5) 从 Oracle Database 11gR1 开始,对于含有 XMLType 的表空间,必须使用数据泵来导出和导入表空间元数据。
如下的查询返回了包含 XMLType 的表空间的列表:

1
2
3
4
5
select distinct p.tablespace_name
from dba_tablespaces p, dba_xml_tables x, dba_users u, all_all_tables t
where t.table_name=x.table_name and
t.tablespace_name=p.tablespace_name and
x.owner=u.username;
  1. 高级队列可传输表空间不支持带有多个容器的 8.0 兼容版高级队列。
  2. 无法传输 SYSTEM 表空间或者用户 SYS 拥有的对象。
  3. 不透明类型(例如 RAW,BFILE 和 AnyTypes)可以被传输,但是他们不会在跨平台传输操作中被转换。他们的实际框架只有应用知道,所以应用必须在这些类型被移动到新的平台后处理字节序的问题。
  4. 浮点数 BINARY_FLOAT 和 BINARY_DOUBLE 类型是可以传输的,但必须使用 Data Pump工具,不能使用早期的导出工具 EXP。

2.5 ASM 文件的可传输表空间导出/导入

2.5.1 使用 RMAN CONVERT
没有直接的方法将 ASM 文件作为可传输表空间导出/导入。但是,可以通过 RMAN 实现这个功能。
请务必遵照如下步骤:

  1. 导出表空间前的准备。
    a) 检查表空间是自包含的:
1
2
SQL>execute sys.dbms_tts.transport_set_check('TBS1,TBS2', true);
SQL> select * from sys.transport_set_violations;

注意:这些违反限制的结果必须在表空间传输前解决。
b) 要成功的进行可传输表空间导出,这些表空间必须处于 READ ONLY 模式。

1
2
SQL> ALTER TABLESPACE TBS1 READ ONLY;
SQL> ALTER TABLESPACE TBS2 READ ONLY;
  1. 导出元数据。
    a) 使用原始导出工具:
1
exp userid=\'sys/sys as sysdba\' file=tbs_exp.dmp log=tba_exp.log transport_tablespace=y tablespaces=TBS1,TBS2

b) 使用数据泵导出:

1
2
CREATE OR REPLACE DIRECTORY dpump_dir AS '/tmp/subdir';
GRANT READ,WRITE ON DIRECTORY dpump_dir TO system;

然后执行:

1
expdp system/password DUMPFILE=expdat.dmp DIRECTORY=dpump_dir TRANSPORT_TABLESPACES = TBS1,TBS2

如果你想要在执行可传输表空间操作同时进行严格的包容性检查,那么使用 TRANSPORT_FULL_CHECK 参数:

1
expdp system/password DUMPFILE=expdat.dmp DIRECTORY = dpump_dir TRANSPORT_TABLESPACES= TBS1,TBS2 TRANSPORT_FULL_CHECK=Y

如果传输的表空间不是自包含的,那么导出会出错。

  1. 使用 V$TRANSPORTABLE_PLATFORM 找到目标库准确的平台名。你可以在目标平台实例上执行如下的查询。
1
2
3
SELECT tp.platform_id,substr(d.PLATFORM_NAME,2,30), ENDIAN_FORMAT
FROM V$TRANSPORTABLE_PLATFORM tp, V$DATABASE d
WHERE tp.PLATFORM_NAME = d.PLATFORM_NAME;
  1. 以目标平台的格式,从 ASM 文件生成一个 OS 文件。
1
2
3
4
RMAN> CONVERT TABLESPACE TBS1
TO PLATFORM 'HP-UX (64-bit)' FORMAT '/tmp/%U';
RMAN> CONVERT TABLESPACE TBS2
TO PLATFORM 'HP-UX (64-bit)' FORMAT '/tmp/%U';
  1. 拷贝生成的文件到目标服务器(如果跟源服务器不是同一台机器)。
  2. 导入可传输表空间。
    a) 使用原始的导入工具:
1
imp userid=\'sys/sys as sysdba\' file=tbs_exp.dmp log=tba_imp.log transport_tablespace=y datafiles='/tmp/....','/tmp/...'

b) 使用数据泵导入:

1
2
CREATE OR REPLACE DIRECTORY dpump_dir AS '/tmp/subdir';
GRANT READ,WRITE ON DIRECTORY dpump_dir TO system;

然后执行:

1
impdp system/password DUMPFILE=expdat.dmp DIRECTORY=dpump_dir TRANSPORT_DATAFILES='/tmp/....','/tmp/...' REMAP_SCHEMA=(source:target) REMAP_SCHEMA=(source_sch2:target_schema_sch2)

如果你想要改变传输的数据库对象的属主的话,可以使用 REMAP_SCHEMA 参数。

  1. 将表空间放置在 read/write 模式。
1
2
SQL> ALTER TABLESPACE TBS1 READ WRITE;
SQL> ALTER TABLESPACE TBS2 READ WRITE;

如果你想要将数据文件从 ASM 环境传输到文件系统,那么操作到此结束。但如果你想要在两个 ASM 环境之间传输表空间,那么你要继续下面的操作。

  1. 使用 rman 拷贝文件’/tmp/….dbf’ 到 ASM 环境。
1
2
rman nocatalog target /
RMAN> backup as copy datafile '/tmp/....dbf' format '+DGROUPA';

这里 +DGROUPA 是 ASM 磁盘组名字。

  1. 将数据文件交换到这个拷贝。
    如果是 10g 数据库,首先要将数据文件离线:
1
SQL> alter database datafile '/tmp/....dbf' offline;

文件交换到这个拷贝:

1
2
rman nocatalog target /
RMAN> switch datafile '/tmp/....dbf' to copy;

记下在 +DGROUPA 磁盘组中创建的拷贝的名字,例如,’+DGROUPA/s101/datafile/tts.270.5’。
10) 使文件重新在线,我们首先要 recover 它。

1
2
SQL> recover datafile '+DGROUPA/s101/datafile/tts.270.5';
SQL> alter database datafile '+DGROUPA/s101/datafile/tts.270.5' online;
  1. 检查数据文件确实已经是 ASM 环境的一部分,并且在线。
1
SQL> select name, status from v$datafile;

输出应该是:

1
+DGROUPA/s101/datafile/tts.270.5 ONLINE

2.5.2 使用DBMS_FILE_TRANSFER
我们同样可以使用 DBMS_FILE_TRANSFER 来将数据文件从一个 ASM 磁盘组拷贝到另外一个,甚至到另外一个主机上。从 10gR2 开始,我们同样可以使用 DBMS_FILE_TRANSFER 来从 ASM 拷贝数据文件到文件系统,以及从文件系统到 ASM。
PUT_FILE 过程读取一个本地文件或者 ASM 并且联系远端数据库来创建一个在远端文件系统的拷贝。被拷贝的文件是源文件,拷贝带来的新文件是目标文件。直到过程成功完成,目标文件都不会被关闭。
语法:

1
2
3
4
5
6
7
DBMS_FILE_TRANSFER.PUT_FILE(
source_directory_object IN VARCHAR2,
source_file_name IN VARCHAR2,
destination_directory_object IN VARCHAR2,
destination_file_name IN VARCHAR2,
destination_database IN VARCHAR2);

其中:
 source_directory_object: 在本地源端拷贝的文件所在的目录对象。在源端,这个目录对象必须存在。
 source_file_name: 从本地文件系统拷贝的文件的名字。这个文件必须存在于本地文件系统上 source_directory_object 所指定的目录里。
 destination_directory_object: 这是在目标端文件所要放置的目录对象。这个目录对象必须存在于远端文件系统。
 destination_file_name: 放在远端文件系统的文件的名字。在远端文件系统目标目录中必须没有重名的文件。
 destination_database: 指向作为拷贝文件的目的地的远端数据库的数据库链接的名字。
如果我们想要使用 DBMS_FILE_TRANSFER.PUT_FILE 来从源端传输文件到目的地主机,步骤3,4,5做如下修改:

  1. 在目标数据库主机创建一个目录,授权给本地用户。这是文件要在目标端放置的目录对象,必须在远端的文件系统存在。
    CREATE OR REPLACE DIRECTORY target_dir AS ‘+DGROUPA’;
    GRANT WRITE ON DIRECTORY target_dir TO “USER”;

  2. 在源数据库主机创建一个目录。这是要拷贝的文件在本地源端所存在的目录对象。这个目录对象必须在源端存在。
    CREATE OR REPLACE DIRECTORY source_dir AS ‘+DGROUPS/subdir’;
    GRANT READ,WRITE ON DIRECTORY source_dir TO “USER”;
    CREATE OR REPLACE DIRECTORY source_dir_1 AS ‘+DGROUPS/subdir/subdir_2’;

  3. 创建一个 dblink 连接到目标数据库主机:
    CREATE DATABASE LINK DBS2 CONNECT TO ‘user’ IDENTIFIED BY ‘password’ USING ‘target_connect’;
    这里 target_connect 是目标数据库的连接字符串,USER 是我们将要用来转移数据文件的用户。

  4. 连接到源实例。会用到如下项目:
     dbs1: 到源数据库的连接字符串
     dbs2: 到目标数据库的 dblink
     a1.dat: 源数据库的文件名
     a4.dat: 目标数据库的文件名

1
2
3
4
5
6
7
8
9
CONNECT user/password@dbs1
-- - put a1.dat to a4.dat (using dbs2 dblink)
-- - level 2 sub dir to parent dir
-- - user has read privs on source_dir_1 at dbs1 and write on target_dir
-- - in dbs2
BEGIN
DBMS_FILE_TRANSFER.PUT_FILE('source_dir_1', 'a1.dat',
'target_dir', 'a4.dat', 'dbs2' );
END;