LINUX 平台上的 ORACLE 踩坑记 -- 之 IMP 导数据

本篇是 LINUX 平台上的 ORACLE 踩坑日记 -- 之 IMP 导数据


系统要迁移到新的数据库,先尝试了 RMAN 异机恢复,一步一坑地走到最后一步 ( 执行升级脚本 @$ORACLE_HOME/rdbms/admin/catupgrd.sql; ),但是各种报错。百度了一下,在浩如烟海的经验教训贴中翻到一篇(https://blog.csdn.net/couyifu7209/article/details/100378598  RMAN跨版本恢复--从Oracle10.2.0.5恢复到Oracle11.2.0.4  ),告诉我如下这个残酷的事实:
  下图是Oracle数据库升级路线,从图看到 Oracle10.2.0.1 不能直接升级到11gR2版本,至少需要先升级到10.2.0.2以后才可以升级到11gR2,必须是10.2.0.2以上或者是10.1.0.5版本才可以直接升级到11gR2。
 

 查看 oracle 版本:sqlplus -v 
 很不幸我的源数据库版本正好是: Oracle10.2.0.1
 而我的目标数据库版本正好是:11.2.0.4.0

就是说 RMAN 那些坑踩过就算是为未来哪天做准备了,这次是指望不上了。
 

于是开启 B 计划 -- 用传统的 imp 工具导数据。

先拎要点:(都是吐血踩坑得到的教训!)


1. EXP 导出时,不用全库导出,费时费力,只要导你的应用账号下的全部对象即可;


2. IMP 导入前,一定要将数据库运行模式切换为“非归档模式”(noarchivelog),否则归档日志增长太快,来不及删除,账号会被封;


3. IMP 导入前,建立你的应用账号XXXXUSER,为该账号建立并分配表空间,!!!为表空间添加足够多的数据文件!!!切记此点,否则会空间满了半途中止,浪费时间;

想要看为啥要有上述要点,请慢慢看后文解释。


用传统的 exp ,imp 导数据,分为如下两大步:

一. exp 将数据从源数据库导出:(先说全库导出,其它各种带条件导出,放到最后的附录中去)

    exp 工具在 $ORACLE_HOME/bin 目录下
    
    [oracle@jcpt bin]$ cd $ORACLE_HOME/bin
    [oracle@jcpt bin]$ exp -help   可以查 exp 命令的参数 。
    
    
    为了避免客户端关闭后,进程也被关闭,我们用 nohup 方式启动工具,让其到后台去运行。
    (nohup 是指 no hang up 的意思,就是不挂起。一般格式是:nohup cmd命令区 >>mynohup.out &   。
      最后这个 & 是指让进程后台运行,>>mynohup.out 则是将输出存放到当前目录下的 mynohup.out 日志文件中。
      nohup 方式启动进程后,按几次回车,会退出到 CMD 状态,然后请输入 exit 退出远程窗口,另开一个远程窗口,去看是否还有进程,以及 mynohup.out 日志文件是否有被刷新,就能看出进程是否还在执行 )
    
    
  [oracle@jcpt bin]$ nohup exp userid=system/manager@TestDB file=/home/data/oracle/backup/fullDB.dmp full=y log=/home/data/oracle/backup/exp_full.log & 
  
  参数:
  userid:是登录 ORACLE  账号和密码,只能用 system 用户,因为 system 是 DBA 账号,能访问所有的表;@TestDB 这里是 SID。
  file:定义要将备份的数据文件保存到哪里,最好给全路径文件名,清楚;
  log:定义将 EXP 过程输出信息保存到哪个日志文件中去,格式是纯文本。我们启动 EXP 命令后(大数据量的情况下,这是一个漫长的过程,好几个小时)到哪里去观察导出进度;
  full:默认值为  N,这里指定为  Y,表示全库导出,包括系统控制表的一切。
  
  注意:
  在真实应用中,其实我们往往会将应用数据保存到特定的用户下,其实只要到这个用户下的全部对象就可以了(含表、函数、存储过程、触发器等等)
  
    导某个用户的全部对象语法如下:
    [oracle@jcpt bin]$ exp userid=system/manager@TestDB file=/home/data/oracle/backup/fullDB.dmp  owner=(system,sys) log=/home/data/oracle/backup/exp_full.log 

 owner:就是要导哪个账号的全部对象。


    启动了 exp 进程后,可以如下方式去观察进程执行情况:
    
    1)ps -aux | grep exp
        如果列表中有刚刚执行的命令 exp,那就对了,证明正常起来进程了。
    2)top
      如果在 1 秒一刷新的列表中,偶尔能看到 exp 进程,证明正常运行中。
    3)观察 mynohup.out 日志,如果有被写的痕迹(从被改日期就能看出),那么证明进程正常运行中,可以打开看进度。
    
    然后就等着去取导出的 .DMP 文件,即可进入下一步,导入。
    
