梁铭图,新炬网络首席架构师。具有十余年数据库运维、数据库设计、数据治理以及系统规划建设经验,拥有Oracle OCM和ACE Director、Togaf企业架构师(鉴定级)、IBM CATE等认证,曾获dbaplus年度MVP、华为云MVP等荣誉,并参与数据资产管理国家标准的编写工作。在数据库运维管理和架构设计、运维体系规划、数据资产管理方面有深入研究。
背景
Oracle 11g已经进入产品生命周期的尽头,我们的客户中使用Oracle 11g的中古系统还是挺多。为避免出现bug或其他问题时得不到Oracle官方的支持,近年来有大量的系统陆续迁移到19C的环境中。之前,在一些线上的分享中我谈得使用GoldenGate进行迁移,这里我们主要描述另一种常用的方式XTTS(Cross-Platform transport tablespace)。
迁移环境
本次迁移环境如下:
源数据库版本 11.2.0.4
源操作系统 HPUX
目标数据库 19.5
目标操作系统 linux
迁移实施
XTTS 实际上是 TTS 的增强功能。TTS 意为传输表空间,原理是将业务表空间从一个库传到另一个库,以达到数据迁移的目的。XTTS 则可以支持跨平台字节格式、增量进行传输。(跨平台传输表空间仅支持 oracle 11g 或以上)。它的主要实施步骤包括:
1)迁移前检查
检查无效index
select owner, index_name, status from dba_indexeswhere status='UNUSABLE' order by 1,2;select i.owner, i.index_name, p.partition_name, p.statusfrom dba_ind_partitions p,dba_indexes iwhere p.index_name=i.index_name and p.status='UNUSABLE'order by 1,2,3;select i.owner,i.index_name,s.subpartition_name,s.status fromdba_ind_subpartitions s,dba_indexes i wheres.index_name=i.index_name and s.status='UNUSABLE'order by 1,2,3;
检查是否有无效对象
select owner,object_type ,STATUS,count(*)from dba_objectswhere status != 'VALID'group by owner,object_type ,STATUS;select object_name,owner ,last_ddl_time from dba_objects where status='INVALID';
检查是否有外部表
SQL> select distinct owner from DBA_EXTERNAL_TABLES;
IOT表检查
select distinct owner from dba_tables where IOT_TYPE is not null;
检查临时表
SELECT owner,table_name FROM DBA_TABLES WHERE TEMPORARY='Y' AND OWNER IN(用户列表);
物化视图检查
select owner,count(*) from dba_mviews group by owner;
表空间是否自包含
SQL> EXEC sys.dbms_tts.transport_set_check('需要迁移的表空间列表',TRUE);select * from transport_set_violations;
表空间是否加密
select tablespace_name,ENCRYPTED from dba_tablespaces;
表空间字段是否加密
select * from DBA_ENCRYPTED_COLUMNS;
检查表空间是否包含XML Types
select distinct p.tablespace_name from dba_tablespaces p,dba_xml_tables x, dba_users u, all_all_tables t wheret.table_name=x.table_name and t.tablespace_name=p.tablespace_nameand x.owner=u.username;select distinct p.tablespace_namefrom dba_tablespaces p,dba_xml_tab_cols x,dba_users u,all_all_tables twhere t.table_name=x.table_nameand t.tablespace_name=p.tablespace_nameand x.owner=u.username;
2)Xtts配置
在新环境中比对并创建角色
select 'create role '||role ||';' from dba_roles@XTTS_DBLINKminusselect 'create role '||role ||';' from dba_roles;
在新环境中比对并创建用户
select 'create user "'||a.username ||'" identified by values '''||b.password||''' default tablespace USERS '|| 'temporary tablespace '||a.TEMPORARY_TABLESPACE||';'from dba_users@XTTS_DBLINK a,sys.user$@XTTS_DBLINK b,dba_users c,sys.user$ dwhere a.username=b.name anda.username=d.name(+)and a.username = c.username(+) and c.username is nullorder by a.username;
在新环境中创建profile
Select 'create profile '||distinct profile||';' from dba_profiles@XTTS_DBLINK minusselect 'create profile '||distinct profile ||';' from dba_profiles;
生成恢复用户默认表空间和临时表空间的脚本
set pagesize 100set line 200spool user_default.sqlselect 'alter user '||username||' default tablespace '||default_tablespace||' temporary tablespace '||temporary_tablespace||';' from dba_users order by username;spool off
创建非默认临时表空间
select tablespace_name,status,sum(bytes)/1024/1024 temp_size_mb from dba_temp_files group by tablespace_name,STATUS;
新旧环境网络配置文件检查
在新旧环境中对比listener.ora、tnsnames.ora和sqlnet.ora文件,需保持一致。--提前检查,环境搭建过程中应创建待确认
生成为应用用户赋对象权限脚本
set head offspool tts_sys_privs.sql-- Grant Table privilegesselect 'grant '||privilege||' on "'||owner||'"."'||table_name||'" to "'||grantee||'"'||decode(grantable,'YES',' with grant option ')||decode(hierarchy,'YES',' with hierarchy option ')||';'from dba_tab_privs awhere owner in(select namefrom system.logstdby$skip_supportwhere action=0)and grantee in(select usernamefrom dba_userswhere username not in(select namefrom system.logstdby$skip_supportwhere action=0) ) and a.table_name not in (select DIRECTORY_NAME from dba_directories);-- Grant Column privilegesselect 'grant '||privilege||' ('||column_name||') '||' on '||owner||'.'||table_name||' to '||grantee||' '||decode(grantable,'YES','WITH Grant option')||';'from dba_col_privswhere owner in(select namefrom system.logstdby$skip_supportwhere action=0)and grantee in(select usernamefrom dba_userswhere username not in(select namefrom system.logstdby$skip_supportwhere action=0) ) ;--Grant directories privileges.select 'grant '||privilege||' on directory "'||owner||'"."'||table_name||'" to "'||grantee||'"'||decode(grantable,'YES',' with grant option ')||decode(hierarchy,'YES',' with hierarchy option ')||';'from dba_tab_privs awhere owner in(select namefrom system.logstdby$skip_supportwhere action=0)and grantee in(select usernamefrom dba_userswhere username not in(select namefrom system.logstdby$skip_supportwhere action=0) ) and a.table_name in (select DIRECTORY_NAME from dba_directories);select 'GRANT '||PRIVILEGE||' TO '||GRANTEE||';'from dba_sys_privs where grantee in ('SRPT','WEBSW'); 注意修改对应的schemaspool offset head on
数据库字符集的检查
select * from props$ where name like '%CHARACTERSET%';
3)源数据库备份
xtt.properties文件编辑
tablespaces=RCARD_DATA --要迁移的表空间platformid=4src_scratch_location=/oracle/datadump/xtts_dir/sdatasrc_scratch_location=/oracle/datadump/xtts_dir/sdatadest_scratch_location=/oracle/datadump/xtts_dir/sdatadest_datafile_location=/oracle/datadump/xtts_dir/sdatabackupondest=/oracle/datadump/xtts_dir/xcdataparallel=8rollparallel=8desttmpdir=/oracle/datadump/xtts_dirgetfileparallel = 8
xtts全备发起
export TMPDIR==/oracle/datadump/xtts_dir$ORACLE_HOME/perl/bin/perl xttdriver.pl –backupscp res.txt oracle: /oracle/datadump/xtts_dirscp rmanconvert.cmd oracle: /oracle/datadump/xtts_dir
4)目标数据库还原文件
目标库恢复表空间
$ORACLE_HOME/perl/bin/perl xttdriver.pl --restore
5)数据库表空间增量备份恢复
xtts增量备份发起
$ORACLE_HOME/perl/bin/perl xttdriver.pl –backupscp `cat incrbackups.txt` oracle@dest:/oracle/datadump/xtts_dirscp res.txt oracle@dest:/oracle/datadump/xtts_dir
目标库增量同步
$ORACLE_HOME/perl/bin/perl xttdriver.pl --restore
此步骤需求定时反复执行不断同步源、目标两个表空间之间的数据
6)最终增量同步
源库表空间设置成readonly进行最后一次增量备份
alter tablespace TS1 read only;$ORACLE_HOME/perl/bin/perl xttdriver.pl –backupscp 'cat incrbackups.txt' oracle@dest:/dest_scratch_locationscp res.txt oracle@dest:/home/oracle/xtt
目标库恢复
$ORACLE_HOME/perl/bin/perl xttdriver.pl --restore
7)元数据导入
对象元数据导入目标数据库
create directory dpump_tts as '/home/oracle/destination/convert';GRANT READ, WRITE ON DIRECTORY dpump_tts TO system;$ORACLE_HOME/perl/bin/perl xttdriver.pl -ecreate public database link ttslink connect to system identified by <password> using '<tns_to_source>';impdp directory=DATA_PUMP_DIR logfile=tts_imp.log network_link=ttslink \transport_full_check=no \transport_tablespaces=TS1,TS2 \transport_datafiles='+DATA/prod/datafile/ts1.285.771686721', \'+DATA/prod/datafile/ts2.286.771686723', \'+DATA/prod/datafile/ts2.287.771686743'
8)最终检查
表空间设置成读写状态
alter tablespace TS1 read write;
后续检查
--编译无效对象@?/rdbms/admin/utlprp.sql 32--对比无效对象select r.owner, r.object_type, r.remote_cnt Source_Cnt, l.local_cnt Target_Cntfrom ( select owner, object_type, count(owner) remote_cntfrom dba_objects@XTTS_DBLINKwhere owner not in(select namefrom system.logstdby$skip_supportwhere action=0) group by owner, object_type ) r, ( select owner, object_type, count(owner) local_cntfrom dba_objectswhere owner not in(select namefrom system.logstdby$skip_supportwhere action=0) group by owner, object_type ) lwhere l.owner (+) = r.ownerand l.object_type (+) = r.object_typeand nvl(l.local_cnt,-1) != r.remote_cntorder by 1, 3 desc;--检查无效indexselect owner, index_name, status from dba_indexeswhere status='UNUSABLE' order by 1,2;select i.owner, i.index_name, p.partition_name, p.statusfrom dba_ind_partitions p,dba_indexes iwhere p.index_name=i.index_name and p.status='UNUSABLE'order by 1,2,3;select i.owner,i.index_name,s.subpartition_name,s.status fromdba_ind_subpartitions s,dba_indexes i wheres.index_name=i.index_name and s.status='UNUSABLE'order by 1,2,3;--检查新环境同义词,若无,则按以下脚本创建:SELECT 'CREATE PUBLIC SYNONYM '||SYNONYM_NAME||' FOR '||TABLE_OWNER||'.'||TABLE_NAME||';'FROM DBA_SYNONYMS WHERE TABLE_OWNER in ('**','test') AND OWNER = 'PUBLIC';(create_synonym.txt)----对象个数比较注意:验证新旧环境对象个数前,原环境需要清理dba_recyclebin,保证检查对象一致。purge recyclebin;---验证新旧环境segment_type, TMP临时表空间和CLOB索引不一致则默认无异常:【新环境执行】:select r.owner, r.segment_type, r.remote_cnt Source_Cnt, l.local_cnt Target_Cntfrom ( select owner, segment_type, count(owner) remote_cntfrom dba_segments@ttslinkwhere owner not in(select namefrom system.logstdby$skip_supportwhere action=0)AND SEGMENT_NAME NOT LIKE 'BIN%' group by owner, segment_type ) r, ( select owner, segment_type, count(owner) local_cntfrom dba_segmentswhere owner not in(select namefrom system.logstdby$skip_supportwhere action=0) AND SEGMENT_NAME NOT LIKE 'BIN%' group by owner, segment_type ) lwhere l.owner (+) = r.ownerand l.segment_type (+) = r.segment_typeand nvl(l.local_cnt,-1) != r.remote_cntorder by 1, 3 desc---验证新旧环境object_type【新环境执行】:select r.owner, r.object_type, r.remote_cnt Source_Cnt, l.local_cnt Target_Cntfrom ( select owner, object_type, count(owner) remote_cntfrom dba_objects@ttslinkwhere owner not in(select namefrom system.logstdby$skip_supportwhere action=0) group by owner, object_type ) r, ( select owner, object_type, count(owner) local_cntfrom dba_objectswhere owner not in(select namefrom system.logstdby$skip_supportwhere action=0) group by owner, object_type ) lwhere l.owner (+) = r.ownerand l.object_type (+) = r.object_typeand nvl(l.local_cnt,-1) != r.remote_cntorder by 1, 3 desc;
小结
实现跨版本的Oracle数据库迁移,根据应用系统数据库的实际情况分析我们往往会采取不同策略的迁移方案再应付。XTTS是其中一种可选的方案,从实际应用情况来看,它不需要额外的工具软件,同时接近于0停机的数据割接,对于数据量不大且交易不算太多的数据库而言也是一种良好的选择。
如果字段的最大可能长度超过255字节,那么长度值可能…
只能说作者太用心了,优秀
感谢详解
一般干个7-8年(即30岁左右),能做到年入40w-50w;有…
230721