注册
Oracle 到 DM8 大表高速装载
技术分享/ 文章详情 /

Oracle 到 DM8 大表高速装载

悬铃木 2026/08/14 98 1 0

Oracle 到 DM8 大表高速装载

本文整理 Oracle 到 DM8 静态迁移中的大表导出、快速装载和数据校验过程,重点对比 SQLULDR2 与 dmfldr OUTORA 两种 Oracle 大表导出方式,并验证 SQLULDR2 导出文件使用 DM8 dmfldr 直接路径导入的效果。

大表对象:MIG_APP.MIG_BIG_SALES_LINE

数据规模:10,100,000 行

导出文件:约 1.88 GB

目标数据库:DM8

目标数据库:DM8


目录

1. 大表高速装载概述

2. Oracle 大表导出测试

3. DM8 大表导入测试

4. dmfldr OUTORA 导出的时间格式问题

5. 数据校验与结论


1. 大表高速装载概述

1.1 案例背景

本案例在一次Oracle 到 DM8 的静态迁移中,针对一张 1010 万行的大表单独测试导出和装载性能。普通表、数据库对象和大表结构可以按常规迁移流程处理,但大表数据需要重点关注执行时间、失败重跑、索引维护、日志压力和数据校验,因此单独设计文件导出与高速装载流程。

大表是否需要采用独立装载方式,应结合数据量、单行宽度、索引约束、预计耗时、业务窗口和失败重跑成本综合判断。

1.2 整体流程

Oracle 大表
   ├─ SQLULDR2 导出
   └─ dmfldr OUTORA 导出
            ↓
      导出文件检查
            ↓
      DM8 dmfldr 导入
            ↓
      数据一致性校验
            ↓
      创建索引、约束并执行业务验证

本案例中的 SQLULDR2 和 dmfldr OUTORA 是两种 Oracle 端导出方案,分别进行测试;DM8 端使用 dmfldr 对导出文件进行装载。


2. Oracle 大表导出测试

2.1 测试表和测试环境

测试表为 MIG_APP.MIG_BIG_SALES_LINE,主要特征如下:

  • 数据量:10,100,000 行;
  • 字段数:16 列;
  • 文本文件大小:约 1.88 GB;
  • 包含主键、关联键、状态、数量、金额、时间、中文文本、NULL 和链路追踪字段;
  • EVENT_TIME 为需要保留小数秒的时间列;
  • EVENT_DATE 为 Oracle DATE 时间列;
  • 不含 LOB 字段,适合采用分隔文本方式测试;
  • LINE_ID 为连续主键,用于范围分片。

导出文件统一采用以下格式:

项目 配置
字符集 UTF-8 / Oracle AL32UTF8
字段分隔符 0x1F
记录分隔符 0x0A,LF
NULL 标记 \N,SQLULDR2 文件使用
表头 不输出
日期时间 显式指定格式

2.2 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

主要检查:

  • 行数是否为 1000;
  • 每行字段数是否为 16;
  • 时间格式、中文和 NULL 是否符合预期。

单进程全表导出

验证通过后去掉行数限制,执行全表导出,并记录文件大小、行数、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

单进程导出结果:

  • 文件大小:约 1.887 GB;
  • 导出耗时:30.99 秒;
  • 平均吞吐:约 60.9 MB/s。

四分片并行导出

本案例按 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;

注意,并行导出必须满足下面条件才能用:

  • 所有分片来自同一个 Oracle SCN 或同一个只读快照;
  • 分片边界不重叠、不遗漏;
  • 分片键非空且分布足够均匀;
  • 导出完成后对合并文件进行整体校验。

四分片并行导出结果:

  • 文件大小:约 1.887 GB;
  • 导出耗时:17.55 秒;
  • 平均吞吐:约 107.5 MB/s。

本轮四片按范围顺序合并后,与单进程导出文件的 SHA-256 一致,说明分片没有引入缺行、重复或数据内容差异。

2.3 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 导出需要确认:

  • OCI 动态库、TNS 配置和数据库服务可用;
  • 导出账号可以直接查询源对象;
  • 控制文件中的列顺序、分隔符和字符集正确;
  • Oracle DATE/TIMESTAMP 显式指定 DATE FORMAT
  • 源表不包含当前 OUTORA 不支持的 LOB 字段。

单进程全表导出

执行 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_ROWSOUT_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 处理能力的共同限制。

2.4 两种导出方式对比

导出方案 方式 文件大小 墙钟时间 平均吞吐 结果说明
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 单任务内部自动并行。


3. DM8 大表导入测试

3.1 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
)

3.2 SQLULDR2 导出文件导入

本案例实际完成了 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 用时

