☰
Oracle数据泵导入报错:ORA-39111/39083/01403排查
2026/11/3 4:37:21 网站建设 项目流程

1. 三个报错先摆到台面上:哪个是真错,哪个是虚惊

同一份导入日志,有人看一眼就喊“挂了”,有人扫两眼就知道“没事”。差别不在水平,在于有没有区分清楚 ORA-39111、ORA-39083、ORA-01403 这三类信息在数据泵(Data Pump)日志里的不同身份。我这几年做过几十次跨环境搬迁,从旧业务库往新机房搬、从测试环境往准生产环境搬,最常见的场面就是:作业跑完了,有人把 import_01.log 从头拉到尾,看到十几行以 ORA- 开头的文本,直接把截图丢到群里说导入失败了。实际上 impdp 的日志会把提示级、警告级、错误级的消息统一打上 ORA- 前缀,它们睡在同一个文件里,长得几乎一样,只有认真读文本内容才能分辨。ORA-39111 多数情况下属于“虚惊”的那一类;ORA-39083 是外壳,它本身只告诉你“某个对象没建成功”,真凶藏在紧跟其后的几行里;ORA-01403(no data found)则是那个经常被牵连出来的、看起来莫名其妙实则线索明确的底层错误。这篇内容面向的是正在处理数据泵导入报错、或者准备做一次跨库搬迁的同行,不管你是刚接手数据库运维的新手,还是已经踩过几次坑的老手,都能从里面找到可直接落地的排查路径和参数模板。

1.1 ORA-39111:日志里最容易被误伤的一行

先把这个说清楚,能省掉很多无效排查。数据泵在写完一组转储文件之后,会在日志里输出一条带 ORA-39111 编号的信息文本,大意是“某组转储文件已经写完”。它是一条进度性质的通知,不是失败判定。判断一次导入到底成没成,只有一个标准动作——把日志拉到最末尾,看最后那行作业结束语,正常的样子是Job "SYSTEM"."SYS_IMPORT_FULL_01" successfully completed at ... elapsed 0 00:12:33。只要这行在,前面出现过多少次 ORA-39111 都不重要。反过来,如果末尾是completed with N error(s),那才需要往下深挖。

为什么那么多人会误判?因为绝大多数人第一次接触数据泵时,脑子里装的是一个朴素规则:日志里出现 ORA- 就是出错。这个规则在 SQL*Plus 里基本成立,在数据泵日志里不成立。数据泵的日志像一个混装信箱,签收通知和报警短信塞在同一个信封里,只按发件人前缀筛选必然误伤。我见过最夸张的一次,一位同行为 ORA-39111 折腾了半天,重建目录对象、改权限、换文件系统,最后发现导出本身完全正常,问题出在他根本没看日志末尾。所以我的建议很直接:先把日志末尾那二十行看完,再决定要不要动手。

还有一个容易混淆的点需要提醒:中断过的作业。如果导入中途被 Ctrl+C 或者被 kill 掉,作业会留在dba_datapump_jobs里,状态显示 NOT RUNNING,对应的主表(比如SYS_IMPORT_FULL_01)也还留在库里。这种残留状态跟 ORA-39111 没有关系,但很多人会把“作业表还在”当成“导入没结束”的证据。清理方式很简单,确认不再续跑之后,用DROP TABLE "SYSTEM"."SYS_IMPORT_FULL_01" PURGE;把主表删掉即可。真要续跑,用impdp ... attach=SYS_IMPORT_FULL_01挂上去,交互式敲continue_client就能接着走,这比从头重来省几个小时。

1.2 ORA-39083:只是个外壳,真凶藏在下面两行

ORA-39083 的完整文本通常长这样:Object type TYPE failed to create with error ... Failing sql is: ...。请注意,它告诉你的是三件事——哪个对象类型、创建失败、失败的 SQL 是哪条,但它唯独不告诉你“为什么失败”。原因在紧跟其后的那两到三行,那里会给出真正的底层错误码。我在日志里见过的组合太多了:底下的 ORA-00942(表或视图不存在)说明依赖对象没到位;ORA-01031(权限不足)说明执行用户权限不够;ORA-01950(表空间无权限)说明配额没给;ORA-00955(名称已被占用)说明目标库存在同名对象;ORA-01652(临时段无法扩展)说明临时表空间不够大。同一个 ORA-39083,配上不同的底层错误,处理手法完全相反。这就是为什么我从不建议直接拿 ORA-39083 去搜索解决方案,那等于拿着“门打不开”四个字去问锁匠。

