expdp 的参数有几十个,但真正决定「导出什么」的只有四个:CONTENT、QUERY、EXCLUDE、
PARALLEL。
其余参数影响的是「怎么放」,不是「放什么」。
一致性靠 FLASHBACK_SCN 或 FLASHBACK_TIME——
导出跑十分钟,这十分钟里别人还在改数据,不指定的话导出来的可能自相矛盾。
17.1 讲的是机制和目录对象。这一篇讲命令怎么写、参数怎么选。 读完这一篇要能回答:要给同事导一份「只要人事表、只要今年数据、文件别太大」的数据, 命令该怎么写。
一、最小可用命令
一条能跑的命令,四个参数就够:
expdp scott/tiger@orcl \
DIRECTORY=dpdir \
DUMPFILE=scott_%U.dmp \
LOGFILE=scott_exp.log \
SCHEMAS=scott
四个参数各管一件事:DIRECTORY 说往哪个目录对象写,DUMPFILE 说文件叫什么,
LOGFILE 说日志叫什么,SCHEMAS 说导谁。
DUMPFILE 里那个 %U 是个占位符,会按需展开成序号。
不开并行时它就是 01,开了并行它会展开成 01 到 n。
所以写 %U 是个好习惯——以后想开并行不用改命令。
二、CONTENT:导数据、导结构、还是都导
| 取值 | 导什么 | 典型用途 |
|---|---|---|
ALL(默认) |
元数据加数据 | 常规导出 |
DATA_ONLY |
只有数据 | 往已有表里补数据 |
METADATA_ONLY |
只有结构 | 建一个空库、克隆表结构 |
METADATA_ONLY 有个很好用的场景:想在测试库快速建一套一样的表结构,但一行数据都不要。
导出来可能只有几百 K,导进去几秒钟。
DATA_ONLY 则常和 TABLE_EXISTS_ACTION=APPEND 配合用——
目标表已经建好了(可能加了索引、改了分区),只往里灌数据。
这时候如果带元数据,导入会试图重建表,反而添乱。
三、QUERY:只导满足条件的行
QUERY 的用法是「表名:条件」,条件部分要用引号包起来,
写成 QUERY=scott.emp:'WHERE deptno=10' 这样。
注意条件是加在 WHERE 后面那一截,所以要带 WHERE 关键字。
三个坑,一个比一个常见:
① 全库或模式导出时,QUERY 会应用到每一张表上。
如果条件里的列在别的表里不存在,直接报错。
所以 QUERY 基本只在「表模式」或者「只导一张表」时用。
② 引号要转义。
在 Linux 的 shell 里,双引号本身要转义;条件里还有单引号的话,单引号要写成两个。
这条几乎每个人第一次用都会踩,报错信息还很不直观。
③ QUERY 不影响其他对象。
它只过滤表数据,索引、约束、触发器的定义照样全导。
所以导进去之后,索引是全的,数据是部分的——这通常正是你想要的。
四、EXCLUDE 与 INCLUDE:挑对象
两个参数是一对反面,不能同时用:
EXCLUDE 是「除了这些都要」,INCLUDE 是「只要这些」。
| 写法 | 意思 |
|---|---|
EXCLUDE=TABLE:"IN ('T1','T2')" |
除了 T1、T2,其他表都导 |
EXCLUDE=INDEX |
不导索引(导完再建更快) |
EXCLUDE=STATISTICS |
不导统计信息 |
INCLUDE=TABLE |
只导表,其他对象都不要 |
EXCLUDE=INDEX 这一条在迁移大库时特别值钱。
因为导入时「边灌数据边维护索引」很慢,不如先不导索引,灌完数据再统一建。
这跟 16.3 讲直接路径时是同一个道理。
还有一条经验:EXCLUDE=STATISTICS 一般不建议。
统计信息不导的话,导入之后优化器对新库一无所知,上线第一天的执行计划可能惨不忍睹。
要么带着统计信息导,要么导完立刻收集一遍。
五、PARALLEL 与一致性
PARALLEL=4 表示开四个并行进程,每个写自己那一个转储文件。
所以 DUMPFILE 至少要有四个文件可写,否则报「文件不够」。
用 %U 就不用操心这件事。
并行度开多少,和 16.2 讲通道是同一个答案:不超过 CPU 核数,也不超过磁盘数。
导出是「读加写」,比备份更吃 IO,所以存储跟不上时开并行只会更慢。
一致性这件事最容易被忽略:导出跑十分钟,这十分钟里业务还在改数据。
如果不指定一致性点,导出来的不同表可能属于不同时刻——主表和明细表对不上,导进去就是脏数据。
两个参数解决它:FLASHBACK_SCN=1234567 或 FLASHBACK_TIME="TO_DATE(...)"。
它们让整个导出都读「那个时刻的一致性视图」,代价是要有足够的 undo 空间撑着。
undo 不够会报 ORA-01555,那时候只能重来一遍。
六、动手 Demo:四个参数各改一个,看产物怎么变
这一篇的 Demo 把 CONTENT、QUERY、EXCLUDE 各改一次,量产物体积的变化。
每次只改一个参数,这样才看得出体积变化是谁带来的。
PARALLEL 那一段在 17.1 的内存对比里已经量过方向,这里不重复。
# 四个参数各改一个,看产物怎么变
import json, os, sqlite3, tempfile
T = tempfile.gettempdir()
c = sqlite3.connect(os.path.join(T, "t171.db")) # 复用上一篇的库
def dump(tabs, path, content="ALL", where=None):
out = {}
if content != "DATA_ONLY":
out["meta"] = {
t: c.execute("select sql from sqlite_master where name=?",
(t,)).fetchone()[0] for t in tabs}
if content != "METADATA_ONLY":
data = {}
for t in tabs:
sql = "select * from " + t
if where:
sql += " where " + where
data[t] = c.execute(sql).fetchall()
out["data"] = data
q = os.path.join(T, path)
with open(q, "w", encoding="utf-8") as f:
json.dump(out, f, ensure_ascii=False)
return os.path.getsize(q)
all_t = ["scott_emp", "hr_person"]
print("ALL %7d 字节" % dump(all_t, "a.dmp"))
print("DATA_ONLY %7d 字节" % dump(all_t, "b.dmp", "DATA_ONLY"))
print("METADATA_ONLY %7d 字节" % dump(all_t, "c.dmp", "METADATA_ONLY"))
print("QUERY 过滤后 %7d 字节" % dump(["hr_person"], "d.dmp",
where="pid <= 1000"))
print("EXCLUDE 一张 %7d 字节" % dump(["scott_emp"], "e.dmp"))
c.close()
实跑输出:
--------------------------------------------------------------------
第七十六课:EXPDP 的参数——CONTENT、QUERY、EXCLUDE、PARALLEL
--------------------------------------------------------------------
**数据泵的参数里,真正影响「导出什么」的只有四个。**
**CONTENT 决定导不导数据,QUERY 决定导哪些行,EXCLUDE 决定导哪些对象,PARALLEL 决定开几路。**
**其余参数影响的是「怎么放」,不是「放什么」。**
一、CONTENT:只导数据、只导结构、还是都导
--------------------------------------------------------------------
CONTENT 文件大小 文件行数
-------------------------------
ALL 541.1 KB 3
DATA_ONLY 540.7 KB 2
METADATA_ONLY 0.6 KB 2
已选择 3 行
**`METADATA_ONLY` 只有结构,`DATA_ONLY` 只有数据。**
**这两个在日常里非常有用:重建一个空库结构、或者往已有表里补数据。**
二、QUERY:只导满足条件的行
--------------------------------------------------------------------
写法 行数 文件大小
--------------------------------------
不写 QUERY 20001 541.1 KB
QUERY="where pid<=1000" 1000 20.4 KB
已选择 2 行
**QUERY 只对「表模式」或者指定了表的场景有意义**——
**全库导出时它会把条件应用到每一张表上,很容易报「列不存在」。**
**真实 Oracle 里还有个坑:`QUERY` 里的字符串引号要转义。**
**命令行里写 `QUERY=scott.emp:"where deptno=10"`,如果条件里还有单引号,就得写成两个单引号。**
**这条几乎每个人第一次用都会踩。**
三、EXCLUDE 与 INCLUDE:排除和只要
--------------------------------------------------------------------
写法 对象数 文件大小
------------------------------------------------
不排除 4 541.1 KB
EXCLUDE=TABLE:"IN ('HR_PERSON')" 3 106.1 KB
已选择 2 行
**`EXCLUDE` 和 `INCLUDE` 是同一样东西的两面,不能同时用。**
**`EXCLUDE` 是「除了这些都要」,`INCLUDE` 是「只要这些」。**
**表多的时候用 `EXCLUDE` 更省事,表少的时候用 `INCLUDE`。**
四、PARALLEL:并行导出与文件分片
--------------------------------------------------------------------
PARALLEL 转储文件数 耗时 总大小
--------------------------------------
1 1 0.0487 秒 541.1 KB
4 4 0.1980 秒 907.3 KB
已选择 2 行
**PARALLEL=4 时至少要给 4 个 DUMPFILE,否则会报「文件不够」。**
**因为每个并行进程要写自己那一个片。**
**这也是为什么生产上的写法是 `DUMPFILE=exp_%U.dmp`**——
**`%U` 会自动展开成序号,一个参数搞定多个文件。**
**耗时反而慢了 4 倍:0.0487 秒变成 0.1980 秒,总大小也从 541.1 KB 涨到 907.3 KB。**
**这不是「并行没用」,是这个模拟没模拟对:**
**它把每一行都单独转一次 JSON、再写进 4 个文件中的一个,写放大很严重。**
**真实 expdp 的并行是「四个进程各读一部分块、各写一个文件」**——
**读是顺序的,写是分散到不同磁盘上的。**
**但有一条结论可以直接搬过来:在一块盘上做并行写,只会更慢。**
**这一点和 16.2 讲通道时的结论完全一致。**
**所以「开并行」之前先问一句:我的存储能同时喂饱几个写者?**
ALL 541 KB、DATA_ONLY 541 KB、METADATA_ONLY 0.6 KB。
前两个几乎一样大,因为元数据(几条建表语句)在数据面前可以忽略。
这条结论要记:元数据几乎不占体积,数据才是大头。
所以「导出慢」永远要去查数据量,别怀疑元数据。
QUERY 那一行:20001 行变 1000 行,体积从 541 KB 掉到 20 KB。
这就是 QUERY 的价值——但它只在一张表上生效,多表时慎用。
EXCLUDE 那一行:4 个对象变 3 个,体积 541 KB 变 106 KB。
注意掉得比预想的多——被排除的 hr_person 正好是最大的那张表(2 万行)。
这套 Demo 的边界:它用的是 JSON 文本格式,真实 dmp 是二进制格式,体积比例会不一样。
但「谁占大头」这个结论是一样的——数据占绝大多数,元数据可以忽略。
30 秒实验
- 把
QUERY的条件改成pid <= 20000(等于不过滤),再跑一次。 看体积是不是回到 541 KB 附近——这说明QUERY生效时确实在过滤。 - 给
dump()加一个content="METADATA_ONLY"的调用,但tabs传全部四个表。 看体积是不是还是很小的几十 K。 - 如果有 Oracle 环境,跑一次
expdp ... QUERY=scott.emp:"WHERE 1=2"。 看是不是只有结构、没有数据——这是「只导结构」的另一种写法。
常见误区
- 「
QUERY可以随便用在全库导出上」。 它会把条件套到每张表上,列名对不上就直接报错。 - 「
EXCLUDE和INCLUDE可以一起写」。 不能,二选一。它们是同一个筛子的两面。 - 「并行度越高导出越快」。 并行要存储跟得上。 一块盘上开四个并行,多半只是把时间花在抢 IO 上。
- 「导出不需要管一致性」。 导出期间业务还在写。
不指定
FLASHBACK_SCN的话,不同表可能属于不同时刻。
自测三问(点开对答案)
1. `DUMPFILE` 里的 `%U` 是什么? —— **一个自动展开的序号占位符。** 不开并行时是 `01`,开并行时按进程数展开。 **所以写 `%U` 以后开并行不用改命令。** 2. `CONTENT=METADATA_ONLY` 的典型用途是什么? —— **在别的环境快速建一套一样的表结构,但不要数据。** **产物很小,导进去几秒钟。** 3. `QUERY` 为什么基本只在表模式里用? —— **因为全库或模式导出时,条件会应用到每一张表上。** **列名对不上就报错。** 4. 为什么要用 `FLASHBACK_SCN`? —— **保证整个导出读的是同一个时刻的一致性视图。** **不然导出期间业务还在写,不同表可能属于不同时刻,导进去就是脏数据。**下一篇讲 IMPDP。导出只是把东西装进箱子,导入才是「搬到新家并摆好」。
麻烦的地方在于:新家可能已经有同名家具了——这就是 TABLE_EXISTS_ACTION 要处理的。
- ☐ (本篇完,下一篇 17.3 IMPDP 导入数据)
相关笔记
- EXPDP 和 IMPDP 概述
- 使用 RMAN 工具备份
- IMPDP 导入数据
-
- 🐬 MySQL必知必会/00-索引
- 🏺 Oracle从入门到精通 · 索引