---------------------------------------------------------------------------------

二. imp 将用 exp 导出的 DMP二进制文件导入到目标数据库中:

--首先一定要将目标库的归档模式改为:非归档模式
  两种归档模式的解释:
  1)非归档模式:指的是不写归档日志文件,就是说不跟踪数据的变化过程,不保存归档日志。
  2)归档模式:指的是要写归档日志文件,每一种数据变化都保存到归档日志中,这样就可以重现这个数据变化的过程。
  归档日志保存目录:$ORACLE_HOME/fast_recovery_area 目录下,会看到一堆的 .arc 文件,这些就的归档日志。当这个目录下的文件大小总和超过你设置的 归档日志大小 (默认 2G)时,oracle 就会挂起这个用户,连接失败,写入失败。
  这里一定要在导数据前将归档模式设置成非归档模式的原因是:存量数据很大,很容易归档日志就满了,影响导入。
  
  PS: 正常的生产系统上线后,请将其改为:归档模式,这样才多重保证数据安全,而不仅仅是依赖每日的备份。
  
  切换归档模式的操作方式如下:
      --以 DBA 身份连接 oracle
      > cd $ORACLE_HOME/bin
      > sqlplus / as sysdba
      
      SQL> select name, log_mode from v$database;
        SQL> shutdown immediate;
        SQL> startup mount;
        SQL> alter database noarchivelog;   # archivelog;
        SQL> alter database open;
  
  PS: 如果用户在生产过程中,归档日志满了,那么应该设置一种机制,周期性在确保安全的情况下删除归档文件。
     删除方式如下:
     > cd $ORACLE_HOME/bin
     > rman target / 
     DELETE NOPROMPT ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE - 7'; 
     
     这里的 'SYSDATE - 7' 参数的意思是:将 7 天前生成的归档日志删除。
     
  PS:一般的操作是在操作系统设置一个 计划任务,周期性执行归档文件删除动作,这个需要观察生产情况,看归档文件生成速度来决定删除几天前的归档日志文件。
     

--先试全量导入
nohup ./imp userid=system/PASSWORD@ORCL buffer=600000000 ignore=y full=y file=/home/data/oracle/backup/exp_full.dmp log=/home/data/oracle/backup/exp_full.log & 

  我是将刚装好的空库手动增加了两个测试表,每个加入了 3 条记录,全量导出后,再导入,但是即使完全没有什么内容的空库导出 DMP 文件,导入时间仍然是太长了,没执行完。看日志,我手动加入的两个小表都正常导入了,然后就是各种报错,都是系统级的。所以不建议全量导入,应该尝试导入某个用户的全部内容。
  
  注释:即使是全库导出得到的 DMP 文件,也可以在 IMP 导入的时候指定条件(比如指定只导 特定 USER 的内容,只导特定表)。
  
  结论:全库(full=y)方式导入,会同时进行系统表(几千个)的同步,而这些系统表安装时就存在,同步不会产生任何效果,而只会各种唯一性检查报错。
        所以,导入时一定要至少按 user 导入。

