在前段时间,进行过一次expdp/impdp的逻辑迁移。但是也没有进行过非常详细的总结。这一次,终于想要把整个迁移过程和遇到的问题、解决方式都从头到尾的总结一下,用来以后给以后的迁移过程做个参考模板。
一、明确迁移目标:
本次迁移是通过expdp/impdp的方式,将业务用户数据用expdp导出到NFS上,然后再通过impdp导入到新数据库中。
二、主机存储操作
1.挂载NFS
将存储划分到其中1个节点,并制作为文件系统,然后将该文件系统通过NFS配置共享出去:
vi /etc/exports #NFS server share directories /share XXX.XXX.XXX.XXX/24(rw,insecure,no_root_squash,sync) /share XXX.XXX.XXX.XXX/24(rw,insecure,no_root_squash,sync) /share XXX.XXX.XXX.XXX/24(rw,insecure,no_root_squash,sync)
复制
注:需要注意,这里将NFS的属性挂在为insecure,否则在导入的时候,可能会报错。
三、源端数据库调研:
1.确认需要迁移的用户
select username from dba_users where username not in ('ANONYMOUS','DBSNMP','EXFSYS','MDDATA','MGMT_VIEW','ORDPLUGINS','OUTLN','SCOTT','SI_INFORMATN_SCHEMA','SYSMAN','WK_TEST','WKPROXY','WKSYS','XDB','APPQOSSYS','AUDSYS','CTXSYS','DVSYS','GGSYS','LBACSYS','MDSYS','OJVMSYS','OLAPSYS','ORDSYS','SYS','SYS$UMF','SYSBACKUP','SYSDG','SYSKM','SYSRAC','SYSTEM','WMSYS','XS$NULL','DBSFWUSER','SI_INFORMTN_SCHEMA','DVF','GSMADMIN_INTERNAL','ORDDATA','GSMCATUSER','REMOTE_SCHEDULER_AGENT','PDBADMIN','GSMUSER','DIP','ORACLE_OCM','SPATIAL_CSW_ADMIN_USR','APEX_040200','APEX_PUBLIC_USER','SPATIAL_WFS_ADMIN_USR','user_impdp','FLOWS_FILES','APEX_050000') and owner not like '%APEX%';
复制
2.查看是否有非系统表在系统表空间下
set line 999 col owner format a10 col segment_name format a30 col tablespace_name format a30 select owner,segment_name,segment_type,tablespace_name from dba_segments where owner not in ('ANONYMOUS','DBSNMP','EXFSYS','MDDATA','MGMT_VIEW','ORDPLUGINS','OUTLN','SCOTT','SI_INFORMATN_SCHEMA','SYSMAN','WK_TEST','WKPROXY','WKSYS','XDB','APPQOSSYS','AUDSYS','CTXSYS','DVSYS','GGSYS','LBACSYS','MDSYS','OJVMSYS','OLAPSYS','ORDSYS','SYS','SYS$UMF','SYSBACKUP','SYSDG','SYSKM','SYSRAC','SYSTEM','WMSYS','XS$NULL','DBSFWUSER','SI_INFORMTN_SCHEMA','DVF','GSMADMIN_INTERNAL','ORDDATA','GSMCATUSER','REMOTE_SCHEDULER_AGENT','PDBADMIN','GSMUSER','DIP','ORACLE_OCM','SPATIAL_CSW_ADMIN_USR','APEX_040200','APEX_PUBLIC_USER','SPATIAL_WFS_ADMIN_USR','user_impdp','FLOWS_FILES','APEX_050000') and owner not like '%APEX%';
复制
注:如果存在业务数据存放在系统表空间下的,则需要先对这些表进行move操作,将表移出系统表空间,因为我们在迁移的时候是不会迁移系统表空间的,只能迁移业务表空间。
3.查看相应的表空间
select tablespace_name,sum(bytes)/1024/1024/1024 gb from dba_data_files where tablespace_name in (select distinct tablespace_name from dba_segments where owner not in ('ANONYMOUS','DBSNMP','EXFSYS','MDDATA','MGMT_VIEW','ORDPLUGINS','OUTLN','SCOTT','SI_INFORMATN_SCHEMA','SYSMAN','WK_TEST','WKPROXY','WKSYS','XDB','APPQOSSYS','AUDSYS','CTXSYS','DVSYS','GGSYS','LBACSYS','MDSYS','OJVMSYS','OLAPSYS','ORDSYS','SYS','SYS$UMF','SYSBACKUP','SYSDG','SYSKM','SYSRAC','SYSTEM','WMSYS','XS$NULL','DBSFWUSER','SI_INFORMTN_SCHEMA','DVF','GSMADMIN_INTERNAL','ORDDATA','GSMCATUSER','REMOTE_SCHEDULER_AGENT','PDBADMIN','GSMUSER','DIP','ORACLE_OCM','SPATIAL_CSW_ADMIN_USR','APEX_040200','APEX_PUBLIC_USER','SPATIAL_WFS_ADMIN_USR','user_impdp','FLOWS_FILES','APEX_050000') and owner not like '%APEX%'group by tablespace_name;
复制
四.创建表空间
1.根据业务实际要求,目标端创建表空间
create tablespace tablespace_name '+DISKGROUP_NAME' size 32767m autoextend off; ... alter tablespace add tablespace_name add datafile '+DISKGROUP_NAME' size 32767m autoextend off; ...
复制
2.查看临时表空间,目标端创建临时表空间
需要注意:,因为要导入索引,所以可能就会有业务用户的临时表空间需要使用:
select distinct tablespace_name from dba_temp_files; create temporary tablespace TEMP_XXX tempfile '+DISKGROUP_NAME' size 32767m autoextend off; ... alter tablespace TEMP_XXX add tempfile='+DISKGROUP_NAME' size 32767m autoextend off; ...
复制
五.查看源库是否有自己安装的组件,如果有,则需要在目标端安装:
col comp_id format a30 col comp_name format a40 col version format a40 select comp_id,comp_name,version from dba_registry;
复制
六.在源端和目标端创建导出和导入用户,目录,授权:
如果用impdp+dblink,则直接在目标端创建即可,因为数据不用落地。这里,我们拿数据落地举例子。
源端:
创建用户:
create user user_impdp identified by oracle; create directory dir_impdp as '/share/xxx';
复制
赋权限:
根据现场赋权限,有些地方不能赋予dba权限,就赋予需要使用的权限
grant dba to user_impdp; (grant connect,resource,unilimited tablespace to user_impdp) grant read,write on directory dir_impdp to user_impdp;
复制
目标端:
创建用户:
create user user_impdp identified by oracle; create directory dir_impdp as '/share/xxx';
复制
赋权限:
根据现场赋权限,有些地方不能赋予dba权限,就赋予需要使用的权限
grant dba to user_impdp; (grant connect,resource,unilimited tablespace to user_impdp) grant read,write on directory dir_impdp to user_impdp;
复制
七.从源端导出全库数据
1.设置parfile:
vi expdp_full.par directory=user_impdp DUMPFILE=full_database_%U.dmp LOGFILE=full_database.log PARALLEL=32 #根据cpu数量,设置并行 #version=11.2.0.4 #根据目标端要求来导出版本 SCHEMAS=AAA,BBB,CCC,DDD,EEE #query=AAA.TABLE_1:"where 1=2" #这种方式可以在引号中写条件,从而导出满足条件的内容,这里表示只导出表结构
复制
2.导出数据
nohup expdp user_impdp/oracle PARFILE=expdp_full.par &
复制
八.导入数据
1.创建用户parfile,方便导入使用
vi schema.par schemas=AAAA, BBBB, CCCC, DDDD, EEEE
复制
2.修改共享目录权限
chmod -R 777 /share
复制
3.清除测试用户
操作前务必核对好机器,不要在生产库操作,且这步操作是在经历多次测试后才做的,新环境下不用做。
drop user AAAA cascade; drop user BBBB cascade; drop user CCCC cascade; drop user DDDD cascade; drop user EEEE cascade;
复制
注:如果删除用户比较慢,则可以先删除其中的大表或者大对象后,在删除用户,就会很快。
4.导入profile
nohup impdp user_impdp/oracle@xx.xx.xx.xxx:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp include=profile content=metadata_only logfile=impdp_profile.log &
复制
5.导入角色
nohup impdp user_impdp/oracle@xx.xx.xx.xxx:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp include=ROLE content=metadata_only logfile=impdp_role.log &
复制
6.导入用户
nohup impdp user_impdp/oracle@xx.xx.xx.xxx:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp include=USER,SYSTEM_GRANT,ROLE_GRANT,DEFAULT_ROLE parfile=schema.par content=metadata_only logfile=impdp_user.log &
复制
7.导入元数据(只导入表结构)
nohup impdp user_impdp/oracle@xx.xx.xx.xxx:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp exclude=type,package,user,index,trigger,procedure,CONSTRAINT,INDEX_STATISTICS,TABLE_STATISTICS parfile=schema.par content=metadata_only logfile=impdp_table.log &
复制
8.导入数据
nohup impdp user_impdp/oracle@xx.xx.xx.xxx:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp PARALLEL=32 parfile=schema.par content=data_only logfile=impdp_data_only.log &
复制
9.重建索引:
重建索引分为两种方式:
1)通过impdp生成sqlfile,然后手动修改索引中的index的并行度,直接执行修改后的sqlfile,创建索引;
nohup impdp user_impdp/oracle@xx.xx.xx.xxx:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp include=index sqlfile=index.sql parfile=schema.par content=metadata_only full=y logfile=create_index_sql.log &
复制
修改sqlfile:
vi index.sql create index AAA.index_1 on AAA.TABLE_1(COL_1) parallel 10; alter index AAA.index_1 no parallel; ....
复制
2)直接通过Impdp导入索引,同时加入parallel参数,对索引开启并行导入。
nohup impdp user_impdp/oracle@xx.xx.xx.xxx:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp include=index parallel=32 parfile=schema.par content=metadata_only full=y logfile=create_index_sql.log &
复制
两种导入的方式不同点在于:
通过sqlfile的方式,可以手动修改单独索引的parallel,从而加快单个索引的创建速度,但是创建索引还是逐个创建(当然,我们也可以通过手动对sqlfile进行分片,分成n个片,那么也可以等效为同时并行创建n个索引,且每个索引都是已parallel的并行度创建);
(12.2以后)直接通过impdp+parallel参数的方式,一条命令即可实现。且可以有parallel个索引同时创建,但是每个索引的创建,并不会使用并行。
10.重建约束:
对于约束,我们则可以直接通过impdp的方式直接导入,因为约束无法开启并行:
nohup impdp user_impdp/oracle@xx.xx.xx.xxx:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp include=container parallel=32 content=metadata_only full=y logfile=create_constraint_sql.log &
复制
而在12C以后,同样导入constraint,开启PARALLEL是会开启多个进程,同时导入多个constraint的,所以效率还是比较高。
在索引和约束导入过程中,需要注意以下几个问题,也是可以提前必坑的:
1)在导入索引后,发现索引的数量和原来的数量无法匹配上;
2)在创建约束的时候,发现有些约束会非常慢;
3)约束创建完以后,发现约束也有非常多的数量无法和源库匹配上;
对于问题一和问题二,我们可以生成约束的sql语句看一下约束创建语句:
nohup impdp user_impdp/oracle@xx.xx.xx.xxx:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp include=container parallel=32 sqlfile=constraint.sql content=metadata_only full=y logfile=create_constraint_sql.log &
复制
发现其中的constraint的创建方法,对于主键均是使用以下方式创建的:
alter table add constraint pk_xxxxx using (create unique index on table_name(col_1));
复制
通过这种方式创建,数据库会先创建唯一索引,再增加约束,而在创建索引的时候,并不会开启并行,所以真正慢的地方不是导入约束,而是创建索引;同时,这些索引是不会在导入索引的时候生成到sqlfile中的,这也就解释了为什么在导入索引后,索引的数量与源端不一致(我们在导入这部分约束后,发现索引数量与源端完全一致);
对于问题三,导入约束后,对比发现目标端约束的数量远小于源端。
筛选几张表,对目标端和源端的表上的约束进行比对,发现在源端的约束中,有很多以SYS_开头的约束名字,但目标端却没有。
select * from dba_constraints;
复制
这是因为,以SYS开头的约束,在创建的时候都没有指定约束名字,而在不同的系统中,没有指定名字的约束,数据库会自动给该约束生成一个SYS+编号名字。
对于这种约束,我们只能通过获取这些约束的DDL,并在目标库上执行才能成功创建。
具体方法如下:
select 'select dbms_metadata.get_ddl('||''''||'CONSTRAINT'||''''||','||''''||CONSTRAINT_NAME||''''||','||''''||owner||''''||')'||' from dual;' from dba_constraints where owner='AAA' and CONSTRAINT_NAME like 'SYS%';
复制
这样的约束大部分都是非空约束,所以执行起来还是比较快。
其实,从这里也可以看到,我们在应用程序开发的时候,开发标准的重要性:
1)如果我们创建主键约束的方式是先创建唯一索引,然后再通过唯一索引添加主键索引,那么索引则可以一次性全部创建好;
2)如果我们创建约束全部制定了名字,那么也可以一次性将约束全部导入,而不用单独创建。
11.最后导入除了统计信息以外的其他元数据(已经存在的会报错存在,并跳过)
nohup impdp user_impdp/oracle@xx.xx.xx.xxx:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp exclude=STATISTICS content=metadata_only logfile=impdp_metadata_last.log &
复制
十.收集全库统计信息:
vi gather_static.sh sqlplus / as sysdba<<EOF alter session set container=pdb; set timing on alter system set resource_manager_plan = 'DEFAULT_PLAN' scope=both exec dbms_stats.set_global_prefs('CONCURRENT','TRUE'); begin dbms_stats.gather_database_stats( ESTIMATE_PERCENT=>DBMS_STATS.AUTO_SAMPLE_SIZE, method_opt => 'for all indexed columns', cascade=>true, degree=>120); end; / exec dbms_stats.set_global_prefs('CONCURRENT','FALSE'); alter system set resource_manager_plan = 'FORCE:' scope=both exit; EOF
复制
执行这个语句,收集全库统计信息:
nohup gather_static.sh &
复制
十一.对比对象
这一步,分别对表、索引、约束、过程、触发器等数量进行比对即可。
select count(*) from dba_objects where owner not in ('ANONYMOUS','DBSNMP','EXFSYS','MDDATA','MGMT_VIEW','ORDPLUGINS','OUTLN','SCOTT','SI_INFORMATN_SCHEMA','SYSMAN','WK_TEST','WKPROXY','WKSYS','XDB','APPQOSSYS','AUDSYS','CTXSYS','DVSYS','GGSYS','LBACSYS','MDSYS','OJVMSYS','OLAPSYS','ORDSYS','SYS','SYS$UMF','SYSBACKUP','SYSDG','SYSKM','SYSRAC','SYSTEM','WMSYS','XS$NULL','DBSFWUSER','SI_INFORMTN_SCHEMA','DVF','GSMADMIN_INTERNAL','ORDDATA','GSMCATUSER','REMOTE_SCHEDULER_AGENT','PDBADMIN','GSMUSER','DIP','ORACLE_OCM','SPATIAL_CSW_ADMIN_USR','APEX_040200','APEX_PUBLIC_USER','SPATIAL_WFS_ADMIN_USR','user_impdp','FLOWS_FILES','APEX_050000') and owner not like '%APEX%') and object_type=&object_type;
复制