Apache Flink 是一个开源的分布式流处理框架,广泛应用于实时和批量数据处理场景。OceanBase Flink DirectLoad 连接器(flink-connector-oceanbase-directload)是专门为 OceanBase 数据库设计的高性能数据导入工具,它利用 OceanBase 的旁路导入(Direct Load)技术,能够以极高的吞吐量将大批量数据快速写入数据库。
立即试用 OceanBase 企业版,体验国产数据库能力
什么是旁路导入
旁路导入是 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 文件。
下载地址:
- Maven Central: https://repo1.maven.org/maven2/com/oceanbase/flink-sql-connector-oceanbase-directload/
下载名为 flink-sql-connector-oceanbase-directload-.jar 的文件。
部署到 Flink
将 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'用户信息表';
步骤 3:启动 Flink 集群
部署完 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
步骤 4:第一个 Flink SQL 示例
现在,让我们通过 Flink SQL 完成第一次数据导入。
4.1 启动 Flink SQL Client
使用 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 参数决定了旁路导入的模式,不同模式适用于不同场景:
- full(全量导入)
-
默认值,适合空表或仅含少量数据的表
-
导入的数据直接写入 major sstable 中
-
列存表优势:导入完成后数据即为列存格式,查询性能最优,无需额外合并
-
使用场景:
-
首次导入数据到空表
-
目标表数据量很小,可接受重建
-
追求最优的列存查询性能
-- full 模式示例
CREATE TABLE t_sink (...) WITH (
'connector' = 'oceanbase-directload',
'load-method' = 'full',
...
);
- 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
...
);
- 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 时生效):
- STOP_ON_DUP(停止导入)
-
遇到主键冲突时立即停止导入,整个作业失败
-
适用场景:
-
对数据一致性要求严格
-
不允许出现主键冲突
-
需要快速发现数据质量问题
'dup-action' = 'STOP_ON_DUP'
- REPLACE(替换)
-
遇到主键冲突时,用新记录替换旧记录
-
适用场景:
-
允许覆盖已存在的数据
-
导入的是最新版本数据
-
注意:在 load-method=inc 时暂不支持
'dup-action' = 'REPLACE'
- IGNORE(忽略)
-
遇到主键冲突时,保留旧记录,忽略新记录
-
适用场景:
-
以旧数据为准
-
增量导入时避免覆盖已存在的记录
'dup-action' = 'IGNORE'
parallel 详解
parallel 参数是服务端并行度,它决定了 OceanBase 服务端使用多少 CPU 资源来处理本次导入任务。
重要特性:
- 与客户端并发无关:parallel 是服务端参数,与 Flink 的并行度是两个独立的概念
- 受租户配置限制:服务端会根据租户 CPU 配置自动限制并行度上限,客户端设置超出范围不会报错
- 受分区分布影响:实际并行度还受表的分区分布影响
实际并行度计算规则:
单节点并行度上限 = 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 连接器的最佳实践建议。
性能优化
1. 合理设置 Flink 并行度
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. 导入到临时表
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;- 先导入到临时表
- 导入完成后通过表切换或数据合并操作
- 方案三:使用普通 JDBC 连接器
- 如果无法接受锁表,改用 flink-connector-oceanbase
- 牺牲部分性能换取表的可用性
Q2:作业一直不结束/数据未提交怎么办?
问题描述:Flink 作业一直处于 RUNNING 状态,OceanBase 中查询不到数据。
原因:DirectLoad 连接器的最终 commit 发生在输入结束(end-of-input)阶段。如果输入是无界流,作业不会结束,数据也不会提交。
排查步骤:
-
检查是否设置了 Batch 模式:
SET 'execution.runtime-mode' = 'BATCH';
-
检查数据源是否为有界流:
- 文件数据源:天然有界 ✅
- 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 后才能转为列存格式。
解决方案:
-
手动触发 major compaction(推荐):
-- 在 OceanBase 中执行
ALTER SYSTEM MAJOR FREEZE;
-- 检查合并进度
SELECT * FROM oceanbase.DBA_OB_MAJOR_COMPACTION; -
等待自动合并: OceanBase 会定期自动触发合并,但可能需要等待较长时间(取决于配置)。
Q5:导入速度很慢怎么优化?
可能原因及解决方案:
-
Flink 并行度过低:
SET 'parallelism.default' = '16'; -- 增加并行度
-
服务端并行度过低:
'parallel' = '16' -- 增加服务端并行度
-
buffer-size 过小:
'buffer-size' = '2048' -- 增大缓冲区
-
OceanBase 资源不足:
- 检查 OceanBase 集群负载
- 考虑扩容或选择低峰期导入
参考资料
- OceanBase 旁路导入官方文档:https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001428636
- Flink 官方文档:https://nightlies.apache.org/flink/flink-docs-stable/
- 项目 GitHub 仓库:https://github.com/oceanbase/flink-connector-oceanbase
- obkv-table-client 项目:https://github.com/oceanbase/obkv-table-client-java
- OceanBase 文档:https://www.oceanbase.com/docs/oceanbase-cloud
- Flink Table API 文档:https://nightlies.apache.org/flink/flink-docs-stable/docs/dev/table/overview/
鸣谢
本文在整理与编写过程中,特别感谢生态团队孙朝阳老师、赵明远老师的大力支持与专业指导。
两位老师在 Flink 与 OceanBase 生态集成、Connector 能力梳理以及场景化实践总结方面提供了大量专业建议和宝贵经验。
在此谨致诚挚谢意。
立即试用 OceanBase 企业版,体验国产数据库能力