--再试用户级导入(普通方式 & nohup 方式)
imp userid=system/PASSWORD@TestDB fromuser=sourceUser touser=sourceUser rows=y indexes=n commit=y buffer=50000000 ignore=n file=exp_table.dmp log=exp_table.log tables=USER_INFO,POLICY_INFO

nohup ./imp userid=system/PASSWORD@TestDB rows=y indexes=n fromuser=system touser=system buffer=500000000 commit=y ignore=y file=/home/data/oracle/backup/exp_hyhtest11.dmp log=/home/data/oracle/backup/imp_hyhtest11.log &

    参数说明:
        userid:是登录 ORACLE  账号和密码,只能用 system 用户,因为 system 是 DBA 账号,能访问所有的表;@TestDB 这里是 SID。
        fromuser:指定只导哪个用户名下的全部对象和数据(这里是指源数据库的 user 名)。
        touser:指定将 fromuser 用户下的全部对象和数据导入到目标数据库中的哪个 user 下(这里是指目标数据库的 user 名)。
                这种只导一个用户的方式,要求必须先在目标数据库中建立好相应的用户,分配好用户的权限、表空间、角色等。
      file:定义从哪个 DMP 文件导入(DMP 文件是 exp 导出生成的,二进制,不能编辑),最好给全路径文件名,清楚;
      log:定义将 imp 过程输出信息保存到哪个日志文件中去,格式是纯文本。我们启动 imp 命令后(大数据量的情况下,这是一个漫长的过程,比export时间还长)到哪里去观察导出进度;
      rows:默认值为  Y,即要将数据表结构 & 数据行 一并导入,导入时是一个装库的过程,会建立和维护相应的索引。
            可以设置为 N,即只导入建立表结构,不导数据行。这样的好处是:先定义表结构,然后可以先删除索引定义、触发器定义,可以提高导入数据的速度,等所有数据都进入后,再建立索引,可以避免在导入数据过程中不断检查索引,尤其是组合唯一索引。
      indexes=n:不导索引。
      buffer=500000000:设置缓冲区 500M,等缓冲区满做一次提交 COMMIT。这个参数是跟着 commit=y 一起用的,意思是每写满一个 buffer ,就 commit 一次,这样的结果是我们在整个导入过程中,可以用如下语句来跟踪某个表导入多少数据了,来进行时间耗费的估算,合理安排工作:
             select count(*) from  %tablename  
      
              如果没有设置 buffer=xxx  COMMIT=Y 这样的参数组合,整个导入过程中,是一个表的数据都导入完成,才做一次 COMMIT,这样的处理方式,在一个一千万以上记录的大表导的过程中,场面即将是:
                1)什么都监测不到,也不知道进展到什么程度了,也不知道工具在干啥,也不敢停掉,非常无助。
                2)临时回滚区特别大,磁盘占用很高,速度很慢
                3)select count(*) from  %tablename   查看一直是 0,因为导入过程将一个表的数据作为一个 transaction,完成后一次 commit。
      commit=y:参数说明参见上一个         buffer=500000000,两个参数配合使用。
      ignore=y:这个参数的意思是:在导入过程中,要创建表和索引,如果创建失败(最常见的失败就是目标库中已经有这个表了),将做忽略处理,继续导数据。这里特别注意,最好是设置成 y,否则可能导数据不完整。
      
