本文整理 Oracle 到 DM8 静态迁移中的大表导出、快速装载和数据校验过程,重点对比 SQLULDR2 与 dmfldr OUTORA 两种 Oracle 大表导出方式,并验证 SQLULDR2 导出文件使用 DM8 dmfldr 直接路径导入的效果。
大表对象:MIG_APP.MIG_BIG_SALES_LINE
数据规模:10,100,000 行
导出文件:约 1.88 GB
目标数据库:DM8
目标数据库:DM8
本案例在一次Oracle 到 DM8 的静态迁移中,针对一张 1010 万行的大表单独测试导出和装载性能。普通表、数据库对象和大表结构可以按常规迁移流程处理,但大表数据需要重点关注执行时间、失败重跑、索引维护、日志压力和数据校验,因此单独设计文件导出与高速装载流程。
大表是否需要采用独立装载方式,应结合数据量、单行宽度、索引约束、预计耗时、业务窗口和失败重跑成本综合判断。
Oracle 大表
├─ SQLULDR2 导出
└─ dmfldr OUTORA 导出
↓
导出文件检查
↓
DM8 dmfldr 导入
↓
数据一致性校验
↓
创建索引、约束并执行业务验证
本案例中的 SQLULDR2 和 dmfldr OUTORA 是两种 Oracle 端导出方案,分别进行测试;DM8 端使用 dmfldr 对导出文件进行装载。
测试表为 MIG_APP.MIG_BIG_SALES_LINE,主要特征如下:
EVENT_TIME 为需要保留小数秒的时间列;EVENT_DATE 为 Oracle DATE 时间列;LINE_ID 为连续主键,用于范围分片。导出文件统一采用以下格式:
| 项目 | 配置 |
|---|---|
| 字符集 | UTF-8 / Oracle AL32UTF8 |
| 字段分隔符 | 0x1F |
| 记录分隔符 | 0x0A,LF |
| NULL 标记 | \N,SQLULDR2 文件使用 |
| 表头 | 不输出 |
| 日期时间 | 显式指定格式 |
SQLULDR2 导出时应显式指定列清单,不建议直接使用 SELECT *。固定列清单可以保证导出文件与目标端 CTL 的字段顺序一致,也可以避免源表新增列后改变文件格式。
正式导出前先导出 1000 行:
QUERY="SELECT LINE_ID,TENANT_ID,ORDER_ID,CUSTOMER_ID,SKU,ORDER_STATUS,
QUANTITY,UNIT_PRICE,DISCOUNT_AMOUNT,NET_AMOUNT,EVENT_TIME,EVENT_DATE,
REGION_CODE,REMARK,NULLABLE_TAG,TRACE_TOKEN
FROM MIG_APP.MIG_BIG_SALES_LINE
WHERE ROWNUM <= 1000"
sqluldr2 \
"user=MIG_EXPORT/<Oracle 的登录密码>@FREEPDB1" \
"query=$QUERY" \
"file=/data/migration/sqluldr2_probe.dat" \
"log=/data/migration/sqluldr2_probe.log" \
field=0x1F \
record=0x0A \
charset=AL32UTF8 \
"datefmt=YYYY-MM-DD HH24:MI:SS" \
"timefmt=YYYY-MM-DD HH24:MI:SS.FF6" \
"null=\\N" \
head=no \
array=10000 \
rows=1000
验证:
wc -l /data/migration/sqluldr2_probe.dat
stat /data/migration/sqluldr2_probe.dat
xxd -g 1 -l 128 /data/migration/sqluldr2_probe.dat
主要检查:
验证通过后去掉行数限制,执行全表导出,并记录文件大小、行数、SHA-256 和耗时:
/usr/bin/time -v -o /data/migration/sqluldr2_full.time \
sqluldr2 \
"user=MIG_EXPORT/<Oracle 的登录密码>@FREEPDB1" \
"query=SELECT LINE_ID,TENANT_ID,ORDER_ID,CUSTOMER_ID,SKU,ORDER_STATUS,QUANTITY,UNIT_PRICE,DISCOUNT_AMOUNT,NET_AMOUNT,EVENT_TIME,EVENT_DATE,REGION_CODE,REMARK,NULLABLE_TAG,TRACE_TOKEN FROM MIG_APP.MIG_BIG_SALES_LINE" \
"file=/data/migration/sqluldr2_full.dat" \
"log=/data/migration/sqluldr2_full.log" \
field=0x1F record=0x0A charset=AL32UTF8 \
"datefmt=YYYY-MM-DD HH24:MI:SS" \
"timefmt=YYYY-MM-DD HH24:MI:SS.FF6" \
"null=\\N" head=no array=10000 rows=1000000
wc -l /data/migration/sqluldr2_full.dat
sha256sum /data/migration/sqluldr2_full.dat
单进程导出结果:
本案例按 LINE_ID 连续范围划分四个分片:
| 分片 | LINE_ID 范围 |
行数 |
|---|---|---|
| P1 | 1~2,525,000 | 2,525,000 |
| P2 | 2,525,001~5,050,000 | 2,525,000 |
| P3 | 5,050,001~7,575,000 | 2,525,000 |
| P4 | 7,575,001~10,100,000 | 2,525,000 |
每个分片使用相同的列清单和导出参数,仅调整 LINE_ID 范围:
SELECT LINE_ID,TENANT_ID,ORDER_ID,CUSTOMER_ID,SKU,ORDER_STATUS,
QUANTITY,UNIT_PRICE,DISCOUNT_AMOUNT,NET_AMOUNT,EVENT_TIME,
EVENT_DATE,REGION_CODE,REMARK,NULLABLE_TAG,TRACE_TOKEN
FROM MIG_APP.MIG_BIG_SALES_LINE
WHERE LINE_ID BETWEEN :lo AND :hi;
注意,并行导出必须满足下面条件才能用:
四分片并行导出结果:
本轮四片按范围顺序合并后,与单进程导出文件的 SHA-256 一致,说明分片没有引入缺行、重复或数据内容差异。
dmfldr OUTORA 导出dmfldr OUTORA 通过 Oracle OCI 读取源库并输出文本文件,控制文件负责定义源对象、列顺序、分隔符和数据格式。命令示意:
dmfldr \
"USERID=MIG_EXPORT/<Oracle 登录密码>@FREEPDB1" \
"CONTROL='/data/migration/dmfldr_outora.ctl'" \
"MODE='OUTORA'" \
"OCI_DIRECTORY='/opt/oracle/instantclient_19_31'" \
"LOG='/data/migration/dmfldr_outora.log'"
OUTORA 导出需要确认:
DATE/TIMESTAMP 显式指定 DATE FORMAT;执行 1010 万行全表导出,结果如下:
10,100,000 行数据已导出
文件字节数:1,885,118,564
耗时:30.90 秒
dmfldr 内部用时:30,507.527 ms
SHA-256:e7527aba5e2f39f921024e5adbab482310ef67eabcbb4247a3e1ea55fa5bc0db
按时间计算,平均吞吐约为 61.0 MB/s。
dmfldr手册中提供了一些快速装载相关的参数:
OUT_FILE_ROWS:限制每个导出文件的最大行数;OUT_FILE_SIZE:限制每个导出文件的最大大小。TASK_THREAD_NUMBER 是客户端处理数据的线程数OUT_FILE_ROWS和OUT_FILE_SIZE两个参数的作用是“将一个导出任务拆成多个文件”,不是和sqluldr2一样启动多个 Oracle 并行查询进程。
为了实际测量 OUTORA 四路并行抽取速度,本次在 Oracle 中按 LINE_ID 范围创建四个临时视图,再启动四个 dmfldr 进程并行导出:
CREATE VIEW MIG_APP.MIG_BIG_SALES_LINE_P1 AS
SELECT * FROM MIG_APP.MIG_BIG_SALES_LINE
WHERE LINE_ID BETWEEN 1 AND 2525000;
-- P2、P3、P4 使用后续三个连续且不重叠的范围
四个分片均为 2,525,000 行,实测结果如下:
| 分片 | 行数 | 文件字节数 | 耗时 |
|---|---|---|---|
| P1 | 2,525,000 | 468,758,636 | 22.08 秒 |
| P2 | 2,525,000 | 471,357,667 | 22.60 秒 |
| P3 | 2,525,000 | 472,446,982 | 23.83 秒 |
| P4 | 2,525,000 | 472,555,279 | 23.79 秒 |
四个进程的整体耗时时间为 23.8412 秒。四个文件合计:
总行数:10,100,000
总字节数:1,885,118,564
平均吞吐:79.1 MB/s
平均行吞吐:423,636 行/秒
合并文件 SHA-256:e7527aba5e2f39f921024e5adbab482310ef67eabcbb4247a3e1ea55fa5bc0db
合并后的四分片文件与正确格式的单进程全表文件 SHA-256 完全一致,证明本次分片没有产生缺行、重复或顺序差异。
与单进程 OUTORA 相比,四分片时间由 30.90 秒缩短到 23.84 秒,约缩短 22.8%,吞吐提高约 1.30 倍。提升幅度低于 SQLULDR2 四分片,说明当前环境下 OUTORA 并行已经受到 Oracle、磁盘、CPU 或 OCI 处理能力的共同限制。
| 导出方案 | 方式 | 文件大小 | 墙钟时间 | 平均吞吐 | 结果说明 |
|---|---|---|---|---|---|
| SQLULDR2 | 单进程 | 1.887 GB | 30.99 秒 | 60.9 MB/s | 已验证 |
| SQLULDR2 | 四分片并行 | 1.887 GB | 17.55 秒 | 107.5 MB/s | 本轮最快导出方案 |
dmfldr OUTORA |
正确格式、单进程 | 1.885 GB | 30.90 秒 | 61.0 MB/s | 已验证 |
dmfldr OUTORA |
正确格式、四分片并行 | 1.885 GB | 23.8412 秒 | 79.1 MB/s | 已验证 |
本机复测中,SQLULDR2 与正确格式的 OUTORA 单进程性能接近;进入四分片后,SQLULDR2 的扩展效果更好。OUTORA 四分片仍有加速,但没有达到 SQLULDR2 四分片的吞吐。需要注意,本文的 OUTORA 四分片是四个独立 dmfldr 任务,不是 dmfldr 单任务内部自动并行。
目标端使用 DM8 dmfldr 导入分隔文本。导入前建议保留必要表结构,暂缓非必要的二级索引、外键和触发器,避免大批量写入过程中重复维护高成本对象。
CTL 模板如下:
OPTIONS
(
SKIP = 0
ROWS = 50000
DIRECT = TRUE
CHARACTER_CODE = 'UTF-8'
NULL_MODE = TRUE
NULL_STR = '\N'
ERRORS = 1000
)
LOAD DATA
INFILE '/data/migration/big_sales_line.dat' STR X '0A'
BADFILE '/data/migration/big_sales_line.bad'
APPEND
INTO TABLE MIG_APP.MIG_BIG_SALES_LINE
FIELDS X '1F'
TRAILING NULLCOLS
(
LINE_ID,
TENANT_ID,
ORDER_ID,
CUSTOMER_ID,
SKU,
ORDER_STATUS,
QUANTITY,
UNIT_PRICE,
DISCOUNT_AMOUNT,
NET_AMOUNT,
EVENT_TIME DATE FORMAT 'YYYY-MM-DD HH24:MI:SS.FF6',
EVENT_DATE DATE FORMAT 'YYYY-MM-DD HH24:MI:SS',
REGION_CODE,
REMARK,
NULLABLE_TAG,
TRACE_TOKEN
)
本案例实际完成了 SQLULDR2 文件导入 DM8 的测试,流程如下:
SQLULDR2 从 Oracle 导出 big_sales_line.dat
→ 文件检查
→ dmfldr DIRECT=TRUE 导入 DM8
→ 行数、摘要和抽样验证
执行命令示意:
dmfldr \
"USERID=<连接标识>" \
"CONTROL='/data/migration/import_fast.ctl'" \
"LOG='/data/migration/import_fast.log'"
导入结果:
10,100,000 行加载成功
0 数据错误
0 行未装载
75,745.948 ms 用时
性能结果:
测试前执行:
TRUNCATE TABLE MIG_APP.MIG_BIG_SALES_LINE;
单进程控制文件:
DIRECT = TRUE
TASK_THREAD_NUMBER = 1 或 4
ROWS = 50000
INDEX_OPTION = 2
CHARACTER_CODE = 'UTF-8'
测试结果:
| 方式 | dmfldr 进程数 | 每进程线程数 | 耗时 | 成功行数 |
|---|---|---|---|---|
| 单进程低线程 | 1 | 1 | 50.844 秒 | 10,100,000 |
| 单进程默认线程 | 1 | 4 | 26.170 秒 | 10,100,000 |
四进程测试使用四个按 LINE_ID 范围生成的 OUTORA 文件,每个文件 2,525,000 行。所有进程都向同一张空表装载,并设置:
DIRECT = TRUE
PARALLEL = TRUE
分别测试每个进程使用 1 个线程和 4 个线程:
| 方式 | dmfldr 进程数 | 每进程线程数 | 总客户端线程数 | 墙钟时间 | 成功行数 |
|---|---|---|---|---|---|
| 四进程并行 | 4 | 1 | 4 | 26.644 秒 | 10,100,000 |
| 四进程并行 | 4 | 4 | 16 | 26.336 秒 | 10,100,000 |
每个进程均成功装载 2,525,000 行,坏行数和格式错误均为 0。
因此,在本次控制变量复测中:
四进程并行没有明显超过单进程 4 线程,差异约为 0.6%~1.8%。
原因主要有三点:
DM8 手册中 PARALLEL=TRUE 的作用是允许多个用户同时向同一张表装载数据,并不代表多个 dmfldr 进程一定获得线性加速。在当前 4 CPU 虚拟机和无二级索引目标表条件下,单个 dmfldr 使用 4 个任务线程已经接近有效吞吐上限。增加到四个 dmfldr 进程没有带来明显收益,甚至可能造成线程过度竞争。
本轮最终数据校验仍为:
目标表行数:10,100,000
坏行:0
数据格式错误:0
| 装载方式 | 文件来源 | dmfldr 进程数 | 每进程线程数 | 耗时 | 平均吞吐 |
|---|---|---|---|---|---|
| 单进程低线程 | OUTORA 合并文件 | 1 | 1 | 50.844 秒 | 198,646 行/秒 |
| 单进程默认线程 | OUTORA 合并文件 | 1 | 4 | 26.170 秒 | 385,947 行/秒 |
| 四进程并行 | OUTORA 四分片文件 | 4 | 1 | 26.644 秒 | 379,220 行/秒 |
| 四进程并行 | OUTORA 四分片文件 | 4 | 4 | 26.336 秒 | 383,553 行/秒 |
| DTS 常规迁移 | Oracle 直接迁移 | — | — | 272.0 秒 | 37,132 行/秒 |
dmfldr DIRECT=TRUE |
SQLULDR2 文件 | 1 | 4 | 26.746 秒 | 384,342 行/秒 |
一开始dmfldr OUTORA的导出是有问题的。
初始 CTL 对时间列只写列名:
EVENT_TIME,
EVENT_DATE,
工具返回导出成功,但输出文本中的时间出现异常尾数,例如:
2024-01-01 00:00:01.00001
2024-01-01 00:00:02.00002
2024-01-01 00:00:03.00003
Oracle 源端实际值为:
2024-01-01 00:00:01.000000000
2024-01-01 00:00:02.000000000
2024-01-01 00:00:03.000000000
| CTL 定义 | 结果 | 判定 |
|---|---|---|
EVENT_TIME, |
时间尾数异常 | 不合格 |
EVENT_TIME DATE, |
时间尾数仍异常 | 不合格 |
EVENT_TIME DATE FORMAT 'YYYY-MM-DD HH24:MI:SS.FF6', |
与预期文本一致 | 合格 |
在业务大表的复测中,修正后的 OUTORA 文件与 SQLULDR2 显式格式导出的对照文件进行比较,LINE_ID、EVENT_TIME 和 EVENT_DATE 均无差异。
EVENT_TIME DATE FORMAT 'YYYY-MM-DD HH24:MI:SS.FF6',
EVENT_DATE DATE FORMAT 'YYYY-MM-DD HH24:MI:SS',
完整 CTL 中的时间列示例:
(
LINE_ID,
TENANT_ID,
ORDER_ID,
CUSTOMER_ID,
SKU,
ORDER_STATUS,
QUANTITY,
UNIT_PRICE,
DISCOUNT_AMOUNT,
NET_AMOUNT,
EVENT_TIME DATE FORMAT 'YYYY-MM-DD HH24:MI:SS.FF6',
EVENT_DATE DATE FORMAT 'YYYY-MM-DD HH24:MI:SS',
REGION_CODE,
REMARK,
NULLABLE_TAG,
TRACE_TOKEN
)
配置原则:
DATE/TIMESTAMP 列都显式指定格式;FF 位数根据源列精度和目标列精度确定;本次异常的直接原因是:OUTORA 控制文件没有为 Oracle DATE/TIMESTAMP 列指定完整的文本格式。
只写列名或只写 DATE 时,工具虽然能够完成导出流程,但输出文本不一定能够准确表示源端时间值。显式增加 DATE FORMAT 后,时间字段恢复正确,因此本次问题应归因于 CTL 配置不完整。
大表导入前,至少检查以下项目:
在 Oracle 源端和 DM8 目标端执行相同统计逻辑:
SELECT COUNT(*) AS row_count,
MIN(line_id) AS min_id,
MAX(line_id) AS max_id,
SUM(quantity) AS sum_quantity,
SUM(net_amount) AS sum_net_amount,
SUM(CASE WHEN nullable_tag IS NULL THEN 1 ELSE 0 END) AS null_tag_rows,
MIN(event_time) AS min_event_time,
MAX(event_time) AS max_event_time,
MIN(event_date) AS min_event_date,
MAX(event_date) AS max_event_date
FROM MIG_APP.MIG_BIG_SALES_LINE;
还应进行分组验证:
SELECT tenant_id,
order_status,
COUNT(*) AS row_count,
SUM(net_amount) AS amount_sum
FROM MIG_APP.MIG_BIG_SALES_LINE
GROUP BY tenant_id, order_status
ORDER BY tenant_id, order_status;
重点对比:
本次 OUTORA 四分片并行导入后的校验结果:
| 校验项 | Oracle 源端 | DM8 目标端 |
|---|---|---|
| 总行数 | 10,100,000 | 10,100,000 |
最小/最大 LINE_ID |
1 / 10,100,000 | 1 / 10,100,000 |
SUM(QUANTITY) |
30,300,000.00 | 30,300,000.00 |
SUM(NET_AMOUNT) |
41,199,132,432.3606 | 41,199,132,432.3606 |
NULLABLE_TAG 为空 |
1,010,000 | 1,010,000 |
最小/最大 EVENT_TIME |
2024-01-01 00:00:01 / 2024-04-26 21:33:20 | 相同 |
最小/最大 EVENT_DATE |
2024-01-01 / 2024-12-30 | 相同 |
按 TENANT_ID 和 ORDER_STATUS 生成的 60 组统计结果也完成了源端、目标端对照,行数和金额汇总一致。
基础数据校验通过后,再完成以下工作:
时间字段比较不能简单依赖 JDBC 或客户端显示字符串。对于带时区时间,应统一会话时区并比较同一 UTC 语义;对于无时区时间,应使用明确格式比较实际日期、时间和小数秒。
本案例完成了 SQLULDR2 和 dmfldr OUTORA 两种 Oracle 大表导出方式的测试。正确格式下,两种工具的单进程导出性能接近;四分片后 SQLULDR2 为 17.55 秒,OUTORA 为 23.8412 秒,SQLULDR2 在本机环境中的并行扩展效果更好。当前手册提供的是 OUTORA 文件分片参数,不是普通单机 OUTORA 的自动多进程导出功能;本次 OUTORA 四分片结果来自四个独立 dmfldr 任务。
OUTORA 四分片导出采用四个连续主键范围视图并发执行,10,100,000 行合计 1,885,118,564 字节,合并文件与单进程 OUTORA 文件 SHA-256 一致。重新控制变量后,OUTORA 合并文件单进程、4 线程导入用时 26.170 秒;四文件并行、每进程 1 线程用时 26.644 秒;四文件并行、每进程 4 线程用时 26.336 秒,三组结果均为 0 坏行。由此可见,本机环境下四进程并行没有带来明显加速,真正有效的变量是单个 dmfldr 的任务线程数。
dmfldr OUTORA 的时间问题也得到解决:Oracle DATE/TIMESTAMP 列如果未显式指定 DATE FORMAT,即使工具返回 export success,输出文本仍可能错误;修正格式后,最小复现、1000 行对照、1010 万行全量导出和 DM8 导入均验证通过。
文章
阅读量
获赞
