OceanBase × Flink 数据集成系列——旁路导入连接器的批量写入能力

Apache Flink 是一个开源的分布式流处理框架,广泛应用于实时和批量数据处理场景。OceanBase Flink DirectLoad 连接器(flink-connector-oceanbase-directload)是专门为 OceanBase 数据库设计的高性能数据导入工具,它利用 OceanBase 的旁路导入(Direct Load)技术,能够以极高的吞吐量将大批量数据快速写入数据库。

立即试用 OceanBase 企业版,体验国产数据库能力

180 天免费试用,零门槛开通

什么是旁路导入

旁路导入是 OceanBase 提供的一种高性能数据导入方式。与传统的 SQL INSERT 方式不同,旁路导入绕过了 SQL 层的解析和事务处理流程,直接将数据写入存储层,从而实现更高的导入性能。这种方式特别适合需要一次性导入大量数据的场景。

DirectLoad 连接器与普通 JDBC 连接器的对比

对比项 DirectLoad 连接器 JDBC 连接器
写入方式 旁路导入,直接写入存储层 通过 SQL INSERT 写入
吞吐量 极高,适合大批量数据 相对较低
适用场景 批量数据导入、数据迁移 实时流式写入、CDC 同步
数据流类型 仅支持有界流(Bounded) 支持无界流(Unbounded)
导入期间表状态 目标表被锁定,仅支持查询 表可正常读写
支持的操作 INSERT、UPDATE_AFTER INSERT、UPDATE、DELETE

核心优势

  • 超高吞吐量:相比传统 JDBC 方式,性能提升数倍甚至数十倍
  • 并行写入:支持多节点并行写入,充分利用集群资源
  • 简单易用:通过 Flink SQL 即可使用,无需复杂配置
  • 灵活的冲突处理:支持多种主键冲突处理策略(停止、替换、忽略)

功能特性与限制

在使用 DirectLoad 连接器前,请务必了解以下特性和限制,以确保正确使用。

✅ 支持的功能

  • 批量数据写入:专为大批量数据导入设计,支持千万级、亿级数据快速写入
  • 有界流处理:支持有界数据源(如文件、数据库快照等)
  • 多种导入模式:支持全量导入(full)、增量导入(inc)、增量替换导入(inc_replace)
  • 灵活的冲突策略:提供 STOP_ON_DUP、REPLACE、IGNORE 三种主键冲突处理策略
  • 并行写入:支持 Flink 多并行度写入,充分利用集群资源
  • 兼容性:支持 OceanBase MySQL 模式和 Oracle 模式

⚠️ 重要限制

  • 仅支持有界流:数据源必须是有界的(Bounded Stream),不支持持续不断的无界流写入
  • 导入期间锁表:旁路导入执行期间,目标表会被锁定,此时仅允许 SELECT 查询操作,不允许对该表进行 INSERT、UPDATE、DELETE 等写入操作
  • 不支持 DELETE 操作:Flink Table/SQL 侧仅支持 INSERT 与 UPDATE_AFTER 变更类型写入,不支持 DELETE、UPDATE_BEFORE
  • 批处理场景:推荐使用 Flink Batch 模式,不适合实时流式写入场景

注意:如果您需要实时流式写入或处理无界数据流,请使用项目中的 flink-connector-oceanbase 连接器,它基于 JDBC 实现,支持无界流和实时写入。

适用场景

DirectLoad 连接器适合以下场景:

  • 数据迁移:从其他数据库或数据仓库批量迁移数据到 OceanBase
  • 离线 ETL:定期批量处理和导入数据,如每日数据汇总
  • 历史数据导入:一次性导入大量历史数据
  • 数据初始化:为新系统批量初始化数据

不适用场景

以下场景不建议使用 DirectLoad 连接器:

  • 实时流式写入:需要持续不断写入数据的实时场景
  • CDC 数据同步:需要实时同步数据库变更的场景
  • 低延迟要求:对写入延迟有严格要求的场景
  • 高频小批量写入:频繁的小批量数据写入

版本要求与依赖

软件版本要求

软件 版本要求 说明
Apache Flink 1.15 或更高版本 推荐使用 1.15+ 以获得更好的性能和稳定性
JDK 8 或更高版本
OceanBase 满足以下任一版本范围: • 4.3.0.1 及以上 • 4.2.4.0 - 4.3.0.0 • 4.2.1.7 - 4.2.2.0 支持 MySQL 模式和 Oracle 模式
OceanBase (增量模式) 4.3.2 或更高版本 使用 inc 或 inc_replace 导入模式时需要

快速开始

本节将指导您快速上手使用 DirectLoad 连接器,从获取连接器到完成第一次数据导入。