--最后一定要将目标库的归档模式改为:归档模式
  为什么一定系统要运行在归档模式(archivelog)?
  原因有两个:
    1)系统数据的安全性:归档模式是数据变化台账,就像会计的台账一样,会如实记录数据被修改的全过程。理论上来说,只要归档日志保存完整,可以还原每一条数据记录的被修改历程。
    2)每日的动态备份 RMAN,要求数据库系统运行在归档模式下才能进行。
  备注:
    1)归档模式下要写归档日志,所以需要定期清理(或转移)归档日志文件,这个可以在 RMAN 中跟着备份一起考虑。
    2)归档日志对于像银行这样的业务非常关键,因为每个人的账户上就是个数字,这个数字的变化历史就显得非常重要(因为哪天这个数字从 100万 被改到 1万,这个动作对数据库系统来说非常简单,但是对储户和银行问题就大了);
    3)但是对普通生产(比如数据生产),这个归档日志就没那么重要了,只要保持一定时间段内的归档日志,保证系统中的数据正常保存着,就可以删除若干天前的归档日志。
      

      
我的踩坑过程:

1. 第一次导入,用了 full=y ,用的新服务器上导出的空库来试,结果特别慢,看日志是各种系统表在折腾,证明不能用完全导入,无谓的付出太多;


2. 第二次导入,没有设置 commit=y,结果是观察已导入的数据始终是 0,这才意识到一个表一次 COMMIT,中止进程,重来;


3. 用  truncate table myuser.xxx; 将已经导入的数据删除,否则会重复导入;


4. 第三次导入,设置了 buffer=4096000000 commit=y,报错说  buffer 设置值不合法,遂减小缓冲区为 600000000,重来;


5. 第四次导入,没有设置目标数据库为 noarchivelog ,结果是大约 1 小时后发现闪回区 归档日志就满了,用户登录被锁,赶紧去手动删除了归档文件,手动用 rman 删除过期归档日志,语句如下:

    先手动删除 $ORACLE_HOME/fast_recovery_area  目录中的文件,删除后,下文中的 crosscheck archivelog all; 就会认定在 RMAN 中保存的归档日志为无效,就可以清除。

    > rman target /
    
    run {
      crosscheck archivelog all; 
      delete noprompt expired archivelog all;  --noprompt 参数必须有,否则会一个一个文件弹窗让你确认,烦死
  }

    然后因为已经导入了一部分,所以不舍得中断,结果就不停地删除归档日志,不停清除。后来证明这种情况下不要不舍得中断,因为后续有很大的表,导入过程很长,如果一直靠手工删除归档,夜里就没办法操作。纠结了一段时间后,还是杀掉进程,重新来过。
    
6. 第五次导入,先 TRUNCATE 已经导入的数据库表,然后设置目标数据库为 noarchivelog ,再次启动 IMP,这次 OK 了(记得一定要用 nohup 后台方式启动,因为这是一个漫长的过程,100G 的数据要折腾 50 小时左右);    

看 $ORACLE_HOME/product/11.2.0/db_1/bin 目录下的 nohup.out 文件,会发现该文件隔一段时间会被写一次(修改时间会变化),打开看该文件,会发现 IMP 在一个表一个表地导入,每导入一个表会记录一下,形如:


Import: Release 11.2.0.4.0 - Production on 星期三 4月 27 14:01:23 2022

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.


连接到: Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, Oracle Label Security, OLAP, Data Mining,
Oracle Database Vault and Real Application Testing option

经由常规路径由 EXPORT:V10.02.01 创建的导出文件
已经完成 UTF8 字符集和 AL16UTF16 NCHAR 字符集中的导入
导入服务器使用 AL32UTF8 字符集 (可能的字符集转换)
导出客户机使用 US7ASCII 字符集 (可能的字符集转换)
. 正在将 xxxxUSER 的对象导入到 xxxxUSER
. . 正在导入表                     "TSYS_CODE"导入了           8 行
. . 正在导入表                 "TSYS_CODEDATA"导入了         124 行
. . 正在导入表                      "TSYS_LOG"导入了    15162575 行
. . 正在导入表                     "TSYS_USER"


如果遇到一个大表,每个字段的内容又多(比如有大字段 CLOB BLOB),这个 nohup.out 文件就会长时间不更新。
此时就去看最后那个表名有多少记录了:

        > cd $ORACLE_HOME/bin
      > sqlplus / as sysdba
      
      SQL> select count(*) from TSYS_USER;
      