实际操作中,一条典型的失败日志段落大概是这样:

Processing object type SCHEMA_EXPORT/TABLE/INDEX/INDEX ORA-39083: Object type INDEX failed to create with error: ORA-01652: unable to extend temp segment by 128 in tablespace TEMP Failing sql is: CREATE INDEX "APPUSER"."IDX_ORDER_HDR_03" ON "APPUSER"."ORDER_HDR" ...

这三行连起来读,结论就很清楚:索引建不出来,因为排序时临时表空间撑不住。处理办法要么扩 TEMP,要么把索引单独拎出来用INCLUDE=INDEX补导一次,要么干脆用EXCLUDE=INDEX先导元数据和数据、事后在业务低峰期单独建索引。三种办法各有取舍,但不管选哪种,前提都是先把日志读成“索引 + 临时表空间”这两条信息,而不是“导入失败了”。

1.3 ORA-01403:no data found 在数据泵里的四条常见来路

ORA-01403 是 PL/SQL 里最普通的“没查到数据”,跑到数据泵日志里就显得很突兀——明明在导对象,怎么会查不到数据?根据我处理过的案例,它出现的位置大致有四条路径,判断依据各不相同。

第一条,元数据抽取阶段。数据泵在生成某个对象的 DDL 时,需要去数据字典里取一批配套信息(比如分区信息、约束依赖、存储属性),如果目标环境里缺了某个前置对象,字典查询返回空行,就会以 ORA-01403 的形式冒出来。这种情形的特征是:报错对象往往是视图、物化视图、或者带复杂依赖的类型对象,而失败的 SQL 通常是内部调用而非你我手写的 DDL。

第二条,PL/SQL 对象编译。包体、存储过程、触发器在创建时只做语法检查,编译是单独一步。如果它依赖的表、视图、自定义类型还没到位,编译就会失败。多数时候报的是 ORA-00942,但在某些内部包路径下会退化成 ORA-01403。判断方法是登录目标库,查dba_objects里该 schema 下状态为 INVALID 的对象,对上了就基本可以确认。

第三条,统计信息导入。数据泵默认会把源库的统计信息一起搬过来。如果目标端的表因为前面出错没建成功,统计信息这一步就会去查一张不存在的表,返回空行。这种情形很好识别:报错对象类型写着STATISTICS,把统计信息排除掉(EXCLUDE=STATISTICS)再导一次就安静了。

第四条,版本与特性差异。从高版本往低版本导,源库对象用了目标库不认识的定义,字典视图字段也不一样,某些查询自然取不到值。这条路径最难缠,因为报错位置飘忽,可能今天报在类型对象,明天报在调度任务。判断线索是:源端与目标端的版本号差几个大版本,且日志里除 ORA-01403 外还伴有语法类错误。

2. 读日志:把一句报错还原成“对象 + 错误 + SQL”

日志读不明白,后面全是瞎猜。数据泵给了三个入口,用好了能省大量时间:日志文件本身、作业主表、以及dba_datapump_jobs视图。三者各有分工,谁也替代不了谁。日志文件是原始流水,信息最全但不好筛;作业主表结构化程度高,能直接按 status 过滤;视图用来确认作业状态和残留。我一般的习惯是先用视图确认作业是否真的结束了,再用主表捞出所有失败对象,最后回到日志文件看被截断的完整 SQL。

2.1 日志文件、日志表和作业状态三处对照

日志文件的位置由LOGFILE参数决定,如果没指定,默认会在目录对象指向的路径下生成import_%U.log之类的名字,%U在并行导入时会被替换成两位序号,所以要留意是不是有多个日志文件。我踩过的一个坑是:并行度设了 4,只看了import_01.log,另外三个文件里的失败信息完全没看,结果补导时又漏掉一批对象。后来我把规矩定死——先把目录里所有 log 文件列出来,再逐个过。