步骤 1:获取连接器 JAR 包

DirectLoad 连接器通过 Flink SQL 使用,您需要下载包含所有依赖的 SQL JAR 文件。

下载地址:

下载名为 flink-sql-connector-oceanbase-directload-.jar 的文件。

将 JAR 文件放置到 Flink 的 lib 目录下:

复制代码
# 将 JAR 文件复制到 Flink lib 目录
cp flink-sql-connector-oceanbase-directload-<version>.jar $FLINK_HOME/lib/

说明:将 JAR 包放入 lib 目录后,在启动 Flink 集群时会自动加载。

步骤 2:准备工作

2.1 确认网络连通性

确保运行 Flink 作业的环境能够访问 OceanBase 数据库:

  • RPC 端口:默认为 2882(用于旁路导入)
  • SQL 端口:默认为 2881(用于连接验证)

您可以通过 telnet 或 nc 命令测试网络连通性:

复制代码
# 测试 RPC 端口连通性
telnet <oceanbase-host> 2882
# 或使用 nc 命令
nc -zv <oceanbase-host> 2882

2.2 准备 OceanBase 账号信息

您需要准备以下信息:

  • 租户名(tenant-name):如 test
  • 用户名(username):如 root(注意:不是连接串格式 root@test)
  • 密码(password)
  • 数据库名(schema-name):目标数据库名
  • 目标表名(table-name)

2.3 在 OceanBase 中创建目标表

在开始导入前,需要先在 OceanBase 中创建目标表。以下是一个示例:

复制代码
-- 连接到 OceanBase 数据库
USEtest;
-- 创建目标表
CREATETABLE`t_user` (
`id`INTNOTNULL,
`username`VARCHAR(50) DEFAULTNULL,
`age`INTDEFAULTNULL,
`score`DECIMAL(10,2) DEFAULTNULL,
  PRIMARY KEY (`id`)
) COMMENT'用户信息表';

部署完 JAR 包后,需要启动 Flink 集群:

复制代码
# 进入 Flink 安装目录
cd $FLINK_HOME
# 启动 Flink 集群(包括 JobManager 和 TaskManager)
./bin/start-cluster.sh

启动成功后,您可以访问 Flink Web UI 验证集群状态:

  • 默认地址:http://localhost:8081
  • 您应该能看到 JobManager 和至少一个 TaskManager 正在运行

常见问题排查:

如果启动失败,可以检查日志:

复制代码
# 查看 JobManager 日志
tail -f $FLINK_HOME/log/flink-*-standalonesession-*.log
# 查看 TaskManager 日志
tail -f $FLINK_HOME/log/flink-*-taskexecutor-*.log

现在,让我们通过 Flink SQL 完成第一次数据导入。

使用 SQL Client 来执行 SQL 语句并导入数据:

复制代码
# 进入 Flink 安装目录(如果还未在该目录)
cd $FLINK_HOME
# 启动 SQL Client
./bin/sql-client.sh

成功启动后,您将看到 Flink SQL Client 的交互式命令行界面:

复制代码
                                   ▒▓██▓██▒
                               ▓████▒▒█▓▒▓███▓▒
                            ▓███▓░░        ▒▒▒▓██▒  ▒
                          ░██▒   ▒▒▓▓█▓▓▒░      ▒████
                          ██▒         ░▒▓███▒    ▒█▒█▒
                            ░▓█            ███   ▓░▒██
                              ▓█       ▒▒▒▒▒▓██▓░▒░▓▓█
                            █░ █   ▒▒░       ███▓▓█ ▒█▒▒▒
                            ████░   ▒▓█▓      ██▒▒▒ ▓███▒
                         ░▒█▓▓██       ▓█▒    ▓█▒▓██▓ ░█░
                   ▓░▒▓████▒ ██         ▒█    █▓░▒█▒░▒█▒
                  ███▓░██▓  ▓█           █   █▓ ▒▓█▓▓█▒
                ░██▓  ░█░            █  █▒ ▒█████▓▒ ██▓░▒
               ███░ ░ █░          ▓ ░█ █████▒░░    ░█░▓  ▓░
              ██▓█ ▒▒▓▒          ▓███████▓░       ▒█▒ ▒▓ ▓██▓
           ▒██▓ ▓█ █▓█       ░▒█████▓▓▒░         ██▒▒  █ ▒  ▓█▒
           ▓█▓  ▓█ ██▓ ░▓▓▓▓▓▓▓▒              ▒██▓           ░█▒
           ▓█    █ ▓███▓▒░              ░▓▓▓███▓          ░▒░ ▓█
           ██▓    ██▒    ░▒▓▓███▓▓▓▓▓██████▓▒            ▓███  █
          ▓███▒ ███   ░▓▓▒░░   ░▓████▓░                  ░▒▓▒  █▓
          █▓▒▒▓▓██  ░▒▒░░░▒▒▒▒▓██▓░                            █▓
          ██ ▓░▒█   ▓▓▓▓▒░░  ▒█▓       ▒▓▓██▓    ▓▒          ▒▒▓
          ▓█▓ ▓▒█  █▓░  ░▒▓▓██▒            ░▓█▒   ▒▒▒░▒▒▓█████▒
           ██░ ▓█▒█▒  ▒▓▓▒  ▓█                █░      ░░░░   ░█▒
           ▓█   ▒█▓   ░     █░                ▒█              █▓
            █▓   ██         █░                 ▓▓        ▒█▓▓▓▒█░
             █▓ ░▓██░       ▓▒                  ▓█▓▒░░░▒▓█░    ▒█
              ██   ▓█▓░      ▒                    ░▒█▒██▒      ▓▓
               ▓█▒   ▒█▓▒░                         ▒▒ █▒█▓▒▒░░▒██
                ░██▒    ▒▓▓▒                     ▓██▓▒█▒ ░▓▓▓▓▒█▓
                  ░▓██▒                          ▓░  ▒█▓█  ░░▒▒▒
                      ▒▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░▓▓  ▓░▒█░
    ______ _ _       _       _____  ____  _         _____ _ _            _  BETA
   |  ____| (_)     | |     / ____|/ __ \| |       / ____| (_)          | |
   | |__  | |_ _ __ | | __ | (___ | |  | | |      | |    | |_  ___ _ __ | |_
   |  __| | | | '_ \| |/ /  \___ \| |  | | |      | |    | | |/ _ \ '_ \| __|
   | |    | | | | | |   <   ____) | |__| | |____  | |____| | |  __/ | | | |_
   |_|    |_|_|_| |_|_|\_\ |_____/ \___\_\______|  \_____|_|_|\___|_| |_|\__|
        Welcome! Enter 'HELP;' to list all available commands. 'QUIT;' to exit.
Flink SQL>

现在您可以在 SQL Client 中执行 SQL 语句了。

4.2 设置为 Batch 模式

DirectLoad 连接器专为批处理设计,强烈建议设置为 Batch 模式以获得最佳性能:

复制代码
-- 设置运行模式为 BATCH
SET 'execution.runtime-mode' = 'BATCH';
-- 可选:设置并行度(根据数据量调整)
SET 'parallelism.default' = '4';

说明:

  • execution.runtime-mode:设置为 BATCH 可以获得更好的批处理性能
  • parallelism.default:根据数据量和集群资源调整并行度

4.3 创建 DirectLoad Sink 表

在 Flink SQL 中创建对应的目标表,映射到 OceanBase 的 t_user 表:

复制代码
CREATE TABLE t_user (
idINT,
  username STRING,
  age INT,
  score DECIMAL(10,2),
  PRIMARY KEY (id) NOTENFORCED
) WITH (
'connector' = 'oceanbase-directload',
'host' = '127.0.0.1',                    -- OceanBase 主机地址
'port' = '2882',                          -- RPC 端口
'tenant-name' = 'test',                   -- 租户名
'username' = 'root',                      -- 用户名
'password' = 'your_password',             -- 密码
'schema-name' = 'test',                   -- 数据库名
'table-name' = 't_user'                   -- 表名
);

配置说明:

  • connector:固定值 oceanbase-directload
  • host 和 port:OceanBase 的主机地址和 RPC 端口(默认 2882)
  • tenant-name:租户名称
  • username 和 password:数据库用户凭证(注意:用户名不包含 @tenant 后缀)
  • schema-name 和 table-name:目标数据库和表名

注意:以上使用了最小参数集。更多参数配置请参考"配置参数详解"章节。

4.4 插入测试数据

复制代码
-- 插入几条测试数据
INSERT INTO t_user
VALUES 
  (1, 'Alice', 25, 95.5),
  (2, 'Bob', 30, 88.0),
  (3, 'Charlie', 28, 92.3),
  (4, 'David', 35, 87.8),
  (5, 'Eve', 22, 96.0);

执行上述语句后,Flink 将提交作业并开始导入数据。您会在控制台看到类似如下的输出:

复制代码
[INFO] Submitting SQL update statement to the cluster...
[INFO] SQL update statement has been successfully submitted to the cluster:
Job ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