性能结果:

  • 导入耗时:75.746 秒;
  • 平均写入吞吐:约 133,342 行/秒;
  • 相比 DTS 普通迁移的 272 秒,耗时明显缩短;
  • 相比 DTS 的平均 37,132 行/秒,写入吞吐约提高 3.6 倍。

3.3 OUTORA 导出文件导入

测试前执行:

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 线程:26.170 秒;
  • 四进程、每进程 1 线程:26.644 秒;
  • 四进程、每进程 4 线程:26.336 秒。

四进程并行没有明显超过单进程 4 线程,差异约为 0.6%~1.8%。

为什么并行没有明显加速

原因主要有三点:

  1. 单进程默认已经使用多线程:本机 4 个 CPU,单个 dmfldr 默认任务线程数也是 4;
  2. 四进程并行没有增加有效处理能力:四进程每进程 1 线程时,总线程数仍约为 4;每进程 4 线程时,总线程数达到 16,属于过度并发;
  3. 目标端是同一张表和同一套直接装载路径:多个客户端最终仍要经过同一个 DM8 实例、同一张表的数据处理和刷盘流程,瓶颈可能位于目标端服务器处理、日志或数据文件。

DM8 手册中 PARALLEL=TRUE 的作用是允许多个用户同时向同一张表装载数据,并不代表多个 dmfldr 进程一定获得线性加速。在当前 4 CPU 虚拟机和无二级索引目标表条件下,单个 dmfldr 使用 4 个任务线程已经接近有效吞吐上限。增加到四个 dmfldr 进程没有带来明显收益,甚至可能造成线程过度竞争。

本轮最终数据校验仍为:

目标表行数:10,100,000
坏行:0
数据格式错误:0

3.4 导入性能对比

装载方式 文件来源 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 行/秒

4. dmfldr OUTORA 导出的时间格式问题

一开始dmfldr OUTORA的导出是有问题的。

4.1 异常现象

初始 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

4.2 不同 CTL 配置的测试结果

CTL 定义 结果 判定
EVENT_TIME, 时间尾数异常 不合格
EVENT_TIME DATE, 时间尾数仍异常 不合格
EVENT_TIME DATE FORMAT 'YYYY-MM-DD HH24:MI:SS.FF6', 与预期文本一致 合格

在业务大表的复测中,修正后的 OUTORA 文件与 SQLULDR2 显式格式导出的对照文件进行比较,LINE_IDEVENT_TIMEEVENT_DATE 均无差异。

4.3 正确配置

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
)

配置原则:

  1. 每个 Oracle DATE/TIMESTAMP 列都显式指定格式;
  2. FF 位数根据源列精度和目标列精度确定;
  3. 带时区时间需要单独设计格式和验证用例;
  4. 不能只靠数据库或客户端默认日期格式;
  5. 全表导出前先做少量数据逐字段对比。

4.4 问题原因

本次异常的直接原因是:OUTORA 控制文件没有为 Oracle DATE/TIMESTAMP 列指定完整的文本格式。

只写列名或只写 DATE 时,工具虽然能够完成导出流程,但输出文本不一定能够准确表示源端时间值。显式增加 DATE FORMAT 后,时间字段恢复正确,因此本次问题应归因于 CTL 配置不完整。


5. 数据校验与结论

5.1 文件校验

大表导入前,至少检查以下项目:

  • 文件行数;
  • 文件字节数;
  • 文件 SHA-256;
  • 分片行数和边界;
  • 每行字段数;
  • NULL 标记是否一致;
  • 日期时间格式是否正确;
  • 中文、特殊字符和长文本是否完整。

5.2 数据校验

在 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;

重点对比:

  • 总行数;
  • 主键范围;
  • 数量汇总;
  • 金额汇总;
  • NULL 数量;
  • 时间边界;
  • 租户和状态分组结果。

本次 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_IDORDER_STATUS 生成的 60 组统计结果也完成了源端、目标端对照,行数和金额汇总一致。

5.3 业务校验

基础数据校验通过后,再完成以下工作:

  1. 创建主键、唯一约束和外键;
  2. 创建必要的二级索引;
  3. 收集统计信息;
  4. 验证关键查询和聚合 SQL;
  5. 检查报表结果和业务数据;
  6. 对源端与目标端的时间值进行统一格式化后比较;
  7. 启用必要触发器并完成业务回归。

时间字段比较不能简单依赖 JDBC 或客户端显示字符串。对于带时区时间,应统一会话时区并比较同一 UTC 语义;对于无时区时间,应使用明确格式比较实际日期、时间和小数秒。

5.4 结论

本案例完成了 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 导入均验证通过。

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服