先问一个问题你最近一次做Oracle数据迁移或者逻辑备份用的还是老的exp/imp吗如果是的话这篇值得你花五分钟看完。我在生产环境摸爬滚打这些年见过太多从exp切到expdp数据泵之后一脸懵的同事——不是数据泵不好用而是没搞明白它的运行机制和参数组合。标题里导出用户、多用户、整个库、指定表这四个词恰恰是DBA日常里最常被问到的四类需求把某个用户的对象整体迁走、把多个业务用户打包、给整个库做逻辑备份、甚至只挑几张关键表出来。这四种场景看着简单但真用的时候目录权限、过滤语法、版本兼容、并行策略每一个都能让人卡半天。这篇文章就把它们完整过一遍顺带把那些绕不开的坑也讲清楚。1. 先搞清楚场景expdp和老的exp/imp到底差在哪1.1 expdp不是exp的升级版是另一套工具先说一个最常见的误区。很多人以为expdp就是exp的加强版参数差不多、用法差不多导出来的文件也能互相通用。实际上完全不是一回事。exp/imp是Oracle早期客户端工具进程跑在客户端机器上而数据泵Data Pump是Oracle 10g开始提供的服务器端工具expdp/impdp命令虽然看起来是命令行的样子但它真正干活的是数据库里的一组后台进程文件也是落在数据库服务器本地的目录对象对应路径上。这个差异直接决定了使用习惯用expdp的时候第一步不是写命令而是确认你有没有directory对象的权限。这个问题后面单独讲因为它是新手最容易翻车的地方。再看文件格式。exp导出来的dmpimp和impdp都能导入——虽然我不建议这么干因为很多老的exp文件里带着版本兼容问题但expdp导出来的dmp老的imp是绝对读不了的。数据泵的文件格式、元数据结构都跟exp那一套不一样它支持更细粒度的对象过滤、并行导出、压缩、加密这些都是老工具给不了的。所以既然要写导出这件事我建议你直接从expdp入门不要再花时间学exp了。1.2 四种导出场景的选型逻辑标题里说的四种场景对应到expdp其实就是几个参数的事需求描述核心参数典型命令片段导出单个用户SCHEMAS用户名expdp ... schemasscott导出多个用户SCHEMAS用户1,用户2expdp ... schemasscott,hr导出整个库FULLyexpdp ... fully导出指定表TABLES表名列表expdp ... tablesscott.emp参数看着简单但组合起来学问就大了。比如导出多个用户的时候这些用户之间可能有互相的约束关系导出整个库的时候要不要排除系统schema导出指定表的时候你到底是只要这几张表的数据还是报表结构、索引、触发器一起出这些细节会直接影响命令的参数设计。我个人做选型的时候通常会先问三个问题目标环境是什么版本要迁移的对象范围是哪些对数据一致性有没有要求版本决定要用什么样的兼容参数范围决定是用schemas还是tables还是full一致性要求决定要不要加flashback相关的参数。带着这三个问题去看下面的内容会顺很多。2. 目录准备directory对象和权限是数据泵的第一道门槛2.1 先建directory对象再谈导出很多新手第一次敲expdp命令的时候直接写expdp scott/tiger dumpfile/home/oracle/backup/exp.dmp logfileexp.log schemasscott然后系统哗哗报错。原因很简单expdp不认操作系统的绝对路径它只认识数据库里的directory目录对象。这个directory对象是一个数据库实体指向服务器文件系统上的某个路径相当于给路径套了一层数据库的壳。需要通过SQL来创建-- 用sys或者有create directory权限的用户执行 create directory dump_dir as /u01/backup;创建完成后还要把读写权限给到执行导出的用户grant read, write on directory dump_dir to scott;然后expdp命令里的directory参数填的才是dump_dir这个对象名而不是路径expdp scott/tigerorcl directorydump_dir dumpfileexp.dmp logfileexp.log schemasscott这里要特别注意路径是数据库服务器上的本地路径不是你自己电脑上的路径。哪怕你在本地客户端敲的expdp命令文件最终还是落到服务器上。所以常见的工作流是先在服务器上导好再把文件从服务器拉回来。远程直接导出到本地的想法趁早放弃数据泵不支持这种做法。2.2 权限授予与安全建议再往深一层说。directory对象创建之后权限管理也要跟上。默认情况下普通用户建不了directory需要DBA授予CREATE ANY DIRECTORY权限。但实际生产环境我一般不会给业务账号授这个权限而是统一由DBA建好目录对象再按需授权。-- DBA创建统一备份目录 create directory dmp_bak as /u01/dmp_bak; -- 给需要做导出的用户授权只读或读写按需 grant read on directory dmp_bak to scott; grant write on directory dmp_bak to scott;如果只是需要读取别人导出的dmp文件做导入那就只授read权限不给write。这样即便账号被攻破也不至于能往服务器任意写文件。还有一个细节经常被忽略操作系统层面的权限。directory对象指向/u01/dmp_bak但Oracle数据库进程是以oracle操作系统用户跑的如果这个目录属主不是oracle或者oracle用户对目录没有写权限数据泵还是会报错。所以建目录的时候建议用oracle用户来创建保证属主正确mkdir -p /u01/dmp_bak chown oracle:oinstall /u01/dmp_bak chmod 775 /u01/dmp_bak这套组合是每次数据泵作业前的固定动作检查完再往下走能省掉后面90%的报错排查时间。3. 导出单个用户和多个用户schemas参数的高频用法与控制粒度3.1 单用户导出的完整命令拆解单个用户或者说单个schema导出是日常用得最多的一种场景。开发给测试导数据、修复环境、准备升级都会用到。Oracle里用户和schema基本可以划等号——一个用户登录后它名下所有表、索引、视图、存储过程、序列合起来就是它的schema。所以导用户实质上就是在导这个schema里的全部对象。最标准的命令长这样expdp system/managerorcl \ directorydump_dir \ dumpfilescott_full.dmp \ logfilescott_full.log \ schemasscott \ version19.1 \ compressionall参数拆开解释一下。schemasscott指定要导出的用户dumpfile是生成的文件名建议加上用户名或业务标识免得导多了分不清logfile最好每次都写不然排查问题的时候连日志都没有compressionall压缩导出内容能省不少磁盘空间和传输时间version19.1是版本兼容参数源库是19c或更高的时候如果目标环境是11g或者12c务必加上对应版本号否则导入的时候很容易报版本不兼容的错误。生产环境导出的时候还有两个参数值得关注。一个是flashback_scn或flashback_time用来指定一个历史时间点或SCN让导出期间的数据保持一致性快照。另一个是estimate_onlyy它只估算导出文件大小、不实际导出用来判断磁盘空间够不够非常实用expdp system/managerorcl \ directorydump_dir \ dumpfileestimate.dmp \ schemasscott \ estimate_onlyy从输出里能看到预估的MB数再结合df -h看磁盘剩余心里就有数了。3.2 多用户导出一个schemas参数搞定多用户导出的命令其实就是把schemas参数里追加多个用户用逗号分隔expdp system/managerorcl \ directorydump_dir \ dumpfilemulti_schema.dmp \ logfilemulti_schema.log \ schemasscott,hr,oe这里有个限制要先说明普通用户只能导自己的schema如果想去导别人的schema需要EXP_FULL_DATABASE角色或者对应的系统权限。所以实际生产里多用户导出大多用system账号或者专门的dba账号来执行。还有一个容易忽略的地方多个用户之间存在外键关联。比如scott用户下的某张表引用了hr用户下的主键表如果只把scott导走目标库里hr的表不存在导入时会报外键相关的错误。我的经验是导多用户之前先理清业务依赖把强关联的用户放同一个schemas列表里一起导别拆开。3.3 用exclude参数精细化控制有时候我不想把整个用户全导出去比如某个用户名下有一张巨无霸日志表光它就能让导出变慢好几倍。这种时候可以用exclude参数把指定对象排除掉expdp system/managerorcl \ directorydump_dir \ dumpfilescott_nolog.dmp \ logfilescott_nolog.log \ schemasscott \ excludetable:IN (T_LOG,T_AUDIT)注意这个语法细节exclude后面跟的是对象类型和过滤条件表名要放在单引号里外层用双引号包住。在Linux shell下这么写没问题但到了Windows的CMD或PowerShell下转义规则又不一样经常出幺蛾子。我的建议是过滤条件复杂的时候干脆把参数写到一个文件里用parfile参数指定它expdp system/managerorcl parfileexp_par.txt文件内容directorydump_dir dumpfilescott_nolog.dmp logfilescott_nolog.log schemasscott excludetable:IN (T_LOG,T_AUDIT)参数文件的好处是既避免了shell转义问题又能把一长串参数整理得清清楚楚方便复用和版本管理。4. 整个库导出fully背后的取舍与系统表处理4.1 fully之前先想清楚这几个问题导出整个库听起来最简单一个fully就完事但实际它是最需要谨慎的一类操作。因为全库导出的对象量非常大包括所有用户的数据、所有系统表、存储过程、序列、同义词、物化视图甚至包括一些底层元数据。如果只是想要业务数据的全量备份直接fully反而会导出一堆系统对象目标库再导入的时候还容易冲突。我一般在以下几种情况才会用整库导出一是数据库迁移两边环境版本接近想把所有业务用户一次性搬过去二是做逻辑备份的兜底方案三是库很小、用户很少直接全库导出然后导入到新环境最省事。4.2 整库导出的常用排除与版本控制整库导出的命令本身不复杂expdp system/managerorcl \ directorydump_dir \ dumpfilefull_db.dmp \ logfilefull_db.log \ fully \ version19.1 \ excludeschema:IN (SYSTEM,XDB,ORDSYS,MDSYS,OLAPSYS,EXFSYS,WMSYS,DBSNMP,OUTLN,CTXSYS,APPQOSSYS,ORDDATA)这个exclude列表是我根据自己的实战经验积累的把那些系统自带的schema排除掉。理由很简单这些schema里的对象通常都是Oracle内部功能用的业务迁移用不到而且它们往往带有版本特性、补丁状态相关信息导入到目标库容易造成冲突。当然不同的Oracle版本对系统schema的要求不一样12c以后还引入了PDB、CDB相关的对象所以具体排除列表要结合目标环境调整。还有一点12c之后如果用的是多租户架构在PDB里执行fully导出的是当前PDB而不是整个CDB实例。如果要从CDB层面全库导出需要在root容器里操作而且需要考虑PDB是否处于mount或open状态。这块内容展开讲又是一大篇这里先提醒一句免得你发现导出来的文件里少了一大半数据却不知道原因。4.3 整库导入时候的反向注意事项导出的另一半是导入。全库导出文件在导入的时候我最担心的是权限、同义词和公共同义词的指向。有些业务表在用户A下面但是用户B建了公有同义词指向它导到新库之后如果能保证用户A、用户B都存在问题不大但如果用户本身不存在导入会直接报错。所以整库导出迁移的实践里我建议同步把用户创建脚本也准备好先建用户再导数据顺序别反了。数据泵导入的时候impdp默认不会帮你去create user除非你用了transformsegment_attributes之类的参数配合另外的选项。最稳的做法是导全库的时候顺便用数据泵把用户元数据一起带上导入端加create database之类的逻辑但这个比较进阶日常用还是先把用户建好再走导入流程。5. 指定表导出include/exclude/query的组合玩法5.1 tables参数的两种用法指定表导出是开发提需求时最常碰到的场景帮我把生产环境上XX表的数据导出来分析一下。最简单的方式就是tables参数直接指定可以带schema点名expdp system/managerorcl \ directorydump_dir \ dumpfiletables_emp.dmp \ logfiletables_emp.log \ tablesscott.emp同时导多张表用逗号分隔expdp system/managerorcl \ directorydump_dir \ dumpfiletables_core.dmp \ logfiletables_core.log \ tablesscott.emp,scott.dept,hr.locations注意两个坑。第一个tables参数如果不带schema默认就是当前登录用户的schema所以最好都显式写schema.表名。第二个表名在数据泵里默认会转成大写如果表是用小写或混合大小写建的加了引号创建的那种过滤和匹配时要处理特殊情况否则会报table not found。5.2 include参数白名单方式的灵活过滤跟tables直接列表名相比include参数更强大它可以按对象类型来做白名单过滤。比如只想导出scott用户下的所有表并且只要emp、dept、salgrade这三张expdp system/managerorcl \ directorydump_dir \ dumpfileinclude_tables.dmp \ logfileinclude_tables.log \ schemasscott \ includetable:IN (EMP,DEPT,SALGRADE)注意这里用的是schemasscott配合include过滤而不是直接写tables...。区别在于tables参数还会自动带上表相关的索引、约束、触发器这些依赖对象而includetable则是严格按照对象类型来过滤——如果只写了table类型那么索引、约束、授权、触发器都不会被导出拿到目标库之后表是光秃秃的没有约束和索引。这到底好不好取决于你的需求。如果只要数据那无所谓如果要把表完整搬过去还是用tables更省事。5.3 query参数只导满足条件的数据更细一级的需求是表可以整张导但我只要其中一部分数据。比如只要emp表里部门编号为10的数据expdp system/managerorcl \ directorydump_dir \ dumpfileemp_dept10.dmp \ logfileemp_dept10.log \ tablesscott.emp \ queryscott.emp:WHERE deptno10query参数的执行逻辑是在导出阶段就对源表做数据过滤条件写在表名后面的冒号里。多张表各自带过滤条件也是可以的比如queryscott.emp:WHERE deptno10,scott.dept:WHERE deptno IN (10,20)这里必须提醒一件事query参数只对表数据生效对元数据不生效。而且如果目标表上有关联的外键约束你只导了部分数据导入时很可能因为缺父表数据导致约束校验失败。所以用query做数据抽取之前先想清楚数据间的关联关系。5.4 content参数只要结构还是只要数据最后一个高频参数是content它控制导出内容的类型contentall默认值结构数据都导contentdata_only只要数据不导建表语句contentmetadata_only只要结构不导数据只导数据比较常见于目标库表结构已经建好了只需要把生产库的数据灌进去。只导结构则常见于先在新环境把表结构建好再通过别的方式同步数据。# 只导数据 expdp system/managerorcl \ directorydump_dir \ dumpfileemp_data.dmp \ tablesscott.emp \ contentdata_only # 只导结构 expdp system/managerorcl \ directorydump_dir \ dumpfileemp_meta.dmp \ tablesscott.emp \ contentmetadata_only使用contentdata_only导出的文件导入之前必须确保目标表的表结构已经存在否则impdp会直接报错。这一点在写自动化脚本的时候特别容易漏。6. 踩坑复盘几个最常遇见的错误和排查思路6.1 ORA-31623 / ORA-39002版本和作业状态引发的连锁报错ORA-31623和ORA-39002是我在实际运维中收到过最多的问题。ORA-31623通常提示的是作业参数无效ORA-39002则提示操作无效两者经常一起出现。排查思路是这样的先查数据库版本和数据泵版本是否匹配如果源库版本高于数据泵客户端版本导出的文件目标库可能不认再看dmp文件头部版本信息如果文件是19c导的、目标库是11g报错就是必然的解决办法是用version参数指定低版本导出。另外数据泵作业偶尔会因为前一次操作中断而残留在数据库里导致后续作业起不来。可以查询并清理-- 查看当前数据泵作业 SELECT * FROM dba_datapump_jobs; -- 如果发现残作业用impdp或expdp attach上去做stop/kill处理 expdp system/managerorcl attachSYS_IMPORT_FULL_016.2 找不到dumpfile导出文件到底落在哪还有一种高频问题是用户问我文件导出成功了但路径在哪找不着。这往往是因为对directory对象的路径不了解。expdp最终输出的路径以directory对象指向的服务器路径为准而不是你命令行里写的工作目录。查询方式select owner, directory_name, directory_path from dba_directories where directory_name DUMP_DIR;拿到路径后再去服务器上用ls -l确认文件存在。很多人在本地电脑上找半天是因为不知道文件根本没落在客户端。6.3 字符集与目标环境不一致的应对导出导入过程中字符集不一致是个慢性病。源库字符集是ZHS16GBK目标库是AL32UTF8数据导过去之后中文可能变成乱码或者问号。在导出前先确认两边的字符集select value from nls_database_parameters where parameterNLS_CHARACTERSET;如果有差异能改的话尽量对齐改不了的话导入后要做数据抽样验证。这个问题的根因是逻辑备份本质上导出的是数据内容字符集转换发生在导入阶段而数据泵对字符集转换的支持并不总是尽如人意的尤其是SQL里硬编码的字符串、注释、存储过程源码里带的中文都可能在转换过程出问题。6.4 两个提高效率的小习惯最后分享两个我自己的使用习惯。第一个是并行度导出和导入时都可以设置parallel4这样类似的参数配合dumpfileexp_%U.dmp的多文件方式能显著缩短大表导出时间。但注意parallel不是越大越好还要看服务器的CPU和IO能力我一般先在4到8之间试。第二个是保留历史日志。数据泵的logfile默认每次导出覆盖同名文件我建议在文件名里带上日期dumpfileexp_$(date %Y%m%d).dmp logfileexp_$(date %Y%m%d).log这样每次导出的文件不会互相覆盖出了问题还能追溯是哪一天导的、当时用了什么参数。代价只是多几个文件却能在恢复数据时省下大把时间。数据泵这套工具说简单也简单无非是参数组合的问题说复杂也复杂版本、字符集、权限、依赖关系、作业残留每一个都能让人踩到怀疑人生。从我个人的经验看最靠谱的成长路径就是反复在一套测试环境上练习把单用户导出多用户导出整库导出指定表这四类场景各自跑通一遍再故意制造一些报错去排查见过足够多的异常输出到了生产环境才能不慌。希望这篇文章能让你少走几步弯路哪怕只是避开了找不到file和版本不兼容这两个大坑也算值了。