作业主表的名字跟作业名一致,默认作业名是SYS_IMPORT_FULL_01(如果是 schema 级导入也可能是SYS_IMPORT_SCHEMA_01),所在 schema 是发起的那个用户,用 SYSTEM 跑就在 SYSTEM 下。这张表保留到作业结束,正常完成后会自动删掉,出错时不删,正好给我们留了现场。

-- 先确认有没有残留作业 SELECT owner_name, job_name, operation, job_mode, state FROM dba_datapump_jobs; -- 再看作业主表里到底哪些对象挂了 SELECT object_schema, object_name, object_type, status FROM "SYSTEM"."SYS_IMPORT_FULL_01" WHERE status = 'ERROR';

第二条 SQL 是我最常用的,比 grep 日志高效得多。它的输出直接就是一份失败清单,对象名、对象类型、schema 一目了然。拿到清单之后再回日志里搜对应对象名,就能看到完整的底层错误和失败 SQL。

2.2 三条 SQL 把失败对象捞干净

光筛选 ERROR 还不够,有些对象是“创建成功但编译失败”,状态会显示为SUCCESS,需要单独看dba_objects才能发现。我常用的三板斧是这样的:

-- 1. 全部失败对象 SELECT object_type, COUNT(*) FROM "SYSTEM"."SYS_IMPORT_FULL_01" WHERE status = 'ERROR' GROUP BY object_type; -- 2. 带失败 SQL 的明细(注意可能被截断) SELECT object_name, object_type, log_file FROM "SYSTEM"."SYS_IMPORT_FULL_01" WHERE status = 'ERROR' AND object_type = 'INDEX'; -- 3. 目标库里状态异常的对象 SELECT object_type, object_name, status FROM dba_objects WHERE owner = 'APPUSER' AND status <> 'VALID' ORDER BY object_type, object_name;

主表里其实还有一列可以取失败 SQL,但实际用下来经常会遇到截断问题——DDL 长了就显示不全。这时候必须回到日志文件,因为日志文件里的Failing sql is:之后是完整文本。我有一次就是因为主表里 SQL 被截在中间,漏看了后半段的TABLESPACE "OLD_DATA",白白多排查了两小时,最后才发现是表空间没重映射。

2.3 对象类型和底层错误的对照矩阵

把失败清单按“对象类型 × 底层错误”做成矩阵,处理思路立刻清晰。下面这张表是我自己整理的,覆盖了我遇到过的八成场景:

对象类型常见底层错误典型成因处置方向
INDEXORA-01652临时表空间不足扩 TEMP 或事后单独建索引
INDEXORA-00942基表未建成功先修表再补索引
VIEWORA-00942 / ORA-01403依赖表或视图缺失按依赖顺序补导
PACKAGE BODYORA-00942依赖对象未到位补导依赖后重新编译
TYPEORA-01403元数据抽取异常、特性不支持手工建或从源端导数 DDL
TABLEORA-01950表空间配额不足给用户加配额
TABLEORA-00955同名对象占用先清理或改创建策略
TRIGGERORA-04088 / ORA-01403触发体依赖缺失检查引用对象
STATISTICSORA-01403表不存在EXCLUDE=STATISTICS 重导
MATERIALIZED VIEWORA-01403基表或物化视图日志缺失先建基表再建物化视图

这张表的价值不在于穷举,而在于给你一个“看第一眼就知道往哪个方向查”的索引。遇到表里没有的组合也不用慌,回归基本逻辑:对象建不出来,要么是依赖没到位,要么是权限或空间不够,要么是语法在目标库不成立,三条路逐一排除就行。

3. 逐类攻破:ORA-39083 与 ORA-01403 的实战处置

排查到对象级别之后,处理方式就分化了。有些问题改一个参数就解决,有些必须手工补 DDL,有些得回头改导出端。这一章按类型拆开讲,每一类我都附上实际处置步骤和判断依据,你可以对照自己的日志直接套。

3.1 依赖对象缺失:视图、同义词、触发器的补救顺序

