简介:这是一款基于Java编写的Oracle数据库导入导出桌面工具,面向数据库运维人员、开发工程师及对命令行操作不熟悉的技术用户,用于解决数据迁移、备份恢复、离线分析等场景下的导入导出需求。压缩包共198个文件,约45.31MB,以68个dll、25个jar、24个properties、22个exe及字体、证书、配置等支持文件为主,构成完整的Java运行与图形界面环境,其中jre为程序运行所必需,不建议删除。工具支持表空间、用户密码、数据文件路径、表或模式、压缩选项等参数设置,并配套操作说明文档,涵盖启动连接、参数配置、注意事项与常见错误处理。已有1985人学习下载。借助图形化界面,读者可简化expdp、impdp及sqlplus等传统方式的操作流程,同时了解分区策略、索引优化与分批导入等性能与安全实践,提升Oracle数据库日常管理效率。
1. Oracle 数据库导入导出工具:从 exp/imp 到数据泵的选型与落地
一次给客户做数据迁移,源库是 11g,目标库是 19c,我图省事直接用了老 exp 导出,结果 80GB 的表空间导到一半报错,字符集还差点对不上。那次之后我才认真把 Oracle 数据库导入导出工具这条线捋清楚:它到底分几代、什么场景用哪个、参数怎么设才不翻车。Oracle 的导入导出工具主要分两代,老一代是 exp/imp,新一代是数据泵 expdp/impdp,两者不通用,导出文件格式也不兼容。选错了工具,轻则速度慢十倍,重则直接报错中断。这篇面向的是需要做库间迁移、备份归档、部分表同步的 DBA 和后端工程师,从工具选型讲到参数配置,再到踩坑排查,尽量让你照着就能跑通。
2. 先搞清楚 exp/imp 和数据泵到底差在哪
2.1 两代工具的底层机制差异
exp/imp 是客户端工具,导出时数据要经过客户端进程中转,再写到文件。这意味着导出 100GB 数据,客户端机器上会先落一份临时数据,网络传输和磁盘 IO 都是瓶颈。数据泵 expdp/impdp 是服务端工具,作业在数据库服务器上直接执行,数据从数据文件读到目录对象指向的路径,不经过客户端。这是两者性能差距的根本原因。
另一个关键差异是并行能力。exp/imp 是单线程的,一张大表只能一个进程慢慢导。数据泵支持 PARALLEL 参数,可以开多个 worker 进程同时导出不同表甚至同一张表的不同分区。我实测过一张 200GB 的分区表,exp 跑了将近 4 小时,expdp 开 4 个并行度只用了 50 分钟。
还有一点容易被忽略:exp/imp 在 12c 之后虽然还能用,但 Oracle 已经不再对它做功能增强,很多新特性比如可传输表空间、压缩导出,只有数据泵支持。如果你还在用 exp 做日常备份,建议尽早切到数据泵。
2.2 什么场景选哪个工具
不是所有场景都无脑上数据泵。下面这张表是我自己总结的选型参考:
| 场景 | 推荐工具 | 理由 |
|---|---|---|
| 跨版本迁移(11g→19c) | expdp/impdp | 支持版本兼容参数,exp 高版本导低版本容易出问题 |
| 单表快速导出 | exp 或 expdp | 小表 exp 更省事,不用建目录对象 |
| 全库备份归档 | expdp | 支持并行、压缩,速度快 |
| 客户端无服务器权限 | exp | expdp 需要数据库目录对象权限 |
| 部分数据按条件导出 | expdp | 支持 QUERY 参数,exp 的 QUERY 限制多 |
| 异构平台迁移 | 数据泵+可传输表空间 | exp 不支持跨平台字节序转换 |
选型时还要注意一点:expdp 导出的文件只能用 impdp 导入,不能混用。我见过有人拿 expdp 导出的 .dmp 文件用 imp 去导,直接报 "not a valid export file"。
2.3 数据泵的核心组件:目录对象和作业
数据泵依赖两个东西:DIRECTORY 对象和作业(Job)。DIRECTORY 是数据库里的一个逻辑名,指向服务器文件系统上的一个真实路径。创建语法:
-- 创建目录对象,指向服务器上的 /data/dump 路径 CREATE OR REPLACE DIRECTORY dpdir AS '/data/dump'; -- 授权给操作用户 GRANT READ, WRITE ON DIRECTORY dpdir TO scott;这里有个坑:路径必须是数据库服务器上的路径,不是你客户端机器的路径。很多人第一次用 expdp 会搞混这一点,以为在自己电脑上建个目录就行。另外 Oracle 的目录对象名是大小写不敏感的,但路径字符串是大小写敏感的,Linux 下写错大小写会报 ORA-39002。
作业是数据泵执行的最小单元。每次 expdp/impdp 调用都会在数据库里创建一个作业,可以通过DBA_DATAPUMP_JOBS视图查看运行状态。作业名可以用 JOB_NAME 参数指定,不指定的话 Oracle 自动生成一个类似 SYSTEM_EXPORT_FULL_01 的名字。作业运行期间如果中断,可以用ATTACH参数重新挂上去看进度,这个后面会细讲。
3. expdp/impdp 实操:从建目录到导入验证
3.1 导出:全库、按 Schema、按表三种模式
数据泵导出有五种模式:FULL、SCHEMA、TABLE、TABLESPACE、TRANSPORTABLE。日常用得最多的是前三种。
全库导出:
# 全库导出,开4个并行度,压缩元数据和数据 expdp system/password@orcl \ DIRECTORY=dpdir \ DUMPFILE=full_%U.dmp \ LOGFILE=full_exp.log \ FULL=Y \ PARALLEL=4 \ COMPRESSION=ALL \ JOB_NAME=full_exp_job按 Schema 导出:
# 导出 scott 和 hr 两个 schema expdp system/password@orcl \ DIRECTORY=dpdir \ DUMPFILE=scott_hr_%U.dmp \ LOGFILE=scott_hr_exp.log \ SCHEMAS=scott,hr \ PARALLEL=2 \ COMPRESSION=ALL按表导出:
# 导出 scott 下的 emp 和 dept 两张表 expdp system/password@orcl \ DIRECTORY=dpdir \ DUMPFILE=emp_dept.dmp \ LOGFILE=emp_dept_exp.log \ TABLES=scott.emp,scott.dept参数说明:DUMPFILE里的%U是通配符,当 PARALLEL 大于 1 时会自动生成多个文件,比如 full_01.dmp、full_02.dmp。如果不写 %U 又开了并行,Oracle 会报错。COMPRESSION=ALL同时压缩数据和元数据,能省 60% 到 80% 的空间,但会消耗 CPU,CPU 紧张的库可以只写COMPRESSION=DATA_ONLY或者不压缩。LOGFILE一定要指定,不然日志会写到默认路径,出问题不好找。
3.2 导入:REMAP_SCHEMA 和 REMAP_TABLESPACE 的用法
导入最常见的需求是换 schema 名和换表空间。比如从测试库导出的 scott 用户数据,要导入到生产库的 app 用户下:
# 导入时重映射 schema 和表空间 impdp system/password@orcl \ DIRECTORY=dpdir \ DUMPFILE=scott_hr_%U.dmp \ LOGFILE=scott_hr_imp.log \ REMAP_SCHEMA=scott:app \ REMAP_TABLESPACE=users:app_data \ PARALLEL=4 \ TABLE_EXISTS_ACTION=SKIPREMAP_SCHEMA=scott:app的意思是源 schema 的 scott 映射到目标的 app。可以写多组,用逗号隔开。REMAP_TABLESPACE同理。TABLE_EXISTS_ACTION有四个值:SKIP 跳过已存在的表,APPEND 追加数据,TRUNCATE 先清空再插入,REPLACE 删表重建。生产环境我一般用 SKIP,确认没问题再手动处理冲突表。
导入前有个必做动作:确认目标 schema 和表空间已经存在。impdp 不会自动建用户,如果 app 用户不存在,会报 ORA-39114。表空间也一样,REMAP 的目标表空间必须提前建好。
3.3 用 SQLFILE 参数先看 DDL 再决定导不导
这是我最推荐的一个习惯:导入前先用SQLFILE参数把 DDL 抽出来看一眼,不实际执行导入。
# 只生成 DDL 到 sql 文件,不实际导入 impdp system/password@orcl \ DIRECTORY=dpdir \ DUMPFILE=scott_hr_%U.dmp \ SQLFILE=preview_ddl.sql \ REMAP_SCHEMA=scott:app \ REMAP_TABLESPACE=users:app_data执行完去 dpdir 目录下看 preview_ddl.sql,里面包含了所有 CREATE TABLE、CREATE INDEX、ALTER TABLE 语句。这样你能提前发现:源库用了什么特殊数据类型、有没有分区表、索引建在哪个表空间、有没有 LOB 字段。我靠这个习惯躲过好几次翻车,比如有一次发现源库用了自定义类型,目标库没建,提前补上了。
3.4 查看作业进度和中断后重新挂载
数据泵作业跑起来后,想查进度有两个办法。一是看日志文件,tail -f盯着。二是用交互模式 attach 到作业上:
# 交互模式 attach 到正在运行的作业 expdp system/password@orcl ATTACH=full_exp_job进去之后可以敲STATUS看当前进度,CONTINUE_CLIENT回到日志输出模式,KILL_JOB杀掉作业。如果作业因为网络断开或者客户端退出而中断,作业本身还在服务器上跑,重新 attach 上去就行。但如果是KILL_JOB杀掉的,作业就没了,得重新导。
有个细节:attach 的时候如果作业已经跑完了,会提示 "Job is not running",这时候去DBA_DATAPUMP_JOBS里查状态,如果是 COMPLETED 就说明成功了,看日志确认。
4. 避坑与排查:那些年我踩过的导入导出坑
4.1 ORA-39002 和 ORA-39070:目录对象路径问题
现象:执行 expdp 报 ORA-39002 invalid operation 和 ORA-39070 unable to open the log file。
原因:九成是目录对象指向的路径在服务器上不存在,或者 Oracle 软件用户没有该路径的写权限。还有一种可能是路径写成了客户端路径。
解决:先登到数据库服务器上,确认路径存在且 oracle 用户可写。用ls -ld /data/dump看权限,必要时chmod 755或chown oracle:oinstall。然后查DBA_DIRECTORIES视图确认目录对象指向的路径对不对:
SELECT directory_name, directory_path FROM dba_directories WHERE directory_name = 'DPDIR';4.2 字符集不一致导致中文乱码
现象:导入后中文变成问号或者乱码。
原因:源库和目标库的字符集不一致。exp/imp 时代这个问题很常见,数据泵稍微好一点但也不是完全免疫。用NLS_CHARACTERSET查两个库的字符集:
SELECT parameter, value FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET';解决:如果源库是 ZHS16GBK,目标库是 AL32UTF8,导入时中文可能出问题。最稳妥的办法是导出前确认两边字符集一致,不一致的话在导入时设置NLS_LANG环境变量匹配源库字符集。但注意,NLS_LANG 只影响客户端显示,不改变数据库实际存储。真正要改字符集得用 CSALTER 脚本,风险很高,建议在测试库先验证。
4.3 导入时表空间不足报 ORA-01652
现象:impdp 跑到一半报 ORA-01652 unable to extend temp segment。
原因:目标表空间没有开自动扩展,或者磁盘满了。数据泵导入时会先建表再插数据,如果表空间不够,建表就失败。
解决:导入前先估算源数据大小,查源库的DBA_SEGMENTS:
SELECT owner, SUM(bytes)/1024/1024/1024 AS size_gb FROM dba_segments WHERE owner IN ('SCOTT','HR') GROUP BY owner;然后确认目标表空间有足够空间,或者加上DATAFILE ... AUTOEXTEND ON。临时表空间也要检查,导入大表时排序操作会占用大量 temp。
4.4 并行度开太高反而变慢
现象:PARALLEL=8 导出,结果比 PARALLEL=2 还慢。
原因:并行度不是越高越好。每个 worker 进程都要消耗 CPU 和 IO,如果服务器 CPU 核数不够或者磁盘 IO 到瓶颈了,开太多并行反而互相抢资源。另外如果导出的表数量少但每张表很大,并行度受限于表的数量,多出来的 worker 是空闲的。
解决:并行度一般设为 CPU 核数的一半到三分之二。导出前看一眼服务器负载,top和iostat都正常再开高并行。如果是单张大表,可以考虑用PARTITION_OPTIONS按分区并行。
4.5 导入后序列当前值不对
现象:导入完成后,序列的 nextval 还是从 1 开始,导致主键冲突。
原因:数据泵导入序列时,默认只导入序列定义,不导入当前值。除非导出时用了INCLUDE=SEQUENCE并且导入时没加EXCLUDE。
解决:导入后手动同步序列当前值。写个脚本批量处理:
-- 查询所有序列的当前值,生成 ALTER 语句 SELECT 'ALTER SEQUENCE ' || sequence_owner || '.' || sequence_name || ' RESTART START WITH ' || last_number || ';' FROM dba_sequences WHERE sequence_owner IN ('APP');把生成的语句在目标库执行一遍。或者导出时加INCLUDE=SEQUENCE参数,但注意这个参数在 11g 和 19c 的写法略有不同。
5. 进阶技巧:用 QUERY 和 INCLUDE 做精细化导出
5.1 QUERY 参数按条件导出部分数据
有时候不需要整张表,只要某个时间段或者某个状态的数据。数据泵的QUERY参数可以做到:
# 只导出 emp 表中 deptno=10 的数据 expdp system/password@orcl \ DIRECTORY=dpdir \ DUMPFILE=emp_dept10.dmp \ LOGFILE=emp_dept10.log \ TABLES=scott.emp \ QUERY="scott.emp:\"WHERE deptno=10\""注意 QUERY 的写法:表名和条件之间用冒号分隔,条件里的引号要转义。多个表可以写多个 QUERY 参数。这个参数在 exp 时代也有,但 exp 的 QUERY 只能用于单表,数据泵支持多表分别指定条件。
有个限制:QUERY 不能用于 FULL 模式,只能用于 TABLE 或 SCHEMA 模式。另外如果条件里用了子查询,性能可能很差,因为数据泵是在服务端逐行过滤的。
5.2 INCLUDE 和 EXCLUDE 控制对象类型
默认情况下,数据泵导出 schema 时会导出所有对象类型。但有时候你只想要表结构不想要数据,或者只想要存储过程不想要表:
# 只导出表结构和索引,不导数据 expdp system/password@orcl \ DIRECTORY=dpdir \ DUMPFILE=ddl_only.dmp \ LOGFILE=ddl_only.log \ SCHEMAS=scott \ CONTENT=METADATA_ONLY \ INCLUDE=TABLE,INDEX # 排除某几张表 expdp system/password@orcl \ DIRECTORY=dpdir \ DUMPFILE=exclude_tmp.dmp \ LOGFILE=exclude_tmp.log \ SCHEMAS=scott \ EXCLUDE=TABLE:"IN ('TMP_LOG','TMP_DATA')"CONTENT有三个值:ALL 导数据和元数据,DATA_ONLY 只导数据,METADATA_ONLY 只导元数据。INCLUDE和EXCLUDE不能同时用,会报错。INCLUDE 的语法是INCLUDE=对象类型:"条件",对象类型可以是 TABLE、INDEX、PROCEDURE、FUNCTION、VIEW、SEQUENCE 等。
5.3 用 PARFILE 管理复杂参数
参数一多,命令行就特别长,容易写错。数据泵支持把参数写到一个文件里,用PARFILE指定:
# parfile 内容示例:exp_full.par DIRECTORY=dpdir DUMPFILE=full_%U.dmp LOGFILE=full_exp.log FULL=Y PARALLEL=4 COMPRESSION=ALL JOB_NAME=full_exp_job然后执行:
expdp system/password@orcl PARFILE=exp_full.parparfile 里每行一个参数,不要写 expdp 命令本身。注释用 # 开头。这个方式特别适合把常用导出配置固化下来,下次直接改几个值就能复用。
5.4 验证导入结果的三个检查点
导入完成后别急着收工,至少做三个检查。第一,对比源库和目标库的对象数量:
-- 源库执行 SELECT object_type, COUNT(*) FROM dba_objects WHERE owner='SCOTT' GROUP BY object_type; -- 目标库执行同样的语句,对比结果第二,抽查几张关键表的行数:
SELECT 'EMP' AS tab_name, COUNT(*) FROM scott.emp UNION ALL SELECT 'DEPT', COUNT(*) FROM scott.dept;第三,检查无效对象:
SELECT object_name, object_type, status FROM dba_objects WHERE owner='APP' AND status='INVALID';如果有 INVALID 的存储过程或函数,用ALTER PROCEDURE ... COMPILE重新编译。我一般还会跑一遍应用的核心查询,确认数据能正常访问。
5.5 一个我坚持了多年的习惯
每次做导入导出,不管多急,我都会先在一个测试库上跑一遍完整流程。导出文件大小、导入耗时、报错信息,全部记录下来。正式操作时对着记录走,心里有底。另外,导出文件至少保留两份,一份在服务器上,一份拷到异地。曾经有一次服务器磁盘故障,导出文件全丢了,只能从备份重来,多花了整整一天。这些习惯看起来笨,但关键时刻能救命。希望帮到你。
本文还有配套的精品资源,点击获取