impdp 卡住 1.5 小时:加了 2G UNDO、连 kill 都杀不动
DBA实战笔记 · Oracle Data Pump 排障专题
环境:Oracle 11g+ impdp 导入分区大表
关键词:ORA-30036、UNDO 表空间、AUTOEXTEND、ORA-00028、kill 无效、ORA-39065
说明:为保护生产环境,主机名与路径已脱敏
一、事件经过:三个反直觉的现象
背景先交代清楚:目标表数据量约 1.6 亿行,此前已经导入约 1 亿行。本次利用切换窗口补导剩余约 6000 万行,采用 TABLE_EXISTS_ACTION=APPEND 方式追加导入。
导入进行中,worker(DW00)卡住不动。半小时后监控到 UNDOTBS1 使用率 99.92%,紧急加了一个 2G 数据文件:
ALTER TABLESPACE UNDOTBS1
ADD DATAFILE '/u01/oradata/twodb/undotbs02.dbf'
SIZE 2G AUTOEXTEND ON NEXT 1G MAXSIZE 30G;
文件加上后,作业依旧卡住;又观察了一个多小时,手工 kill DW00——kill 竟然长时间不生效。最终作业报错退出,日志尾部:
ORA-39014: One or more workers have prematurely exited. ORA-31671: Worker process DW00 had an unhandled exception. ORA-00028: your session has been killed ORA-30036: unable to extend segment by 8 in undo tablespace 'UNDOTBS1' ORA-39065: unexpected master process exception in MAIN ORA-06533: Subscript beyond count
整个过程中有三个现象怎么都对不上"undo 不足"这个简单解释:
- 加了 2G 文件,undo 还是满;
undotbs02.dbf从头到尾停在 2G,一次都没自增;- 卡住期间,导其他表完全正常。
二、逐个还原真相
2.1 undo 99.92%,但数据库并不是"没空间"
事后核对三条证据:磁盘剩余 57%、alert log 无任何 I/O 错误、卡住期间其他表照常导入。磁盘满和数据库级 undo 枯竭都排除。
99.92% 的构成才是关键:被吃满的 32G,几乎全是大表导入事务自己的 ACTIVE undo。ACTIVE 状态的 undo,任何会话都抢不走——空间饥饿是"结构性"的,不是"全局性"的。
那 2G 新文件去哪了?它够小表们转圈:小事务的 undo 分配、过期、复用,2G 永远不会被压到上限,自然一次都不会触发 AUTOEXTEND。文件不涨不是故障,是"没有会话需要它涨"。
2.2 卡住的真面目:不是挂死,是"活跃地极慢"
卡住时抓的 v$session:
SID SERIAL# EVENT WAIT_CLASS 3 83 db file sequential read User I/O 2227 655 wait for unread message on broadcast channel Idle
SID 3 就是 DW00:持续单块读、SECONDS_IN_WAIT=0——它在干活(分区表插入 + 索引维护的单块读风暴),不是在等锁,也不是在等 undo 段。全程没有任何 enqueue 等待。SID 2227 是 Data Pump 主控的典型空闲等待,主控本身没病。
教训很直接:对着"99.92%"扩空间之前,先看一眼会话到底在等什么。
2.3 为什么 kill 杀不动
alter system kill session 只是打标记。会话要走到安全点、处理完自身事务收尾才会消失;而这个会话的单事务体量巨大、undo 被 ACTIVE 顶死,收尾推进极慢——于是会话迟迟不死,ORA-00028 在一个多小时后才真正落到异常栈里。
2.4 错误链的完整解读
ORA-30036出现在KUPD$DATA内部:是 worker 自己的 DML 申请 undo extent 失败。32G + 2G 依然追不上几十 G 的 undo 需求,失败是必然,只是时间问题;ORA-00028:手工 kill 最终生效了;ORA-39065 + ORA-06533(Subscript beyond count):Data Pump 主控的已知 bug,worker 异常死亡时主控消息处理崩溃,是"临终症状",不是新根因;- 日志里大量
0 rows imported的空分区,只是作业终止前的进度清单,不用过度解读。
三、常见误区:是不是 kill 把 undo 吃光了?
复盘时最容易得出这个结论,但因果链要倒过来看:
| 疑似因果 | 判定 |
|---|---|
| kill 导致 undo 不够 | 错。99.92% 在 kill 前一个多小时就出现了 |
| 回滚 30G 事务要吃 30G 新 undo | 错。回滚是应用已有 undo 记录恢复数据块,只产生少量递归 undo |
| 回滚期间 undo 被 ACTIVE 锁死不放 | 对。这才是加文件无效、会话不死的直接原因 |
| kill → ORA-00028 → 主控 bug → 作业终止 | 对。这是错误链的收场方式 |
一个简单的验证逻辑:如果真是"回滚把空间吃光",undo 使用率应该在 kill 之后继续上涨;实际是 kill 前就已顶格、kill 后长期钉死——这是"ACTIVE 锁死"的特征,不是"回滚新增消耗"的特征。
结论:即使不 kill,这次导入也注定失败。kill 没有制造危机,只是让既有危机提前收场,并改变了失败的形式。
四、下次怎么办
应急动作:
- 卡住先看等待事件,判断会话是"在等"还是"在慢":
SELECT sid, event, wait_class, seconds_in_wait, blocking_session
FROM v$session WHERE program LIKE 'DW%';
- 真是 undo 不足,扩容一次到位,别补 2G 小文件——
NEXT 1G的逐次增长在 undo 风暴面前永远追不上消耗:
-- 方式一:老文件直接扩(注意表空间内文件不必对称)
ALTER DATABASE DATAFILE '/u01/oradata/twodb/undotbs01.dbf' RESIZE 60G;
-- 方式二:新加一个大文件,一次给足
ALTER TABLESPACE UNDOTBS1
ADD DATAFILE '/u01/oradata/twodb/undotbs03.dbf'
SIZE 20G AUTOEXTEND ON NEXT 4G MAXSIZE 60G;
执行前先 df -h 确认文件系统余量,别在磁盘上翻车;
3. kill 不动时,确认 v$session.status 是否 KILLED(等自身回滚完成),紧急情况升级为 alter system disconnect session ... immediate。
治本措施:
- 单分区 = 单事务。导入前按分区数据量估算 undo 需求,预扩容或切换 undo 表空间;
- 大表导入:
ACCESS_METHOD=DIRECT_PATH、先导数据后建索引/约束、降低 PARALLEL; - 重导前确认上一次尝试已彻底回滚干净(
dba_datapump_jobs+v$transaction无残留); - 应用最新 RU,修复主控 ORA-39065/ORA-06533 bug。
关于 ACCESS_METHOD=DIRECT_PATH,多说两句:
它让 impdp 的表数据加载走直接路径——绕过 Database Buffer Cache,数据块格式化后直接写入数据文件,数据部分的 undo 和 redo 都大幅减少。
收益:undo 峰值低(对本文这种场景正是对症)、加载速度通常更快。
但有明确的限制和代价:
- 表上有触发器、聚簇表、部分 LOB 类型等场景会自动退化为 conventional path,什么都不省;
- 索引维护和约束校验的 undo 不会省,大表先导数据后建索引才是完整解法;
- 直接路径加载期间对表持有排他效果(与
APPEND语义类似),加载窗口内不能有并发 DML,只适合切换窗口这类业务静默场景; - 本次用的
TABLE_EXISTS_ACTION=APPEND与它不冲突,组合使用是补导场景的常见做法——但 APPEND 只减少数据块层面的开销,undo 依然随行数线性增长,6000 万行的追加照样要按几十 G 级别准备 undo。
专注于 Oracle / MySQL / PostgreSQL / 达梦等主流数据库的运维实战与架构分享,欢迎关注,一起做靠谱的数据库人。
欢迎赞赏支持或留言指正