来观察大表的入库进度。在 SQL> 中,可以只输入 / 重复上次的查询,形如:
    SQL> /
    
可以跟踪入库进度,例如每 10 分钟查询一次,看看每分钟入库多少记录,借此估算共需要的时间。

7. 根据源服务器上的数据量,以及 EXP 导出的文件的大小,估算了一下需要多少表空间,为该用户的默认表空间追加数据文件,确保导数据过程中不会出现空间不够报错而前功尽弃(别问我是怎么知道的。。。)。

  计算方法:一个数据文件最大 32G,看你的 EXP 导出文件有多大,留出充足的余量,为表空间追加足够多的数据文件。数据文件一旦创建,磁盘就是被占的状态,你可以看到那个大小为 4G(初始大小) 的文件已经存在在那里,随着装库的进行,会自动扩展大小,直到32G。
  
  语句如下:
  
  CREATE TABLESPACE "xxxxSPACE"     LOGGING     DATAFILE '/home/data/oracle/oradata/ORCL/xxxxSPACE.ORA' SIZE 4096M REUSE AUTOEXTEND     ON NEXT  1280K MAXSIZE  32767M EXTENT MANAGEMENT LOCAL     SEGMENT SPACE MANAGEMENT  AUTO ;  --创建表空间
  
  CREATE TEMPORARY TABLESPACE "TS_TEMP_xxxxSPACE" TEMPFILE '/home/data/oracle/oradata/TEMP_xxxxSPACE.ORA' SIZE    80M REUSE AUTOEXTEND     ON NEXT  640K MAXSIZE  32767M EXTENT MANAGEMENT LOCAL UNIFORM    SIZE 1024K;  --创建临时表空间

    ALTER USER "xxxxUSER"  DEFAULT TABLESPACE "xxxxSPACE"     TEMPORARY TABLESPACE "TS_TEMP_xxxxSPACE" -- 为用户设置默认表空间
    --如上的为用户创建表空间的过程,应该是开始导入之前就完成的。
    
    
    
    --为 xxxxSPACE 表空间增加 4 个额外的数据文件。
    ALTER TABLESPACE "xxxxSPACE" ADD DATAFILE '/home/data/oracle/oradata/ORCL/xxxxSPACE_FILE2.ORA'  SIZE 4096M REUSE AUTOEXTEND     ON;
    
    ALTER TABLESPACE "xxxxSPACE" ADD DATAFILE '/home/data/oracle/oradata/ORCL/xxxxSPACE_FILE3.ORA'  SIZE 4096M REUSE AUTOEXTEND     ON;
    
    ALTER TABLESPACE "xxxxSPACE" ADD DATAFILE '/home/data/oracle/oradata/ORCL/xxxxSPACE_FILE4.ORA'  SIZE 4096M REUSE AUTOEXTEND     ON;
    ALTER TABLESPACE "xxxxSPACE" ADD DATAFILE '/home/data/oracle/oradata/ORCL/xxxxSPACE_FILE5.ORA'  SIZE 4096M REUSE AUTOEXTEND     ON;


  解释:
  每个表空间的文件,最大为  32G
  如果 exp 导出文件的大小明显大于这个 32g, 那么一定要提前为表空间添加好数据文件,否则数据文件满了,后续导数据过程中报错,形如:
  
  . . 正在导入表               "xxxx_RECIEVE"