导入过程是按对象类型分批处理的,顺序大体是类型定义、序列、表、数据、索引、约束、视图、触发器、PL/SQL、统计信息。理论上依赖关系是被考虑过的,但一旦某个环节出错,后面的对象就会连锁失败。比如某张表因为表空间问题没建成功,基于它的视图、同义词、触发器会一个接一个报错,日志里瞬间铺满几十行。

我的处置顺序是:先把所有失败的基表找出来,单独补导(INCLUDE=TABLE加上TABLE_EXISTS_ACTION=SKIP避免重复),确认表和数据都在了,再补导索引和约束,最后补导视图和触发器。这种“分层补导”比一次性重来稳得多,也便于控制影响范围。补导的时候注意加上EXCLUDE=STATISTICS,能少掉一批无关报错。

有一个细节值得单独说:同义词。如果源库存在指向其他 schema 的私有同义词,而目标库那个 schema 不存在,同义词创建会成功(它允许指向不存在的对象),但所有依赖它的视图和存储过程都会编译失败。这种“不报错但不可用”的情况最阴险,靠日志看不出来,必须查dba_objects的 INVALID 列表。

3.2 PL/SQL 与类型对象:创建成功不等于可用

包、存储过程、函数、触发器在数据泵里的处理分两步:先按源端 DDL 创建,再触发编译。如果编译失败,日志里可能只留一句警告,对象却实实在在地躺在那儿,状态是 INVALID。业务上线时才会突然报“对象无效”,那时候查起来就更费劲了。

我习惯在导入结束后立刻跑一次全 schema 编译和状态检查:

BEGIN DBMS_UTILITY.COMPILE_SCHEMA(schema => 'APPUSER', compile_all => FALSE); END; / SELECT object_type, object_name FROM dba_objects WHERE owner = 'APPUSER' AND status = 'INVALID';

compile_all => FALSE的意思是只编译 INVALID 的对象,比全量编译快得多。查完如果还有残留的无效对象,逐个看它的依赖。我遇到过一次特别典型的情况:一个包体里引用了一张表,而这张表在导入时因为TABLE_EXISTS_ACTION=SKIP被跳过了,但目标库里那张表是老结构的,字段名对不上,编译自然失败。这种问题日志里完全看不出来,只能靠事后校验。

TYPE 对象要更谨慎一些。自定义类型如果带方法体(CREATE TYPE BODY),导入失败的处理成本比普通 PL/SQL 高得多,因为表可能依赖这个类型,删不掉也改不了。这种情况我的做法是:先把依赖它的表结构导出来(用impdp ... sqlfile=ddl.txt生成 DDL 文本,不实际执行),手工把类型建好,再回头按顺序建表。

3.3 权限、表空间、配额:环境没备好,导一百次也白搭

这类问题最冤,因为跟数据本身没关系,纯粹是目标环境没准备好。典型表现是 ORA-01950(表空间无权限)、ORA-01031(权限不足)、ORA-00959(表空间不存在)。我在项目里养成了一个习惯:正式导入之前,先让目标端的 DBA 把这几样东西确认一遍,签字画押。

第一,目标用户要有UNLIMITED TABLESPACE或者足够的配额。配额不足时,表能建、数据能进一部分,然后在某个大表上突然报错,前面导的都白费。查配额用SELECT tablespace_name, bytes, max_bytes FROM dba_ts_quotas WHERE username='APPUSER';,max_bytes是 -1 表示不限。

第二,表空间要么已经建好,要么在导入命令里用REMAP_TABLESPACE重映射。忘了重映射的后果是:导入跑到一半报 ORA-00959,作业中断,主表残留,还得清理。重映射的写法是REMAP_TABLESPACE=OLD_DATA:NEW_DATA,可以写多组。

第三,目录对象和文件系统空间。导入虽然不写转储文件,但如果用了LOGFILE、SQLFILE、DUMPFILE(补导时),都需要目录对象有写权限。目录对象指向的路径在数据库服务器上,不是客户端,这一点新手经常搞混,结果在本地机器上翻半天找不到日志。

3.4 版本、字符集、时区与隐式参数:跨版本搬迁的暗礁