作业执行说明:

  • 作业将在后台执行,您可以在 Flink Web UI(http://localhost:8081)中查看作业进度
  • 作业状态会经历:INITIALIZING → RUNNING → FINISHED
  • DirectLoad 的最终提交发生在作业结束阶段,请务必等待作业状态变为 FINISHED 后再验证数据

4.5 验证数据写入

步骤 1:检查作业状态

在 Flink Web UI(http://localhost:8081)中确认作业已经完成(状态为 FINISHED)。

步骤 2:查询 OceanBase 中的数据

等待作业成功完成后,在 OceanBase 客户端中查询验证:

复制代码
-- 连接到 OceanBase 数据库
USE test;
-- 查询写入的数据
SELECT * FROM t_user ORDER BY id;

预期结果:

您应该能看到刚才插入的 5 条数据:

复制代码
+----+----------+-----+-------+
| id | username | age | score |
+----+----------+-----+-------+
|  1 | Alice    |  25 | 95.50 |
|  2 | Bob      |  30 | 88.00 |
|  3 | Charlie  |  28 | 92.30 |
|  4 | David    |  35 | 87.80 |
|  5 | Eve      |  22 | 96.00 |
+----+----------+-----+-------+
5 rows in set

步骤 5:关闭和清理(可选)

完成测试后,您可以停止 Flink 集群:

复制代码
# 停止 Flink 集群
cd $FLINK_HOME
./bin/stop-cluster.sh

在 SQL Client 中退出:

复制代码
-- 在 SQL Client 中执行
QUIT;

或者直接按 Ctrl+D 退出 SQL Client。

重要提示

  • 等待作业完成:旁路导入的最终提交发生在作业结束阶段,请务必等待作业状态变为"FINISHED"后再验证数据
  • 导入期间锁表:在 INSERT 语句执行期间,目标表 t_user 会被锁定,此时只能查询该表,无法执行写入操作
  • 推荐使用 Batch 模式:DirectLoad 连接器仅支持有界流,推荐使用 Batch 模式并确保数据源是有界的
  • 错误处理:如果作业失败,请检查日志中的错误信息,常见问题包括网络连接失败、账号权限不足、参数配置错误等

配置参数详解

本节详细介绍 DirectLoad 连接器的所有配置参数。带 * 标记的参数为必填项。

连接参数

参数名 必填 默认值 类型 说明
connector* String 连接器类型,固定值:oceanbase-directload
host* String OceanBase 数据库的主机地址,如 127.0.0.1 或域名
port* 2882 Integer 旁路导入使用的 RPC 端口,默认为 2882
tenant-name* String 租户名称,如 test
username* String 数据库用户名,如 root。 注意:这里填写纯用户名,而不是连接串格式(如 root@test)
password* String 数据库密码
schema-name* String 数据库名(Schema),如 test
table-name* String 目标表名,如 t_user

导入行为参数

参数名 必填 默认值 类型 说明
load-method full String 导入模式,支持:full、inc、inc_replace 详见下文"load-method 详解"
dup-action REPLACE String 主键冲突处理策略,支持: - STOP_ON_DUP:遇到冲突立即停止导入 - REPLACE:替换已存在的记录 - IGNORE:忽略冲突记录 详见下文"dup-action 详解"
max-error-rows 0 Long 最大可容忍的错误行数。超过此阈值导入失败。 设置为 0 表示不容忍任何错误

性能调优参数

参数名 必填 默认值 类型 说明
parallel 8 Integer 服务端并行度。该参数决定了 OceanBase 服务端使用多少 CPU 资源来处理本次导入任务。 详见下文"parallel 详解"
buffer-size 1024 Integer 写入缓冲区大小(单位:行数)。累计到该行数后触发一次 flush 写入。 详见下文"buffer-size 调优建议"
timeout 7d Duration 单次旁路导入任务的超时时间。支持格式:1d、12h、30m、3600s
heartbeat-timeout 60s Duration 客户端心跳超时时间
heartbeat-interval 10s Duration 客户端心跳间隔时间

核心参数详细说明

load-method 详解

load-method 参数决定了旁路导入的模式,不同模式适用于不同场景:

  1. full(全量导入)
  • 默认值,适合空表或仅含少量数据的表

  • 导入的数据直接写入 major sstable 中

  • 列存表优势:导入完成后数据即为列存格式,查询性能最优,无需额外合并

  • 使用场景:

  • 首次导入数据到空表

  • 目标表数据量很小,可接受重建

  • 追求最优的列存查询性能

    -- full 模式示例
    CREATE TABLE t_sink (...) WITH (
    'connector' = 'oceanbase-directload',
    'load-method' = 'full',
    ...
    );

  1. inc(普通增量导入)
  • 适合向非空表追加数据

  • 会进行主键冲突检查,如果发现冲突按 dup-action 策略处理

  • 版本要求:OceanBase 4.3.2 及以上版本

  • 限制:暂不支持 dup-action 为 REPLACE

  • 列存表注意:数据写入转储(转储暂不支持列存),导入后查询性能为行存性能,需要等待一次合并才能达到列存查询性能

    -- inc 模式示例
    CREATE TABLE t_sink (...) WITH (
    'connector' = 'oceanbase-directload',
    'load-method' = 'inc',
    'dup-action' = 'STOP_ON_DUP', -- 不能使用 REPLACE
    ...
    );

  1. inc_replace(增量替换导入)
  • 适合向非空表导入数据,且需要覆盖已存在的记录

  • 不进行主键冲突检查,直接覆盖原有主键的数据(相当于 REPLACE 的效果)

  • 版本要求:OceanBase 4.3.2 及以上版本

  • dup-action 参数会被忽略(因为不检查冲突)

  • 列存表注意:与 inc 模式相同,需要合并后才能达到列存查询性能

    -- inc_replace 模式示例
    CREATE TABLE t_sink (...) WITH (
    'connector' = 'oceanbase-directload',
    'load-method' = 'inc_replace', -- dup-action 参数无效
    ...
    );

模式选择建议:

场景 推荐模式 理由
首次导入到空表 full 性能最优,列存表查询效果最佳
向非空表导入,可能有冲突且需要覆盖 inc_replace 自动覆盖旧数据
向非空表导入,不允许冲突 inc + dup-action=STOP_ON_DUP 严格控制数据一致性

dup-action 详解

dup-action 参数用于处理主键冲突的情况(仅在 load-method=full 或 inc 时生效):

  1. STOP_ON_DUP(停止导入)
  • 遇到主键冲突时立即停止导入,整个作业失败

  • 适用场景:

  • 对数据一致性要求严格

  • 不允许出现主键冲突

  • 需要快速发现数据质量问题

    'dup-action' = 'STOP_ON_DUP'

  1. REPLACE(替换)
  • 遇到主键冲突时,用新记录替换旧记录

  • 适用场景:

  • 允许覆盖已存在的数据

  • 导入的是最新版本数据

  • 注意:在 load-method=inc 时暂不支持

    'dup-action' = 'REPLACE'

  1. IGNORE(忽略)
  • 遇到主键冲突时,保留旧记录,忽略新记录

  • 适用场景:

  • 以旧数据为准

  • 增量导入时避免覆盖已存在的记录

    'dup-action' = 'IGNORE'

parallel 详解

parallel 参数是服务端并行度,它决定了 OceanBase 服务端使用多少 CPU 资源来处理本次导入任务。

重要特性:

  1. 与客户端并发无关:parallel 是服务端参数,与 Flink 的并行度是两个独立的概念
  2. 受租户配置限制:服务端会根据租户 CPU 配置自动限制并行度上限,客户端设置超出范围不会报错
  3. 受分区分布影响:实际并行度还受表的分区分布影响

实际并行度计算规则:

复制代码
单节点并行度上限 = MIN(租户CPU数 * 2, parallel 配置值)
实际总并行度 = 单节点并行度上限 * 分区分布的节点数

示例 1:单节点场景

  • 租户配置:2C
  • parallel 设置:10
  • 表的分区在 1 个节点
  • 实际并行度 = MIN(2 * 2, 10) * 1 = 4

示例 2:多节点场景

  • 租户配置:2C
  • parallel 设置:10
  • 表的分区均匀分布在 2 个节点
  • 实际并行度 = MIN(2 * 2, 10) * 2 = 8

调优建议:

  • 对于大数据量导入,适当调大 parallel 可以显著缩短 commit 阶段耗时

  • 建议根据租户 CPU 配置设置合理值,过大无效,过小影响性能

  • 一般设置为租户 CPU 数的 2-4 倍是合理的起点

    -- 示例:4C 租户建议设置
    'parallel' = '8' -- 或 '16'

buffer-size 调优建议

buffer-size 表示客户端写入缓冲区大小,单位是行数。累计到该行数后触发一次 flush 写入。

调优建议:

  • 数据量大且单行小:可适当调大(如 2048、4096),减少 flush 次数,提升吞吐
  • 单行数据大:避免设置过大,防止内存压力,建议保持默认值或适当调小
  • 内存紧张:减小 buffer-size,避免 TaskManager OOM

推荐值:

场景 推荐值
单行 < 1KB,内存充足 2048 - 4096
单行 1KB - 10KB 1024(默认值)
单行 > 10KB 或内存紧张 512
复制代码
-- 示例
'buffer-size' = '2048'

使用示例

本节提供更完整和实用的使用示例,涵盖不同场景和配置。

示例 1:基础批量导入

这是最基础的使用示例,适合初学者快速上手。

复制代码
-- 1. 设置为 Batch 模式
SET'execution.runtime-mode' = 'BATCH';
-- 2. 创建 Sink 表(使用最小参数集)
CREATETABLE orders_sink (
  order_id BIGINT,
  user_id BIGINT,
  product_name STRING,
  amount DECIMAL(10, 2),
  order_time TIMESTAMP(3),
  PRIMARY KEY (order_id) NOTENFORCED
) WITH (
'connector' = 'oceanbase-directload',
'host' = '127.0.0.1',
'port' = '2882',
'tenant-name' = 'test',
'username' = 'root',
'password' = 'your_password',
'schema-name' = 'test',
'table-name' = 'orders'
);
-- 3. 从数据源导入(这里假设从另一个表读取)
INSERTINTO orders_sink
SELECT order_id, user_id, product_name, amount, order_time
FROM orders_source;

示例 2:全量导入到空表(full 模式)

适用于首次导入大量数据到空表的场景。

复制代码
SET 'execution.runtime-mode' = 'BATCH';
SET'parallelism.default' = '8';  -- 根据数据量调整
CREATETABLE user_profile_sink (
  user_id BIGINT,
  username STRING,
  email STRING,
  age INT,
  city STRING,
  register_time TIMESTAMP(3),
  PRIMARY KEY (user_id) NOTENFORCED
) WITH (
'connector' = 'oceanbase-directload',
'host' = '127.0.0.1',
'port' = '2882',
'tenant-name' = 'prod',
'username' = 'admin',
'password' = 'your_password',
'schema-name' = 'userdb',
'table-name' = 'user_profile',
-- 使用 full 模式(适合空表)
'load-method' = 'full',
-- 性能调优
'parallel' = '16',  -- 服务端并行度
'buffer-size' = '2048'
);
-- 从 CSV 文件或其他数据源导入
INSERTINTO user_profile_sink
SELECT * FROM user_profile_csv_source;

示例 3:增量替换导入(inc_replace 模式)

适用于需要用新数据覆盖旧数据的场景。

复制代码
SET 'execution.runtime-mode' = 'BATCH';
CREATETABLE product_info_sink (
  product_id BIGINT,
  product_name STRING,
  price DECIMAL(10, 2),
  stock INT,
  update_time TIMESTAMP(3),
  PRIMARY KEY (product_id) NOTENFORCED
) WITH (
'connector' = 'oceanbase-directload',
'host' = '127.0.0.1',
'port' = '2882',
'tenant-name' = 'ecommerce',
'username' = 'root',
'password' = 'your_password',
'schema-name' = 'productdb',
'table-name' = 'product_info',
-- 使用 inc_replace 模式(自动覆盖已存在的数据)
'load-method' = 'inc_replace',  -- dup-action 参数会被忽略
-- 性能配置
'parallel' = '16',
'buffer-size' = '2048'
);
-- 导入更新数据(自动覆盖旧记录)
INSERTINTO product_info_sink
SELECT * FROM product_info_latest;

示例 4:从 Kafka 读取有界数据并导入

这个示例展示了如何从 Kafka 读取有界数据(通过 bounded 模式)并导入到 OceanBase。

复制代码
SET 'execution.runtime-mode' = 'BATCH';
-- 创建 Kafka Source(有界模式)
CREATETABLE kafka_source (
  event_id BIGINT,
  event_type STRING,
  user_id BIGINT,
  event_data STRING,
  event_time TIMESTAMP(3)
) WITH (
'connector' = 'kafka',
'topic' = 'user-events',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'flink-import-group',
'scan.startup.mode' = 'earliest-offset',
'scan.bounded.mode' = 'latest-offset',  -- 有界模式:读取到最新offset后停止
'format' = 'json'
);
-- 创建 OceanBase Sink
CREATETABLE event_sink (
  event_id BIGINT,
  event_type STRING,
  user_id BIGINT,
  event_data STRING,
  event_time TIMESTAMP(3),
  PRIMARY KEY (event_id) NOTENFORCED
) WITH (
'connector' = 'oceanbase-directload',
'host' = '127.0.0.1',
'port' = '2882',
'tenant-name' = 'analytics',
'username' = 'root',
'password' = 'your_password',
'schema-name' = 'eventdb',
'table-name' = 'events',
'load-method' = 'full',
'parallel' = '16'
);
-- 导入数据
INSERTINTO event_sink
SELECT * FROM kafka_source;

示例 6:从文件导入数据

从 CSV 文件批量导入数据到 OceanBase。

复制代码
SET 'execution.runtime-mode' = 'BATCH';
-- 创建文件 Source
CREATETABLE csv_source (
idBIGINT,
nameSTRING,
  age INT,
  salary DECIMAL(10, 2)
) WITH (
'connector' = 'filesystem',
'path' = 'file:///data/employees.csv',
'format' = 'csv',
'csv.field-delimiter' = ',',
'csv.ignore-parse-errors' = 'false'
);
-- 创建 OceanBase Sink
CREATETABLE employee_sink (
idBIGINT,
nameSTRING,
  age INT,
  salary DECIMAL(10, 2),
  PRIMARY KEY (id) NOTENFORCED
) WITH (
'connector' = 'oceanbase-directload',
'host' = '192.168.1.100',
'port' = '2882',
'tenant-name' = 'hrms',
'username' = 'admin',
'password' = 'your_password',
'schema-name' = 'hr',
'table-name' = 'employees',
'load-method' = 'full',
'parallel' = '8',
'buffer-size' = '2048',
'max-error-rows' = '10'-- 允许最多10行错误
);
-- 执行导入
INSERTINTO employee_sink
SELECT * FROM csv_source;

最佳实践

本节提供生产环境中使用 DirectLoad 连接器的最佳实践建议。

性能优化

Flink 并行度决定了有多少个并行的 Writer 任务。合理设置可以充分利用集群资源。

复制代码
-- 根据数据量和集群资源设置并行度
SET 'parallelism.default' = '8';

2. 调优 parallel 参数(服务端并行度)

parallel 参数对 commit 阶段性能影响很大。

调优策略:

  • 大数据量导入时,适当调大 parallel 可大幅缩短 commit 阶段耗时

  • 建议设置为租户 CPU 数的 2-4 倍

  • 不用担心设置过大,服务端会自动限制

    -- 示例:8C 租户
    'parallel' = '16' -- 或 '32'

性能对比示例

parallel commit 阶段耗时
8(默认) 约 10 分钟
16 约 5 分钟
32 约 3 分钟

3. 调优 buffer-size

根据数据特征调整缓冲区大小:

复制代码
-- 小行数据(每行 < 1KB)
'buffer-size' = '4096'
-- 中等行数据(每行 1-10KB)
'buffer-size' = '1024'  -- 默认值
-- 大行数据(每行 > 10KB)
'buffer-size' = '512'

生产环境建议

1. 选择合适的导入窗口期

由于旁路导入期间会锁表,建议:

  • 选择业务低峰期:如凌晨、周末

  • 提前通知相关方:避免影响其他业务

  • 设置合理的超时时间:防止导入任务长时间占用表

    -- 设置2小时超时(根据数据量评估)
    'timeout' = '2h'

2. 根据场景选择 load-method

场景 推荐 load-method 原因
首次导入到空表 full 性能最优,列存查询效果最佳
定期追加增量数据 inc 适合增量场景,支持冲突检查
每日全量更新维度表 inc_replace 自动覆盖旧数据,无需手动删除
历史数据回溯 full(导入到临时表后切换) 避免影响在线表

列存表使用建议

如果目标是列存表,需要注意不同 load-method 的影响:

full 模式(推荐):

  • ✅ 数据直接写为列存格式
  • ✅ 导入完成后查询性能最优
  • ⚠️ 仅适合空表或可接受重建的表

inc / inc_replace 模式:

  • ⚠️ 数据写入转储(行存格式)

  • ⚠️ 导入后查询性能为行存性能

  • ✅ 需要等待一次 major compaction 后才能达到列存性能

  • ⚠️ 如果追求高查询性能,建议手动触发合并:

    -- 在 OceanBase 中手动触发合并
    ALTER SYSTEM MAJOR FREEZE;

常见问题

Q1:导入期间其他写入操作失败怎么办?

问题描述:在执行旁路导入时,尝试对目标表进行 INSERT/UPDATE/DELETE 操作失败。

原因:这是旁路导入的固有特性。导入期间目标表会被锁定,仅允许 SELECT 操作。

解决方案:

  1. 方案一:错峰导入
  • 将导入任务安排在业务低峰期(如凌晨)

  • 提前与业务方沟通,暂停写入操作

  • 方案二:使用中间表

    -- 1. 导入到临时表
    CREATE TABLE target_table_tmp LIKE target_table;
    -- 使用 DirectLoad 导入到 target_table_tmp
    -- 2. 切换表名
    RENAME TABLE target_table TO target_table_old,
    target_table_tmp TO target_table;

    • 先导入到临时表
    • 导入完成后通过表切换或数据合并操作
  1. 方案三:使用普通 JDBC 连接器
  • 如果无法接受锁表,改用 flink-connector-oceanbase
  • 牺牲部分性能换取表的可用性

Q2:作业一直不结束/数据未提交怎么办?

问题描述:Flink 作业一直处于 RUNNING 状态,OceanBase 中查询不到数据。

原因:DirectLoad 连接器的最终 commit 发生在输入结束(end-of-input)阶段。如果输入是无界流,作业不会结束,数据也不会提交。

排查步骤:

  1. 检查是否设置了 Batch 模式:

    SET 'execution.runtime-mode' = 'BATCH';

  2. 检查数据源是否为有界流:

  • 文件数据源:天然有界 ✅
  • Kafka:需设置 scan.bounded.mode ✅
  • JDBC:天然有界 ✅
  • 自定义 Source:检查是否正确发送 end-of-input 信号
  • 查看 Flink Web UI:
  • 检查作业状态
  • 查看是否有 Backpressure
  • 检查各个算子的处理进度

Q3:如何选择 load-method?

决策树:

复制代码
是否为首次导入到空表?
├─ 是 → 使用 full 模式(性能最优,列存效果最佳)
└─ 否(向非空表导入)
    └─ 是否需要覆盖已存在的记录?
        ├─ 是 → 使用 inc_replace 模式
        └─ 否 → 使用 inc 模式
            └─ 是否允许主键冲突?
                ├─ 否 → dup-action = STOP_ON_DUP
                ├─ 允许(保留新数据) → dup-action = REPLACE
                └─ 允许(保留旧数据) → dup-action = IGNORE

特殊场景:

  • 列存表追求最优查询性能:优先使用 full 模式
  • OceanBase 版本 < 4.3.2:只能使用 full 模式
  • 需要频繁增量导入:使用 inc 或 inc_replace 模式

Q4:列存表使用 inc/inc_replace 模式后查询很慢怎么办?

问题描述:使用 inc 或 inc_replace 模式向列存表导入数据后,查询性能不如预期。

原因:inc/inc_replace 模式的数据写入转储(行存格式),需要等待一次 major compaction 后才能转为列存格式。

解决方案:

  1. 手动触发 major compaction(推荐):

    -- 在 OceanBase 中执行
    ALTER SYSTEM MAJOR FREEZE;
    -- 检查合并进度
    SELECT * FROM oceanbase.DBA_OB_MAJOR_COMPACTION;

  2. 等待自动合并: OceanBase 会定期自动触发合并,但可能需要等待较长时间(取决于配置)。

Q5:导入速度很慢怎么优化?

可能原因及解决方案:

  1. Flink 并行度过低:

    SET 'parallelism.default' = '16'; -- 增加并行度

  2. 服务端并行度过低:

    'parallel' = '16' -- 增加服务端并行度

  3. buffer-size 过小:

    'buffer-size' = '2048' -- 增大缓冲区

  4. OceanBase 资源不足:

  • 检查 OceanBase 集群负载
  • 考虑扩容或选择低峰期导入

参考资料


鸣谢

本文在整理与编写过程中,特别感谢生态团队孙朝阳老师、赵明远老师的大力支持与专业指导。

两位老师在 Flink 与 OceanBase 生态集成、Connector 能力梳理以及场景化实践总结方面提供了大量专业建议和宝贵经验。

在此谨致诚挚谢意。

立即试用 OceanBase 企业版,体验国产数据库能力

180 天免费试用,零门槛开通

相关推荐
格兰芬多呼神护卫1 小时前
具身机器人三层闭环控制架构—笔记1
架构·机器人
Swift社区10 小时前
鸿蒙 App 模块化拆分:架构解析 + 实战案例
华为·架构·harmonyos
@insist12310 小时前
系统架构设计师-实时性评价、调度算法与内核架构选型
算法·架构·系统架构·软考·系统架构设计师·软件水平考试
皮皮蟹虾饺14 小时前
DNS协议指南:从报文格式到安全加密与 K8s 实战
安全·容器·kubernetes
xiangw@GZ17 小时前
802.11全系列标准调制编码与速率档对应关系
网络·单片机·嵌入式硬件·架构
沪漂阿龙17 小时前
create_agent:LangChain 新版 Agent 的核心入口
人工智能·架构·langchain
带娃的IT创业者19 小时前
深度解析:从 GitHub 热门项目看 SEO 自动化的技术架构演进
架构·自动化·github·seo·技术架构·反爬虫
星辰_mya19 小时前
CountDownLatch深度解析
java·开发语言·后端·架构
lihongbao8019 小时前
kuboard v3创建用户分配命名空间
kubernetes·kuboard