IMP-00058: 遇到 ORACLE 错误 1658
ORA-01658: 无法为表空间 xxxxSPACE 中的段创建 INITIAL 区
. . 正在导入表     "xxxx_ROLERIGHTRELATION"导入了          14 行
. . 正在导入表                   "Txxxx_SEQ"
IMP-00058: 遇到 ORACLE 错误 1658
ORA-01658: 无法为表空间 xxxxSPACE 中的段创建 INITIAL 区
. . 正在导入表                     "xxxx_BASE"
IMP-00058: 遇到 ORACLE 错误 1658
ORA-01658: 无法为表空间 xxxxSPACE 中的段创建 INITIAL 区
. . 正在导入表                  "xxxx_CJBATCH"导入了          36 行
. . 正在导入表                 "xxxx_CJDETAIL"导入了        3507 行
. . 正在导入表                   "xxxx_VOLUME"
IMP-00058: 遇到 ORACLE 错误 1658
ORA-01658: 无法为表空间 xxxxSPACE 中的段创建 INITIAL 区
...............................

最抓狂的是:某些小的表,能在数据表空间装下的,就创建成功了,而有的表太大,就创建不成功,导致整个导数据工作支离破碎,后续处理也很难。
所以重要的事情说三遍:
!!!为用户默认表空间添加足够的数据文件!    
!!!为用户默认表空间添加足够的数据文件!    
!!!为用户默认表空间添加足够的数据文件!    

附录:
1. 查询数据库用户的语句:
  首先以  DBA 身份登录 ORACLE: sqlplus / as sysdba
  
    然后在 SQL> 提示符下,输入如下命令。可以复制粘贴,但是没有上箭头可以复用命令。
    
    --如果执行上次执行的命令,可以只输入一个  /  ,重复执行;
    
    --查询 ORACLE 系统中所有 用户账号
    select username from all_users; 
    
    --查询某个 用户账号 名下的所有表名
    select table_name from all_tables a where a.OWNER = upper('xxxxUSER');   --单引号里的是你的真实用户账号
    
    --如下组合修改数据库归档模式过程
        select name, log_mode from v$database;
        shutdown immediate;
        startup mount;
        alter database noarchivelog;   # archivelog;
        alter database open;

        --解锁 user
        alter user xxxxUSER account unlock;   

    

2. 各种导出 EXP:
 1) 将数据库全库导出

   exp userid=system/manager@TestDB file=E:\sampleDB.dmp  full=Y 
   
 2) 将数据库中system用户与sys用户的表导出

   exp userid=system/manager@TestDB file=E:\sampleDB.dmp  owner=(system,sys)

 3) 将数据库中的表 TableA,TableB 导出

    exp userid=system/manager@TestDB  file=E:\sampleDB.dmp  tables=(TableA,TableB)

 4) 将数据库中的表tableA中的字段filed1 值为 "王五" 的数据导出

   exp userid=hr/hr@TestDB tables=(employees) file=query.dmp log=query.log query='where employee_id ">" 110 '    --将会导出导出满足条件的数据。
   
   
3. 设置周期性归档日志删除机制

 1)编辑一个 sh 文件,内容是删除指定天数(这里假设是  7 天)前的归档日志
    
    cd  /var
    vi deletearchive.sh
    
    编辑该文件的内容为:
    
#script:deletearchive.sh
#desc:backup full database datafile in archive with rman
#connect database
export ORACLE_BASE=/home/data/oracle
export ORACLE_SID=ORCL
export ORACLE_HOME=/home/data/oracle/product/11.2.0/db_1

export PATH=$PATH:$HOME/bin:$ORACLE_HOME/bin

su - oracle -c "rman target / << EOF_RMAN

#delete archivelog before 7 days, and obsolete
run{
    report obsolete;
    delete noprompt obsolete;
    
    DELETE NOPROMPT ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-7';
}

"

#end
    
    
    
  2)先去观察 ORACLE 的闪回区($ORACLE_HOME/fast_recovery_area 目录)的归档日志文件有多少,有没有 7 天前产生的,然后尝试手动运行一下这个 sh 文件
      cd  /var
    ./deletearchive.sh
   
  3)再观察 ORACLE 的闪回区的归档日志文件有多少 ,看看 7 天前生成的日志文件有没有被删除。
   
   

      

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值