在前段时间,进行过一次expdp/impdp的逻辑迁移。但是也没有进行过非常详细的总结。这一次,终于想要把整个迁移过程和遇到的问题、解决方式都从头到尾的总结一下,用来以后给以后的迁移过程做个参考模板。
本次迁移是通过expdp/impdp的方式,将业务用户数据用expdp导出到NFS上,然后再通过impdp导入到新数据库中。
将存储划分到其中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,否则在导入的时候,可能会报错。
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%';
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操作,将表移出系统表空间,因为我们在迁移的时候是不会迁移系统表空间的,只能迁移业务表空间。
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;
create tablespace tablespace_name '+DISKGROUP_NAME' size 32767m autoextend off;
...
alter tablespace add tablespace_name add datafile '+DISKGROUP_NAME' size 32767m autoextend off;
...
需要注意:,因为要导入索引,所以可能就会有业务用户的临时表空间需要使用:
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;
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" #这种方式可以在引号中写条件,从而导出满足条件的内容,这里表示只导出表结构
nohup expdp user_impdp/oracle PARFILE=expdp_full.par &
vi schema.par
schemas=AAAA,
BBBB,
CCCC,
DDDD,
EEEE
chmod -R 777 /share
操作前务必核对好机器,不要在生产库操作,且这步操作是在经历多次测试后才做的,新环境下不用做。
drop user AAAA cascade;
drop user BBBB cascade;
drop user CCCC cascade;
drop user DDDD cascade;
drop user EEEE cascade;
注:如果删除用户比较慢,则可以先删除其中的大表或者大对象后,在删除用户,就会很快。
nohup impdp user_impdp/[email protected]:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp include=profile content=metadata_only logfile=impdp_profile.log &
nohup impdp user_impdp/[email protected]:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp include=ROLE content=metadata_only logfile=impdp_role.log &
nohup impdp user_impdp/[email protected]: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 &
nohup impdp user_impdp/[email protected]: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 &
nohup impdp user_impdp/[email protected]:1521/pdb directory=dir_impdp DUMPFILE=full_database_%U.dmp PARALLEL=32 parfile=schema.par content=data_only logfile=impdp_data_only.log &
重建索引分为两种方式:
1)通过impdp生成sqlfile,然后手动修改索引中的index的并行度,直接执行修改后的sqlfile,创建索引;
nohup impdp user_impdp/[email protected]: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/[email protected]: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个索引同时创建,但是每个索引的创建,并不会使用并行。
对于约束,我们则可以直接通过impdp的方式直接导入,因为约束无法开启并行:
nohup impdp user_impdp/[email protected]: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/[email protected]: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)如果我们创建约束全部制定了名字,那么也可以一次性将约束全部导入,而不用单独创建。
nohup impdp user_impdp/[email protected]: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<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;