跨版本导入是 ORA-01403 和 ORA-39083 的高发区,原因很杂。数据泵只保证“同在支持矩阵内、大致兼容”,具体到某个特性,能不能过要看运气。我踩过的几类:源端用了目标端不支持的段创建属性,DDL 里带着目标端不认识的子句;字符集不同导致超长标识符被截断,进而引发对象名冲突;时区文件版本不同,带时区列的表建出来就报错。

字符集这件事值得多讲两句。从 AL32UTF8 往 ZHS16GBK 导,中文字符占用的字节数从 3 变成 2,字段长度够不够要看字节语义。如果源端是VARCHAR2(100 CHAR),目标端按字节算,一个中文字符 2 字节,理论够用;但如果源端是VARCHAR2(100)字节语义、里面塞满中文,到目标端反而不会出问题,真正出问题的是反方向。所以搬迁方向不同,风险点也不同。我的做法是导入前先跑一遍字段长度扫描,把接近上限的列找出来,心里有数。

时区的问题更隐蔽。日志里报的是 ORA-39083 加一串看不懂的底层错误,实际原因是目标库的时区文件版本比源库旧。解决办法是升级时区文件版本,但这一步在生产环境要走变更流程,不能随手做。所以跨大版本搬迁之前,一定要把两端的SELECT * FROM v$timezone_file;拿出来对一遍。

4. 一次完整实操:从报错环境到干净导入

理论讲完,落到具体动作。这一章我按真实操作顺序走一遍,你可以直接照着抄。假设场景是:把一套业务库从旧环境搬到新环境,schema 为 APPUSER,源端数据量 120GB,中间表大约 3000 张。

4.1 导入前的环境体检清单

这一步花二十分钟,能省掉后面几小时。我整理的清单如下:

检查项命令或方法合格标准
目标用户是否存在SELECT username FROM dba_users WHERE username='APPUSER';存在且未被锁定
表空间是否齐备对比源端dba_tablespaces与目标端缺失的已建好或已重映射
用户配额dba_ts_quotasmax_bytes 为 -1 或足够大
临时表空间dba_temp_files至少为最大单表索引排序量的 1.5 倍
目录对象dba_directories存在且服务器路径可写
字符集nls_database_parameters明确差异并评估影响
时区文件版本v$timezone_file两端一致或已知差异
剩余磁盘空间操作系统层面 df不低于源库数据量的 1.2 倍

临时表空间那一条特别说明一下怎么估。排序所需空间大致跟最大索引的键值总量相关,粗略可以按“最大的几张表数据量之和的 10%~20%”估算,再留一倍余量。比如最大的三张表合计 30GB,TEMP 至少给到 8GB 以上,我一般直接给 16GB,宁可浪费也别中途报 ORA-01652。

磁盘空间的 1.2 倍系数是这么来的:源库数据量 120GB,考虑索引重建(索引通常占数据量的 20%~30%)、undo 增长、以及导入过程中的临时占用,实际落盘需求往往超过 1.2 倍。如果空间确实紧张,可以考虑分批导入,一个 schema 一个 schema 地过,或者先CONTENT=METADATA_ONLY建结构、再CONTENT=DATA_ONLY灌数据。

4.2 参数模板:一份可以直接改名字就用的命令

下面这套参数是我打磨过多次的版本,适用于大多数 schema 级搬迁:

