数据库基础体系 · 第 113/139 篇。文章以各产品官方稳定版本的公开语义为准;示例会明确引擎、事务与部署边界。
Oracle Data Pump 与迁移:导出导入、字符集、校验和停机窗口
Oracle Data Pump 是 Oracle 提供的逻辑数据移动工具,主要由 expdp(Export Data Pump)和 impdp(Import Data Pump)组成。它适合在数据库、Schema、表、表空间等逻辑边界上导出和导入对象及数据。
它解决的是“如何把逻辑数据库对象搬到另一个 Oracle 数据库”,而不是所有形式的备份、复制或高可用问题。因此,判断 Data Pump 是否适合一次迁移,至少要同时回答以下问题:
- 导出结果在什么事务视图上保持一致?
- 导入时对象、数据、索引、约束和权限如何重建?
- 源库和目标库的字符集是否兼容?
- dump 文件传输过程中如何校验?
- 校验通过是否足以证明迁移正确?
- 源库在导出期间继续写入时,停机窗口到底发生在哪里?
- Data Pump 与 RMAN、Data Guard、RAC、在线变更和 SQLite 的边界分别是什么?
一、Data Pump 搬运的到底是什么
1. 逻辑对象,而不是数据库物理副本
Data Pump 导出的内容通常包括:
- 表、分区表、索引组织表;
- 表数据;
- 索引定义;
- 约束、触发器、序列、同义词;
- 视图、存储过程、函数、包;
- 用户、角色、系统权限和对象权限;
- 表空间配额等元数据;
- 某些数据库对象附带的统计信息。
这些内容最终以逻辑描述和数据记录的形式写入 dump 文件。导入时,impdp 在目标库中执行相应的 DDL、插入数据、创建索引和恢复约束。
因此,导出文件不是以下任何一种东西:
- 数据文件的物理副本;
- RMAN backup piece;
- redo 或 archived log;
- Data Guard 的 standby 数据库;
- 可被 SQLite 直接读取的数据库文件;
- 可以直接挂载为目标 Oracle 数据库的数据文件。
Data Pump 会重建对象,但不会把源库的数据库身份、数据文件布局、DBID、实例参数和所有物理状态原样复制过去。
2. Data Pump 的基本数据流
一个典型的迁移流程如下:
源库对象和数据
│
▼
expdp 数据库服务器进程
│
├── 读取元数据
├── 通过一致性读读取数据
├── 生成 dump 文件
└── 写入 Data Pump master table 和日志
│
▼
dump 文件
│
文件传输与校验
│
▼
目标库 impdp 数据库服务器进程
│
├── 读取 master table
├── 创建或匹配目标对象
├── 装载表数据
├── 创建索引和约束
└── 记录错误、跳过项和作业状态
expdp 和 impdp 是客户端命令,但真正执行大量工作的通常是数据库服务器端的 Data Pump worker。DIRECTORY 参数指向的不是客户端当前目录,而是数据库中的 DIRECTORY 对象。该对象映射到数据库服务器能够访问的文件系统路径。
例如:
CREATE OR REPLACE DIRECTORY dp_dir AS '/u01/app/oracle/dpump';
GRANT READ, WRITE ON DIRECTORY dp_dir TO APP_ADMIN;
这里的 /u01/app/oracle/dpump 必须由 Oracle 数据库服务器进程访问,而不是仅仅由执行 expdp 的客户端访问。
在 RAC 中,若 Data Pump worker 可能运行在多个实例上,目录对应的文件系统必须被相关实例一致访问。把目录建在单个节点的本地磁盘上,而 Data Pump worker 被调度到其他节点,是常见的失败原因。共享文件系统、集群文件系统或明确的实例绑定策略都必须纳入设计。
二、导出模式和导入边界
Data Pump 有多个导出模式,模式决定了对象范围和权限要求。
1. Schema 模式
Schema 模式适合迁移一个或多个业务 Schema:
expdp app_admin/password@SRCDB \
directory=DP_DIR \
dumpfile=app_%U.dmp \
logfile=app_export.log \
schemas=APP \
filesize=10G \
parallel=4
这里:
schemas=APP表示导出APPSchema;%U让 Data Pump 生成多个文件名,例如app_01.dmp、app_02.dmp;filesize=10G限制单个 dump 文件大小;parallel=4允许多个 worker 工作,但通常需要多个 dump 文件才能有效并行写入;logfile是数据库服务器端目录中的日志文件。
导入到目标库:
impdp app_admin/password@DESTDB \
directory=DP_DIR \
dumpfile=app_%U.dmp \
logfile=app_import.log \
schemas=APP
若目标 Schema 名称不同:
impdp system/password@DESTDB \
directory=DP_DIR \
dumpfile=app_%U.dmp \
logfile=app_import.log \
remap_schema=APP:APP_NEW
REMAP_SCHEMA 只改变对象归属,不会自动解决表空间、字符集、外部系统配置或应用连接串问题。
2. Table 模式
只迁移部分表时可以使用:
expdp app_admin/password@SRCDB \
directory=DP_DIR \
dumpfile=orders.dmp \
logfile=orders_export.log \
tables=APP.ORDERS,APP.ORDER_ITEMS
需要注意,单独导出表可能无法自然携带整个应用所需的依赖对象。表上的索引和约束可以导出,但关联的类型、序列、包、同义词或外部依赖可能需要额外处理。
3. Full 模式
完整数据库导出使用:
expdp system/password@SRCDB \
directory=DP_DIR \
dumpfile=full_%U.dmp \
logfile=full_export.log \
full=y \
parallel=4
FULL=Y 并不意味着它是 RMAN 意义上的“物理完整备份”。它仍然是逻辑导出,不能替代数据库级物理恢复方案。
4. 表空间和可传输表空间
对于大规模数据库,Data Pump 还可以与可传输表空间能力结合。其基本思想不是逐行导出所有表数据,而是传输表空间数据文件,再用 Data Pump 导出和导入对象元数据。
这类方案的速度和停机特征可能优于普通逻辑导出,但前置条件更多,包括:
- 表空间是否自包含;
- 源、目标平台和字节序是否支持;
- 版本是否支持目标传输方式;
- 只读或冻结表空间的安排;
- 跨平台转换是否需要 RMAN
CONVERT; - 用户、权限和非表空间对象如何迁移。
因此,不能因为“使用了 Data Pump”就把普通 Schema 导出和可传输表空间当成同一种迁移。
三、导出的事务一致性:一个 SCN 视图,而不是停机快照
1. SCN 和一致性读
Oracle 的 SCN(System Change Number)可以理解为数据库逻辑时间线上的位置。对某一时刻的查询,Oracle 通过一致性读使用当前数据块和 undo 信息重建该时刻可见的数据版本。
设导出开始时选择的 SCN 为 ,某一行在 SCN 时已经提交,则该行应当对一致性读可见;在 之后才提交的修改,则不应出现在该一致性视图中。
可以把 Data Pump 的目标抽象为:
其中:
- 是导出使用的 SCN;
VisibleData(S)是在该 SCN 上可见的已提交数据;- 导出过程持续时间可以大于一个事务,但逻辑上使用同一时间点的可见性。
使用显式 SCN 的例子:
SELECT current_scn FROM v$database;
假设得到:
CURRENT_SCN
-----------
1234567890
然后:
expdp app_admin/password@SRCDB \
directory=DP_DIR \
dumpfile=app_%U.dmp \
logfile=app_export.log \
schemas=APP \
flashback_scn=1234567890
FLASHBACK_TIME 也可以按时间指定,Oracle 会将其转换为相应的 SCN:
expdp app_admin/password@SRCDB \
directory=DP_DIR \
dumpfile=app_%U.dmp \
logfile=app_export.log \
schemas=APP \
flashback_time=\"to_timestamp('2025-01-20 22:00:00','YYYY-MM-DD HH24:MI:SS')\"
生产脚本中更容易审计和复现的是显式记录 SCN。时间转换还会受到数据库时区、客户端引号和参数文件解析方式影响。
2. 导出时源库可以继续写入吗
通常可以。Data Pump 的一致性读机制并不要求普通表在整个导出期间都被锁住。导出开始后,应用提交的新事务不应出现在指定 SCN 的一致性数据中。
但这不表示导出期间没有风险:
-
undo 必须保留足够长时间
如果导出运行很久,而源表的旧版本被覆盖,可能出现类似ORA-01555: snapshot too old的错误。 -
对象定义可能发生变化
DDL、分区操作、表重定义和对象依赖变化可能使导出失败、产生警告或导致对象不完整。 -
跨表业务一致性不等于数据库一致性
Data Pump 看到的是数据库 SCN 上的已提交状态,但它不会理解“订单和支付记录必须一起完成”这类业务语义。如果应用在一个业务流程中跨多个系统提交,单个 Oracle SCN 也无法覆盖外部系统。 -
导出完成时间不是数据截止时间
例如导出在 22:00 开始、01:00 完成,若使用FLASHBACK_SCN,数据截止点可能是 22:00 附近,而不是 01:00。导出日志的完成时间不能作为数据时间点。
3. 长事务与 undo 的关系
假设导出需要读取一个大表 3 小时,而源库对该表持续更新。为了重建旧版本,Oracle 需要在 undo 中保留导出所需的版本。
如果所需旧版本已经被回收,读取可能失败。提高 UNDO_RETENTION 只是配置意图,是否真正保留还取决于 undo 表空间空间和自动管理行为。不能把它当成无限期保证。
迁移前应通过测试估计:
- 导出耗时;
- 期间的更新量;
- undo 使用峰值;
- 目标环境的 I/O 和并行度;
- 是否存在长时间运行的批处理事务。
四、导出、传输、导入与“校验”的三个层次
迁移校验不能只依赖一个数字。至少要区分三层。
1. 文件完整性
文件完整性回答:
dump 文件在生成、复制和读取过程中,字节是否发生变化或损坏?
外部哈希
Linux 上可以使用:
sha256sum app_01.dmp app_02.dmp app_export.log > app_export.sha256
在目标服务器传输后:
sha256sum -c app_export.sha256
预期输出类似:
app_01.dmp: OK
app_02.dmp: OK
这能检测文件复制、截断和普通位翻转,但它只证明“目标文件与源文件相同”,不证明源导出本身正确。
Data Pump 内置 checksum
部分 Oracle 版本提供 Data Pump 的 CHECKSUM 和校验相关参数,用于在 dump 文件块层面生成或验证校验值。具体参数、默认值、可用算法和适用版本应以目标版本的命令帮助和官方文档为准:
expdp help=y
impdp help=y
不要在未确认版本的环境中直接假设某个 CHECKSUM_ALGORITHM 或验证参数一定存在。即使 Data Pump 内置校验成功,它通常也只能说明 dump 文件块通过了相应检查,不能证明表行数、业务关系和目标对象状态正确。
2. 导入过程完整性
导入日志可以发现:
- 对象创建失败;
- 表已存在;
- 权限不足;
- 表空间不存在;
- 无法创建索引;
- 约束启用失败;
- 某些对象被跳过;
- 字符转换或对象版本不兼容。
导入结束后不能只看命令退出码,还要检查日志中的:
ORA-
UDI-
ORA-39083
ORA-39126
ORA-31684
ORA-39151
不同错误的含义不同。例如:
ORA-31684常见于对象已存在,未必表示迁移整体失败;ORA-39151表示对象已存在且被跳过等特定情况;ORA-39083常表示某个对象 DDL 执行失败;- 数据表导入成功但索引创建失败,应用可能仍然能查,但性能和唯一性语义可能已经改变。
可以先用 SQLFILE 预览导入将要执行的 DDL:
impdp system/password@DESTDB \
directory=DP_DIR \
dumpfile=app_%U.dmp \
sqlfile=app_preview.sql \
schemas=APP
SQLFILE 的作用是生成 SQL 文件而不执行这些 DDL,适合审核 CREATE USER、ALTER USER、表空间、权限、触发器等内容。它不能替代真正导入,因为数据装载、内部处理和部分对象恢复并不会通过这一个 SQL 文件完整表达。
3. 数据语义完整性
文件哈希和导入日志都不能证明:
- 每张表的行数一致;
- 主键范围一致;
- 子表没有丢失父表引用;
- 字符没有发生不可逆转换;
- 触发器、权限和应用行为一致;
- 统计信息适合目标数据分布。
至少应进行表级核对:
SELECT
COUNT(*) AS row_count,
MIN(order_id) AS min_order_id,
MAX(order_id) AS max_order_id
FROM app.orders;
源库和目标库分别执行并比较结果。对于大表,COUNT(*) 本身可能很昂贵,且只比较行数不能发现“同样多但内容不同”。
可以增加按主键范围分桶的校验:
SELECT
TRUNC(order_id / 100000) AS bucket_id,
COUNT(*) AS row_count,
MIN(order_id) AS min_id,
MAX(order_id) AS max_id
FROM app.orders
GROUP BY TRUNC(order_id / 100000)
ORDER BY bucket_id;
若主键不是连续数字,应使用日期分桶、哈希分桶或业务分区。ORA_HASH 可以用于快速抽样或分组比较,但它不是无碰撞的密码学证明,不能单独作为强一致性校验。
业务上重要的金额字段还应分别聚合:
SELECT
COUNT(*) AS row_count,
SUM(amount) AS amount_sum,
MIN(created_at) AS min_created_at,
MAX(created_at) AS max_created_at
FROM app.orders;
这仍然不是数学意义上的全量证明。例如,两行金额互换可能保持总和不变。因此,对高风险表应使用稳定排序后的分片校验,或者在业务停写后进行最终全量比较。
五、字符集:导入成功不等于文本没有损失
1. Oracle 中的两个字符集概念
Oracle 数据库至少需要区分:
- 数据库字符集:主要影响
CHAR、VARCHAR2、CLOB等类型; - 国家字符集:主要影响
NCHAR、NVARCHAR2、NCLOB。
可以查询:
SELECT parameter, value
FROM nls_database_parameters
WHERE parameter IN (
'NLS_CHARACTERSET',
'NLS_NCHAR_CHARACTERSET'
)
ORDER BY parameter;
典型结果可能是:
NLS_CHARACTERSET AL32UTF8
NLS_NCHAR_CHARACTERSET AL16UTF16
AL32UTF8 是 Oracle 对 Unicode UTF-8 的数据库字符集名称。它不要与 Oracle 历史上的 UTF8 混淆;二者的 Unicode 编码语义并不相同。
2. Data Pump 如何处理字符转换
Data Pump 导出文件包含用于解释元数据和文本数据的字符集信息。导入时,Oracle 会根据源和目标数据库字符集进行转换。
理想情况是目标字符集能够表示源数据中的所有字符。例如:
源库:AL32UTF8
目标库:AL32UTF8
通常不需要发生数据库字符集之间的降级转换。
风险情况是:
源库:AL32UTF8
目标库:WE8MSWIN1252
源库可能包含中文、日文、emoji 或其他目标字符集无法表示的字符。导入可能出现警告、转换错误或不可逆替换,具体表现取决于数据类型、转换路径和 Oracle 版本。不能因为 impdp 最终返回成功,就推断每个字符都保持不变。
字符集转换的核心条件可以写成:
其中:
- 是源数据实际出现的字符集合;
- 是目标数据库字符集;
- 若存在一个字符不满足条件,就可能发生替换、报错或信息损失。
这不是简单地比较字符集名称大小就能完全判断的,因为数据中实际出现了哪些字符同样重要。
3. 字节长度和字符长度不是一回事
在多字节字符集下:
SELECT
LENGTH(text_col) AS char_length,
LENGTHB(text_col) AS byte_length
FROM app.messages;
LENGTH通常按字符计算;LENGTHB按字节计算。
例如,同样是 10 个字符,在单字节字符集和 AL32UTF8 中占用的字节数可能不同。迁移到目标字符集后,以下问题可能出现:
VARCHAR2(n BYTE)的字节限制被触发;- 索引键长度变化;
- 复合索引更接近键长度上限;
- 应用按字节截断字符串造成数据损坏;
CLOB内容虽然能保存,但下游接口编码不一致。
应区分:
VARCHAR2(100 BYTE)
VARCHAR2(100 CHAR)
前者限制字节数,后者限制字符数,但二者仍受到数据库版本和最大字符串设置等条件约束。
4. 字符集迁移前的测试
不要只查数据库参数,还要抽取真实文本边界样本:
SELECT message_id, text_col
FROM app.messages
WHERE REGEXP_LIKE(text_col, '[^ -~]')
FETCH FIRST 100 ROWS ONLY;
这只是寻找非 ASCII 字符的样本,不是完整的 Unicode 能力测试。更可靠的做法是:
- 从生产库抽取包含中文、组合字符、emoji、全角字符和特殊标点的代表性数据;
- 在与生产相同的字符集和版本上导出;
- 导入到目标测试库;
- 比较字符长度、字节长度和原始文本;
- 检查应用驱动、连接池和消息队列的编码;
- 对不可表示字符制定明确的拒绝或清洗策略。
NLS_LANG 主要影响 Oracle 客户端与服务器之间的客户端字符集协商和消息环境,不能把设置 NLS_LANG 当成改变数据库字符集或修复 Data Pump 数据转换的手段。
六、导入不是“把文件解压进去”
1. 导入中的对象依赖
一个 Schema 导入大致要处理这些阶段:
用户与表空间映射
│
▼
表和基础对象
│
▼
表数据
│
▼
索引、约束、触发器
│
▼
视图、包、过程、权限、统计信息等
实际调度由 Data Pump 控制,具体顺序会因对象类型和依赖关系变化。导入过程中,某些对象可能因为前置对象不存在而失败。日志中的错误必须结合对象名称和 DDL 内容分析。
例如目标环境没有源表空间:
impdp system/password@DESTDB \
directory=DP_DIR \
dumpfile=app_%U.dmp \
logfile=app_import.log \
remap_tablespace=APP_DATA:USERS
这会把源表空间 APP_DATA 映射到目标表空间 USERS。但如果目标表空间容量、区块大小、自动扩展策略或配额不合适,映射成功仍可能在装载阶段失败。
2. 已存在对象的处理
导入已有 Schema 时,必须明确 TABLE_EXISTS_ACTION:
impdp system/password@DESTDB \
directory=DP_DIR \
dumpfile=app_%U.dmp \
logfile=app_import.log \
schemas=APP \
table_exists_action=replace
常见行为包括:
SKIP:目标表存在时跳过;APPEND:向已有表追加数据;TRUNCATE:先清空目标表再装载;REPLACE:删除并重新创建目标表。
REPLACE 不是无风险的刷新操作。它可能删除目标表上的本地修改、依赖对象或不在导出文件中的辅助结构。生产导入前应使用独立目标 Schema 或新表空间演练,而不是直接在唯一生产对象上试错。
3. 预览和实际导入分离
建议先生成 SQL 预览:
impdp system/password@DESTDB \
directory=DP_DIR \
dumpfile=app_%U.dmp \
logfile=app_preview.log \
schemas=APP \
remap_schema=APP:APP_NEW \
remap_tablespace=APP_DATA:APP_NEW_DATA \
sqlfile=app_preview.sql
审核内容包括:
- 是否错误创建了源库用户;
- 默认表空间和临时表空间是否正确;
- 是否包含不应迁移的目录对象、数据库链接或权限;
- 是否引用了目标库不存在的表空间;
- 是否有依赖源库路径的外部表、BFILE 或调度任务;
- 是否会覆盖目标库已有对象。
七、停机窗口:Data Pump 能减少什么,不能消除什么
1. 普通 Data Pump 的停机模型
最简单的迁移方式是:
1. 导出源库
2. 传输 dump 文件
3. 导入目标库
4. 校验目标库
5. 停止应用写入
6. 做最后差异处理或确认无差异
7. 切换连接到目标库
8. 重新开放应用
如果在第 1 步导出后源库继续写入,那么目标库只包含导出一致性点之前的数据。假设:
- 初始导出 SCN 为 ;
- 应用在 之后产生事务;
- Data Pump 本身不提供持续增量复制。
则目标库缺少:
其中 表示 SCN 时已提交的数据状态。
因此,Data Pump 的导出时间可能很长,但真正的停机窗口是否很短,取决于是否有办法处理 。
2. 方案一:导出期间允许写入,切换时停机
这是最容易理解的方案:
- 在允许业务继续运行时做一致性导出;
- 创建目标库并导入;
- 停止应用写入;
- 将源库在导出点之后产生的差异补到目标;
- 做最终校验;
- 切换连接。
差异补偿可以是:
- 应用层双写;
- 按更新时间或递增 ID 抽取;
- 业务日志重放;
- 数据库变更捕获工具;
- 在切换前重新导出变更表。
但“按更新时间补数据”并不是天然正确的增量方案。时钟精度、更新但不改变 updated_at、删除记录、回写旧时间和事务边界都会造成漏数或重复。若采用这种方案,必须定义幂等键、删除传播和重复导入行为。
3. 方案二:停写后再做完整导出
如果数据库规模允许,可以在应用停写后执行 Data Pump:
停止写入
│
├── 记录停写时间和 SCN
├── 导出
├── 传输并导入
├── 全量校验
└── 切换应用
它的优点是数据模型简单,目标自然对应停写后的稳定状态。缺点是停机时间等于导出、传输、导入和校验的总耗时,通常不适合大库。
4. 方案三:使用在线复制或物理切换能力
如果目标是大幅缩短停机时间,Data Pump 通常只负责初始装载,后续需要其他机制同步变化,例如逻辑复制、变更数据捕获或 Oracle 体系内的高可用能力。
这时要区分:
- Data Pump:逻辑初始装载和对象迁移;
- Data Guard:以 redo 传输和物理或逻辑备用机制为核心;
- RMAN:物理备份、恢复和数据库级灾难恢复;
- RAC:同一数据库的多实例并发访问,不等于跨数据库迁移;
- 在线变更或 CDC:持续传播源库变更,帮助缩短切换窗口。
Data Pump 本身不是 CDC 工具,也不会自动记录并重放导出结束后的所有 DML。
八、Data Pump 与 RMAN、Data Guard、RAC 的边界
1. Data Pump 与 RMAN
RMAN 主要处理物理备份和恢复,例如:
- 数据文件;
- 控制文件;
- archived redo;
- 数据库时间点恢复;
- 介质故障恢复。
Data Pump 主要处理逻辑对象:
- Schema 迁移;
- 表和数据抽取;
- 表空间映射;
- 版本或平台之间的逻辑转换;
- 选择性导入和对象过滤。
如果目标是“源库磁盘损坏后恢复到某个时间点”,应考虑 RMAN,而不是 Data Pump。
如果目标是“只把一个 Schema 搬到另一套 Oracle 数据库,并修改表空间名称”,Data Pump 更符合问题边界。
2. Data Pump 与 Data Guard
Data Guard 的核心是数据库级 redo 传输和备用库应用。它适合构建物理或逻辑备用数据库并执行故障切换、切换和灾难恢复。
Data Guard 通常不适合直接表达以下迁移需求:
- 只迁移一个 Schema;
- 改变对象名称;
- 改变表空间布局;
- 清理部分对象;
- 将数据映射到完全不同的逻辑模型。
这些是 Data Pump 或 ETL 的职责。
反过来,Data Pump 也不能替代 Data Guard 的持续 redo 保护。导出文件成功不意味着目标库能立即接替源库的所有提交。
3. Data Pump 与 RAC
RAC 是多个实例访问同一数据库。Data Pump 可以在 RAC 数据库上运行,但要注意:
- DIRECTORY 对象是数据库对象,路径由服务器端解释;
- 所有可能执行 worker 的实例都必须能访问 dump 文件和日志路径;
- 并行度受 CPU、I/O、对象大小和文件数量限制;
- 多个 worker 不会让单个不可并行对象无限加速;
- 目标库导入期间的索引创建、约束启用和日志写入仍可能成为瓶颈。
RAC 解决的是同一数据库的实例级并发和可用性,不是迁移同步机制。
九、作业状态、断点和失败恢复
Data Pump 作业有自己的状态和 master table。导出或导入过程被中断时,不能简单地假设重新执行同一条命令一定安全。
可以附着到作业:
impdp system/password@DESTDB \
attach=SYS_IMPORT_SCHEMA_01
进入交互界面后可以查看状态:
STATUS
请求停止:
STOP_JOB=IMMEDIATE
恢复已停止的作业时,通常应附着到现有作业并执行:
START_JOB
KILL_JOB 是更强的终止操作,可能清理作业状态,但不能撤销已经提交的目标库 DDL 或数据。导入后的恢复策略不能依赖“杀掉作业就自动回滚整个数据库”。
生产环境应提前决定:
- 失败后是继续导入,还是删除目标 Schema 后重来;
- 目标对象是否允许部分存在;
- 是否使用专用目标用户和表空间;
- 如何清理已导入数据;
- 如何区分可接受的“对象已存在”与真正的数据错误;
- 如何保存导出参数、SCN、文件清单和日志。
对于可重复演练的迁移,使用新的目标 Schema,例如 APP_STAGE,通常比反复覆盖正在使用的 APP 更容易诊断。
十、一个可执行的 Schema 迁移演练
以下例子假设:
- 源库服务名为
SRCDB; - 目标库服务名为
DESTDB; - 源 Schema 为
APP; - 目标 Schema 为
APP_NEW; - 两端均已创建同名 DIRECTORY 对象;
- 执行账号拥有对应 Schema 和 DIRECTORY 权限;
- 目标表空间
APP_NEW_DATA已存在。
步骤 1:记录源库环境和一致性点
SELECT current_scn FROM v$database;
SELECT parameter, value
FROM nls_database_parameters
WHERE parameter IN (
'NLS_CHARACTERSET',
'NLS_NCHAR_CHARACTERSET'
);
SELECT owner, object_type, COUNT(*) AS object_count
FROM dba_objects
WHERE owner = 'APP'
GROUP BY owner, object_type
ORDER BY object_type;
保存查询结果。SCN 用于解释数据截止点,字符集用于判断转换风险,对象统计用于导入后比对。
步骤 2:导出
expdp app_admin/password@SRCDB \
directory=DP_DIR \
dumpfile=app_%U.dmp \
logfile=app_export.log \
schemas=APP \
flashback_scn=1234567890 \
filesize=10G \
parallel=4
预期结果是生成一个或多个 dump 文件和导出日志。日志中应检查:
- 实际导出的对象数量;
- 是否出现 ORA 错误;
- 是否存在无法导出的对象;
- 任务是否正常完成;
- 使用的 SCN、文件名和参数是否被记录。
步骤 3:传输并校验文件
sha256sum app_01.dmp app_02.dmp > app.sha256
在目标服务器执行:
sha256sum -c app.sha256
只有所有文件显示 OK,才进入导入步骤。
步骤 4:预览 DDL
impdp system/password@DESTDB \
directory=DP_DIR \
dumpfile=app_%U.dmp \
logfile=app_preview.log \
schemas=APP \
remap_schema=APP:APP_NEW \
remap_tablespace=APP_DATA:APP_NEW_DATA \
sqlfile=app_preview.sql
检查生成的 app_preview.sql,确认用户、表空间、权限和对象引用符合目标环境。
步骤 5:正式导入
impdp system/password@DESTDB \
directory=DP_DIR \
dumpfile=app_%U.dmp \
logfile=app_import.log \
schemas=APP \
remap_schema=APP:APP_NEW \
remap_tablespace=APP_DATA:APP_NEW_DATA \
parallel=4
导入后检查:
SELECT owner, object_type, COUNT(*) AS object_count
FROM dba_objects
WHERE owner = 'APP_NEW'
GROUP BY owner, object_type
ORDER BY object_type;
检查无效对象:
SELECT owner, object_name, object_type
FROM dba_objects
WHERE owner = 'APP_NEW'
AND status <> 'VALID'
ORDER BY object_type, object_name;
注意:某些对象的 STATUS、编译依赖或统计信息需要在目标环境中重新处理。无效对象列表应与源库差异解释,而不是机械地要求数量完全相同。
步骤 6:比较关键表
源库:
SELECT COUNT(*) AS row_count,
MIN(order_id) AS min_id,
MAX(order_id) AS max_id,
SUM(amount) AS amount_sum
FROM app.orders;
目标库:
SELECT COUNT(*) AS row_count,
MIN(order_id) AS min_id,
MAX(order_id) AS max_id,
SUM(amount) AS amount_sum
FROM app_new.orders;
如果结果不同,应继续按分区、日期、主键范围或业务批次定位,不应直接用 APPEND 重跑,因为那可能掩盖重复数据。
十一、常见误解和失败表现
误解一:导出日志显示完成,所以迁移一定正确
错误。日志完成只表示 Data Pump 作业按照自身规则完成。它不保证:
- 所有业务对象都被包含;
- 所有对象都在目标库成功创建;
- 文本没有字符损失;
- 数据行内容与源库一致;
- 应用依赖的外部对象已迁移。
必须结合错误日志、对象状态、数据核对和应用验证。
误解二:dump 文件的 SHA-256 一致,所以目标数据一定一致
错误。SHA-256 只能证明传输后的文件字节与计算哈希时的文件一致。若源端导出时遗漏对象、使用了错误 Schema、使用了错误的 SCN,哈希仍然会一致。
误解三:导出期间应用可以写入,所以可以随时切换
错误。导出期间写入只说明源库可以继续服务;目标库仍然停留在导出一致性点。切换前必须处理导出点之后的变更,否则会丢失提交。
误解四:目标字符集更“新”,所以一定兼容
错误。字符集兼容取决于实际字符集合和目标存储能力。即使目标使用 Unicode,应用驱动、连接参数、文件接口和下游系统仍可能发生编码问题。
误解五:增加 PARALLEL 就一定更快
错误。并行度受以下因素共同限制:
- dump 文件数量;
- 源库读取能力;
- 目标库写入能力;
- 索引创建和排序;
- undo、redo 和临时表空间;
- 单个对象能否并行处理;
- 存储系统吞吐量。
盲目提高并行度可能导致源库和目标库同时争用 CPU、I/O、临时表空间和日志写入。
误解六:Data Pump 可以替代 SQLite 迁移工具
错误。SQLite 没有 Oracle Data Pump 的对象模型、用户体系、表空间和数据库字符集机制。Oracle 到 SQLite 的迁移通常需要:
- 使用 Data Pump 或 SQL 查询从 Oracle 提取数据;
- 根据 SQLite 类型系统重新设计类型映射;
- 单独迁移主键、唯一约束、索引和触发器;
- 处理日期、时间、精确数字、CLOB、BLOB 和 NULL 语义;
- 使用 SQLite 事务批量装载;
- 对应用 SQL 和并发模型重新测试。
这属于 ETL 或应用级迁移,不是 impdp 的目标范围。
十二、与 Expand-Contract 和可回滚发布的关系
当数据库迁移同时包含 Schema 变更时,不应把“数据搬迁”和“应用切换”设计成一次不可逆操作。
例如将:
APP.ORDERS.amount NUMBER
迁移为新的金额结构,较安全的流程可以是:
Expand
├── 创建新列或新表
├── 部署兼容旧结构的应用版本
├── 回填和同步新数据
└── 验证新旧结构一致
Contract
├── 切换应用只读新结构
├── 观察一段时间
└── 再删除旧列或旧表
Data Pump 可以参与 Expand 阶段的初始装载,但它不能自动提供双写、实时同步或业务回滚。切换时应明确:
- 哪个版本的应用可以读旧结构和新结构;
- 旧结构是否仍然保留;
- 迁移失败时如何把连接切回源库;
- 源库在切换前是否继续保留写入能力;
- 如何处理已经写入目标库但尚未写回源库的数据。
若目标是“失败后立即切回”,最重要的不是导入命令本身,而是切换后源库是否仍拥有完整、可继续服务的数据,以及双向差异是否会破坏回滚。
十三、如何设计停机窗口的计算
停机窗口不能只写成“预计几分钟”,应拆成可测量的阶段:
其中:
- :停止写入、排空请求和关闭批处理的时间;
- :补齐初始导出之后变更的时间;
- :关键表、约束、应用读写和数据一致性验证时间;
- :连接串、服务名、负载均衡或路由切换时间;
- :连接池重建、缓存预热和后台任务恢复时间。
普通完整停机导入时,初始导入时间也会计入窗口:
而初始导出和文件传输如果在业务运行期间完成,就不会全部计入停机窗口,但必须为最终差异同步付出代价。
可靠的估算方式是做至少一次与生产规模接近的演练,记录:
- 导出总耗时和吞吐;
- dump 文件实际大小;
- 传输速度;
- 导入表数据耗时;
- 索引和约束创建耗时;
- 校验查询耗时;
- 失败重试和清理耗时。
没有演练数据时,任何精确停机承诺都缺乏依据。
十四、迁移完成的判定条件
一次可接受的 Data Pump 迁移,至少应满足以下事实条件:
- 导出日志和导入日志已归档;
- 使用的导出模式、Schema、SCN 和参数可追溯;
- 所有 dump 文件在传输后通过文件级校验;
- 导入日志中的 ORA 错误已逐项解释;
- 关键对象数量与源库差异可解释;
- 关键表的行数、范围、聚合值或分桶校验通过;
- 主键、唯一约束、外键和索引状态符合预期;
- 字符集和代表性文本测试通过;
- 应用读写、事务提交、连接池和后台任务测试通过;
- 切换失败时有明确的连接回退和数据处理方案;
- 源库在回滚窗口内仍保留可恢复状态;
- 后续备份、监控、统计信息和维护作业已经接管目标库。
Data Pump 适合做可选择范围的逻辑迁移,尤其适用于 Schema 重建、表空间映射和跨环境装载。它的核心保证是逻辑导出与导入过程中的对象和数据搬运,不是持续复制、物理灾备或业务语义验证。
真正的迁移完成点,应当是目标库在明确一致性点上完成装载、字符数据未发生不可接受的转换、文件和数据均通过验证,并且应用可以在可回滚的切换策略下稳定运行。
系列导航与关联阅读
- 系列入口:数据库完整学习路线:从关系模型、事务索引到分布式与向量检索
- 上一篇:Oracle 监控与调优:AWR、ASH、等待事件、统计和容量
- 下一篇:SQLite 类型系统与 STRICT 表:亲和性、存储类、约束和兼容
- 延伸:Oracle RMAN、Data Guard 与 RAC:备份恢复和高可用边界
- 延伸:数据库迁移与在线变更:Expand-Contract、锁风险和可回滚发布
官方资料
本文依据数据库官方文档重新梳理;正文、示例与生产检查清单由 WR BLOG 编写。

评论
0 条讨论