本文梗概:




正文:




由于公司服务器升级,原Oracle 9i RAC(裸设备)系统也升级到了Oracle 10g RAC(ASM),原数据库中的数据也要导入到新建的数据库中。



数据迁移要求:




环境

原系统:Sun Slaris 8.0(UltraSPARC_64)

Oracle 9i R2 RAC


新系统:RHEL 5.5(x86_64)

Oracle 10g R2 RAC

要求

全数据量迁移(约300G)

原系统数据库不可下线


Oracle的数据迁移有很多种方法:RMan的备份/恢复方式、EXP/IMP、DB Link + extent等。


RMan:


由于系统环境的差异(SPARC -> x86)和数据库版本的问题,RMAN不予考虑。



EXP/IMP


最开始使用此方式,实际上在TOAD中提供的exp/imp工具非常好用,基本无视数据库的编码格式和版本,图形化的界面,可以实时看到导入、导出的进度,可以导出数据库结构,但由于原数据库的某些表数据量较大,在导出时提示exp kgefec: fatal error 0,基本每表可以导出6G数据后,就会报这个错误。这个错误我想应该是与数据库有关,应该不是TOAD的问题。总之,数据部分导出和没导是一样的,所以该方式也被放弃了。



DB Link + extent


DB Link实际上是尝试的第一种方式,建立这种模式的DB Link请参考如下代码:




CREATE DATABASE LINK RC CONNECT TO IDENTIFIED BY <PWD> USING 
'(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 10.1.0.62)(PORT = 1521))) (CONNECT_DATA = (SERVICE_NAME = RAC)))';



在建立好DB Link后,使用Insert into select ....


INSERT INTO tb SELECT * FROM tb@rc


问题大了!在第一试导入数据量在1.8G的表时,新数据库的UNDO表空间开始狂飙,最后占用到了6.5G,其间又手工为UNDO建立了一个新的数据文件,可是最后用不上了,删除也删除不了。


上网找方法,发现可以使用INSERT /**APPEND**/来避免UNDO激增的问题


INSERT /**APPEND**/ INTO tb SELECT * FROM tb@rc


但发现如果表的数据量很大的话,这一过程非常慢,一个7G左右的表,大概用时5个小时。后来发现原表中是建立了extent的,于想到了按区块导,多个进程并行。


注意:在导入前最好删除或禁止新表中的KEY和INDEX,这样导入会快得多。



操作:


1、在新数据库上建立一个ROW_ID表



create table MY_ROWID
(
ID NUMBER,
ROWID_MIN VARCHAR2(100),
ROWID_MAX VARCHAR2(100),
HAS_DEAL NUMBER
);



2、在原数据库上建立一个DB Link,主要用于向新数据库的ROW_ID表提供目标表数据区块列表


3、导入目标表的区块列表,在原数据库执行



insert into my_rowid@RC(id,rowid_min,rowid_max,has_deal)
select rownum,
DBMS_ROWID.ROWID_CREATE(1,o.data_object_id,e.RELATIVE_FNO,e.BLOCK_ID,0),
DBMS_ROWID.ROWID_CREATE(1,o.data_object_id,e.RELATIVE_FNO,e.BLOCK_ID+e.BLOCKS-1,10000),
0
from dba_extents e,dba_objects o
where e.segment_name=upper('base_table')
and e.owner='CSDN'
AND o.object_name = upper('base_table')
AND o.owner='CSDN';



4、在新数据库建立存储过程,用于多个进程同时调用。




CREATE OR REPLACE PROCEDURE .SP_XF_COPY_TABLE(N NUMBER) IS V_SQLERRM VARCHAR2(200); 

BEGIN FOR C IN (
    SELECT ID, ROWID_MIN, ROWID_MAX FROM MY_ROWID WHERE HAS_DEAL = 0 AND MOD(ID, 10) = N) LOOP --注意这里的10,是该过程被要调用次数 
    INSERT /*+APPEND */ INTO 表1 SELECT * FROM 表1@rc t WHERE ROWID >= CHARTOROWID(C.ROWID_MIN) AND ROWID <= CHARTOROWID(C.ROWID_MAX); 
    UPDATE MY_ROWID SET HAS_DEAL = 1 WHERE ID = C.ID;
    COMMIT; 
END LOOP; 
COMMIT; 

EXCEPTION WHEN OTHERS THEN V_SQLERRM := SUBSTR(SQLERRM, 1, 200);
    DBMS_OUTPUT.PUT_LINE(V_SQLERRM); 
    ROLLBACK; 
END SP_XF_COPY_TABLE; /




注:这个过程是在CSDN找的,写的非常精妙(我修改了一下,在其中也加入了/*+APPEND */),如果调用过程中,被用户不小心被取消了,也可以重新调用继续导入,我就犯了这个错误,由于是termial到一台windows上,再toad+sqlplus到数据库服务器,由于是半夜了,所以去看了MESSI VS CR9,中场回来发现笔记本长时间没有操作进入了休眠,登陆的termial过期了,调用该过程的sqlplus也被杀了,杯催的同时,还有更杯催的,我一开始没有想太清楚,上去就truncate了新表的所有数据,回头仔细看了看过程,发现完全可以接着调用。在此向此过程的作者致意,并提醒和我一样的菜鸟们,林子大着呢......




5、调用过程,开了10个SQLPLUS

set serveroutput on;--打开输出,这样可以看到错误
call SP_XF_COPY_TABLE(0);
call SP_XF_COPY_TABLE(1);
call SP_XF_COPY_TABLE(2);
......
call SP_XF_COPY_TABLE(9);




这里的调用是在10个窗口中同时进行了,回头看看表空间,数据表空间在飞快增长着,而UNDO基本没有变化,其间出几个调用出现了



ORA-02049: timeout: distributed transaction waiting for lock



网上也没找到太好的答案,应该是我反反复复重启的问题导致锁了表,于是关闭了出错的过程,只同时调用4个没有发现问题,4个做完再做接下来的4个、2个,BINGO,37G的表大概用了3个小时。



因为300G的数据主要集中在十几个表中,可以建立个多个ROW_ID表,所以可以把马力开到最大。如果数据是分布在更多的表中,每表数据也不多,我还是建议用EXP/IMP。总之,恼人的新数据库建立基本搞定了,测试的工作还在等待着我,革命还在继续。