impdp system/******@TARGETDB \ directory=DP_DIR \ dumpfile=appuser_%U.dmp \ logfile=imp_appuser_%U.log \ schemas=APPUSER \ remap_schema=APPUSER:APPUSER \ remap_tablespace=OLD_DATA:NEW_DATA,OLD_IDX:NEW_IDX \ parallel=4 \ exclude=statistics \ transform=segment_attributes:n \ table_exists_action=skip \ metrics=y

逐个说下取舍理由。parallel=4不是拍脑袋定的,经验公式是取 CPU 核数的一半、转储文件数、以及目标库 IO 承受能力的较小值。如果导出时只生成了 2 个 dmp 文件,导入并行度设 8 也没用,反而多消耗资源。exclude=statistics是为了绕开统计信息导入引发的 ORA-01403,导入完成后用DBMS_STATS重新收集更干净。transform=segment_attributes:n的作用是丢弃源端的物理段属性(比如初始区大小、pctfree),让对象按目标库的默认表空间属性创建,能避免一批由存储参数不兼容导致的失败。table_exists_action=skip适合首次导入,如果表已存在要覆盖数据,得改成truncate或append,但注意truncate是破坏性的,务必确认。

metrics=y这个参数容易被忽略,它会在日志里输出每个对象类型的耗时和占比,事后复盘时非常有价值。我第一次用它是为了搞清楚到底哪一步最慢,结果发现索引重建占了总时间的四成,后面就调整成先导数据、事后并行建索引,整体时间缩短了近三分之一。

4.3 出错之后:分批补导与 sqlfile 预演

导入报错之后,别急着从头再来。正确的做法是先判断失败对象的范围,再决定补导策略。如果失败对象集中在少数几类,用INCLUDE精准补导效率最高:

# 只补索引 impdp system/******@TARGETDB directory=DP_DIR dumpfile=appuser_%U.dmp \ logfile=fix_idx.log schemas=APPUSER include=index \ table_exists_action=skip parallel=4 # 只补视图和触发器 impdp system/******@TARGETDB directory=DP_DIR dumpfile=appuser_%U.dmp \ logfile=fix_view.log schemas=APPUSER include=view include=trigger

补导之前有个步骤我强烈建议做:用sqlfile参数预演。它只把 DDL 写到一个文本文件里,不实际执行,你可以先看看数据泵打算建什么、语法在目标库是否成立、有没有引用不存在的表空间。这一步在跨版本搬迁时几乎是必做的,能提前发现大量问题:

impdp system/******@TARGETDB directory=DP_DIR dumpfile=appuser_%U.dmp \ sqlfile=preview_ddl.sql logfile=preview.log schemas=APPUSER

生成出来的preview_ddl.sql可以直接打开看,也可以 grep 出所有TABLESPACE关键字,确认有没有漏掉的重映射。我有一次就是靠这个发现源端有个USERS_OLD表空间没在重映射列表里,提前补上,省了一次失败重跑。

还有一种情况需要单独处理:作业中断后主表残留。清理动作是DROP TABLE "SYSTEM"."SYS_IMPORT_FULL_01" PURGE;,但删之前确认作业确实不在跑了,用dba_datapump_jobs看状态。如果状态是 EXECUTING,说明后台还在跑,这时候删表会引起更麻烦的问题,正确做法是attach上去执行kill_job。

4.4 校验:别只看“成功”两个字

作业显示成功,不等于数据可用。我见过太多“导入成功但业务跑不起来”的案例。校验环节我固定跑四组检查。

第一组,对象数量对账。把源端和目标端的对象数按类型比对:

SELECT object_type, COUNT(*) FROM dba_objects WHERE owner = 'APPUSER' AND object_name NOT LIKE 'BIN$%' GROUP BY object_type ORDER BY 1;

两边结果拉出来一对比,差在哪个类型一目了然。差几个通常正常(比如回收站对象、临时对象),差几十个就要警觉。

第二组,无效对象扫描。前面提过的dba_objects查 INVALID,配合DBMS_UTILITY.COMPILE_SCHEMA跑一遍,跑完再查一次。如果还有残留,逐个看依赖。

第三组,行数抽查。挑几张核心业务表,源端和目标端各跑一次SELECT COUNT(*)。全表 count 在超大表上代价高,可以改用SELECT COUNT(*) FROM t WHERE ROWNUM <= 1000000做抽样,或者直接看数据泵日志里每个表的行数统计(日志里导完每张表都会打印行数),拿日志里的数字跟目标端实际数字对。

第四组,序列当前值。这是最容易被忽略的一项。数据泵导入序列时只导定义,不带当前值,如果你的业务依赖序列生成主键,导入后序列从初始值开始,很可能跟已有数据撞主键。处理办法是导入完成后手工把序列推进到MAX(ID)+1:

-- 以 APPUSER.ORDER_SEQ 为例,先查当前最大值 SELECT MAX(order_id) FROM appuser.order_hdr; -- 再调整序列 ALTER SEQUENCE appuser.order_seq INCREMENT BY 50000; SELECT appuser.order_seq.NEXTVAL FROM dual; ALTER SEQUENCE appuser.order_seq INCREMENT BY 1;

这里INCREMENT BY的跳变量要大于当前最大 ID 与序列当前值的差,保守一点直接给个足够大的数,走一次NEXTVAL再改回来。

5. 速查表与踩坑笔记

处理得多了,会发现同类问题反复出现。这一章把高频问题的判断路径整理成表,再补几条文档里不会写、但实际很管用的经验。

5.1 报错速查表

现象第一判断快速验证处置
日志有 ORA-39111,末尾 successful正常,无需处理看日志最后一行忽略
日志有 ORA-39111,末尾 completed with N error有其他真错误搜 ORA-39083 定位按对象类型处理
ORA-39083 + ORA-00942依赖对象缺失查 dba_objects 是否存在按依赖顺序补导
ORA-39083 + ORA-01652临时表空间不足查 TEMP 使用与大小扩 TEMP 或事后建索引
ORA-39083 + ORA-01950配额不足查 dba_ts_quotas加配额后补导
ORA-39083 + ORA-01403,对象为 STATISTICS统计信息导入失败看对象类型EXCLUDE=STATISTICS 重导
ORA-39083 + ORA-01403,对象为 VIEW/TYPE元数据依赖异常查依赖对象是否齐全补齐后单独补导
导入成功但业务报对象无效编译失败查 dba_objects 的 INVALIDCOMPILE_SCHEMA 后逐个修
作业表残留作业中断查 dba_datapump_jobs 状态kill_job 后 DROP 主表
序列值从 1 开始数据泵不导当前值对比最大 ID手工跳变序列

5.2 几条文档里不会写的经验

第一条,关于日志的阅读顺序。官方文档教你从前往后读,我建议倒着读。先看末尾的作业结论,再往上找最后一条 ERROR,往往一次就能定位到根因。因为数据泵的失败常常是雪崩式的:第一个错误引发后续几十个连锁错误,从后往前找会看到一大堆“受害者”,从第一个错误入手才能找到“肇事者”。

第二条,关于EXCLUDE=STATISTICS的取舍。很多人担心不导统计信息会影响执行计划,确实会。但导入过程中统计信息失败导致的 ORA-01403 更烦人,而且导入完成后重新收集统计信息本身是更规范的做法——源库的统计信息未必适合目标库的硬件和负载。所以我的默认动作是排除统计信息,导入完成后挑业务低峰期跑一次全 schema 收集,慢是慢点,胜在干净。

第三条,关于并行度与索引。并行导入的瓶颈往往不在数据,而在索引维护。我一般的做法是导入阶段EXCLUDE=INDEX加上EXCLUDE=CONSTRAINT,数据灌完之后再单独补索引和约束,然后ALTER INDEX ... NOPARALLEL把并行度改回来。这样做有两个好处:一是数据导入阶段快得多,二是索引建失败时影响范围小,重来成本低。有个前提要注意,跳过约束意味着数据里如果有重复值,建唯一约束时会失败,所以补约束那一步要留足时间。

第四条,关于 ORA-01403 的“假阳性”。有一类 ORA-01403 其实完全不影响结果,出现在某些内部对象或系统 schema 的处理上,作业最终仍然成功。判断标准还是那条:末尾是 successfully completed 就别管,是 completed with errors 就一定要查。我见过有人为了一个假阳性的 ORA-01403 折腾到半夜,最后发现业务一切正常。

第五条,别迷信“从头重跑”。数据泵导入 100GB 数据动辄几个小时,出点小错就重来是巨大的浪费。绝大多数报错都可以通过INCLUDE精准补导解决,只要把失败清单理清楚,补导往往十几分钟就完事。真正需要整体重跑的场景只有两种:数据一致性要求极高且已经脏了,或者失败对象太多、补导的复杂度超过重来。判断标准我一般定在失败对象占比 30% 以上才考虑重来。

最后分享一个我在实际搬迁中形成的习惯:每次导入结束,不管成功失败,都把那次的日志、主表导出结果、参数命令一起归档到一个目录里,按日期和场景命名。这件事看起来繁琐,但半年后再遇到类似问题时,翻出旧记录一对比,往往几分钟就能定位。数据泵的报错翻来覆去就那么些组合,真正稀缺的不是知识,是你自己踩过的坑留下的证据。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询