跳到主要内容
最后 更新

写入 Doris 数据

Flink Doris Connector 把 Flink 中的数据攒批后通过 Stream Load 写入 Doris。本页介绍写入模式、FlinkSQL 写入方法、常见写入场景以及 Sink 配置项;DataStream API 的写入方式见 DataStream API 读写

写入模式

写入数据时,Flink Doris Connector 会在 Flink 内存中攒批,再通过 Stream Load 批量导入 Doris。Connector 提供两种攒批模式,默认使用基于 Flink Checkpoint 的流式写入:

对比项流式写入批量写入
触发条件依赖 Flink Checkpoint,跟随 Checkpoint 周期写入到 Doris基于 Connector 内的时间阈值、数据量阈值周期性提交
一致性Exactly-OnceAt-Least-Once,基于主键模型可保证 Exactly-Once
延迟受 Checkpoint 时间间隔限制,通常较高独立的批处理机制,灵活调整
容错与恢复与 Flink 状态恢复完全一致依赖外部去重逻辑(如 Doris 主键去重)
  • 流式写入(默认):必须开启 Checkpoint,在整个 Checkpoint 期间持续将上游数据写入 Doris,不会一直将数据缓存在内存中;通过两阶段提交保证 Exactly-Once。任务重启时需要从最新的 Checkpoint/Savepoint 恢复,否则会因 label 重复而报错(见 常见问题)。
  • 攒批写入:Connector 1.5.0 起支持。不依赖 Checkpoint,将数据缓存在内存中,由 sink.buffer-flush.max-rowssink.buffer-flush.max-bytessink.buffer-flush.interval 控制写入时机,通过 'sink.enable.batch-mode' = 'true' 开启。不保证 Exactly-Once,可借助 Unique 模型做到幂等写入。

除通过 Stream Load 写入外,还可以设置 'sink.write-mode' = 'TVF',先将数据暂存到 S3 对象存储,再通过 S3 表值函数导入 Doris,见 使用 S3 TVF 写入

通过 Flink 的 Datagen 模拟上游持续产生的数据:

-- 启用 checkpoint
SET 'execution.checkpointing.interval' = '30s';

CREATE TABLE student_source (
id INT,
name STRING,
age INT
) WITH (
'connector' = 'datagen',
'rows-per-second' = '1',
'fields.name.length' = '20',
'fields.id.min' = '1',
'fields.id.max' = '100000',
'fields.age.min' = '3',
'fields.age.max' = '30'
);

-- doris sink
CREATE TABLE student_sink (
id INT,
name STRING,
age INT
)
WITH (
'connector' = 'doris',
'fenodes' = '10.16.10.6:28737',
'table.identifier' = 'test.student',
'username' = 'root',
'password' = 'password',
'sink.label-prefix' = 'doris_label'
-- 'sink.enable.batch-mode' = 'true' 增加该配置可以走攒批写入
);

INSERT INTO student_sink SELECT * FROM student_source;

使用 S3 TVF 写入

TVF 写入模式先将数据以 JSON 格式暂存至 S3 对象存储,再通过 S3 表值函数导入 Doris。适合优先使用对象存储作为数据传输通道,或无法使用 Stream Load 网络链路的场景。

使用前,确保 Flink 和 Doris 均可访问 S3,且 Doris 中已创建目标表。

SET 'execution.checkpointing.interval' = '30s';

CREATE TABLE student_tvf_sink (
id INT,
name STRING,
age INT
) WITH (
'connector' = 'doris',
'fenodes' = '127.0.0.1:8030',
'jdbc-url' = 'jdbc:mysql://127.0.0.1:9030',
'table.identifier' = 'test.student_tvf',
'username' = 'root',
'password' = '',
'sink.write-mode' = 'TVF',
'sink.label-prefix' = 'student_tvf',
'sink.s3.endpoint' = 'https://s3.example.com',
'sink.s3.region' = 'us-east-1',
'sink.s3.bucket' = 'staging-bucket',
'sink.s3.prefix' = 'doris/student',
'sink.s3.access-key' = 'access-key',
'sink.s3.secret-key' = 'secret-key'
);

INSERT INTO student_tvf_sink VALUES (1, 'Alice', 18);

Connector 不会自动删除 S3 中的暂存对象,请按需配置对象存储生命周期策略。

常见写入场景

部分列更新

通过 Stream Load 参数 partial_columns 开启部分列更新,并在 columns 中指定需要更新的列。表侧的要求见 部分列更新

CREATE TABLE doris_sink (
id INT,
name STRING,
bank STRING,
age int
)
WITH (
'connector' = 'doris',
'fenodes' = '127.0.0.1:8030',
'table.identifier' = 'database.table',
'username' = 'root',
'password' = '',
'sink.properties.format' = 'json',
'sink.properties.read_json_by_line' = 'true',
'sink.properties.columns' = 'id,name,bank,age', -- 需要更新的列
'sink.properties.partial_columns' = 'true' -- 开启部分列更新
);

导入 Bitmap 数据

sink.properties.columns 中通过 to_bitmap 函数将整型列转换为 Bitmap:

CREATE TABLE bitmap_sink (
dt int,
page string,
user_id int
)
WITH (
'connector' = 'doris',
'fenodes' = '127.0.0.1:8030',
'table.identifier' = 'test.bitmap_test',
'username' = 'root',
'password' = '',
'sink.label-prefix' = 'doris_label',
'sink.properties.columns' = 'dt,page,user_id,user_id=to_bitmap(user_id)'
)

根据指定列删除数据

Kafka 中的消息通常会使用特定字段来标记操作类型,例如 {"op_type":"delete",data:{...}}。针对这类数据,希望将 op_type=delete 的数据删除。

DorisSink 默认会根据 RowKind 来区分事件的类型,CDC 情况下可直接获取到事件类型,对隐藏列 __DORIS_DELETE_SIGN__ 进行赋值达到删除的目的。Kafka 则需要根据业务逻辑判断,显式地传入隐藏列的值。

-- 比如上游数据:{"op_type":"delete",data:{"id":1,"name":"zhangsan"}}
CREATE TABLE KAFKA_SOURCE(
data STRING,
op_type STRING
) WITH (
'connector' = 'kafka',
...
);

CREATE TABLE DORIS_SINK(
id INT,
name STRING,
__DORIS_DELETE_SIGN__ INT
) WITH (
'connector' = 'doris',
'fenodes' = '127.0.0.1:8030',
'table.identifier' = 'db.table',
'username' = 'root',
'password' = '',
'sink.enable-delete' = 'false', -- false 表示不从 RowKind 获取事件类型
'sink.properties.columns' = 'id, name, __DORIS_DELETE_SIGN__' -- 显式指定 Stream Load 的导入列
);

INSERT INTO DORIS_SINK
SELECT json_value(data, '$.id') as id,
json_value(data, '$.name') as name,
if(op_type = 'delete', 1, 0) as __DORIS_DELETE_SIGN__
from KAFKA_SOURCE;

写入 CDC 变更数据

上游是 Flink CDC 或 Debezium 格式的变更数据时,DorisSink 会根据 RowKind 区分 insert、update、delete 事件,删除事件通过 sink.enable-delete 同步到 Doris(需要 Unique 模型)。完整示例见 整库同步(Flink CDC);DataStream API 使用 JsonDebeziumSchemaSerializer,见 Debezium 格式

配置项

以下为 Sink 配置项。连接相关的通用配置项见 连接配置与 TLS

KeyDefault ValueRequiredComment
sink.label-prefix--Y导入使用的 label 前缀。2PC 场景下要求全局唯一,用来保证 Flink 的 EOS 语义。
sink.properties.*--NStream Load 的导入参数。例如:'sink.properties.column_separator' = ', ' 定义列分隔符;'sink.properties.escape_delimiters' = 'true' 表示特殊字符作为分隔符,\x01 会被转换为二进制的 0x01;JSON 格式导入:'sink.properties.format' = 'json''sink.properties.read_json_by_line' = 'true',详细参数参考 Stream Load。Group Commit 模式:'sink.properties.group_commit' = 'sync_mode' 设置 group commit 为同步模式。Flink Connector 从 1.6.2 开始支持导入配置 group commit,详细使用与限制参考 Group Commit。从 26.1.0 开始 Stream Load 默认启用 gz 压缩,可通过设置 'sink.properties.compress_type' = '' 关闭压缩。
sink.enable-deleteTRUEN是否启用删除。此选项需要 Doris 表开启批量删除功能(Doris 0.15+ 版本默认开启),只支持 Unique 模型。
sink.enable-2pcTRUEN是否开启两阶段提交(2pc),默认为 true,保证 Exactly-Once 语义。关于两阶段提交可参考 Stream Load 2PC
sink.write-modeSTREAM_LOADN写入模式,支持 STREAM_LOADSTREAM_LOAD_BATCHTVF
sink.s3.endpoint--仅 TVF 写入模式兼容 S3 的对象存储 endpoint
sink.s3.region--仅 TVF 写入模式对象存储 region
sink.s3.bucket--仅 TVF 写入模式暂存文件使用的 bucket
sink.s3.prefix--仅 TVF 写入模式暂存文件的对象 key 前缀,不能包含 glob 字符
sink.s3.access-key--仅 TVF 写入模式对象存储 access key
sink.s3.secret-key--仅 TVF 写入模式对象存储 secret key
sink.s3.path-style-accessFALSENTVF 模式是否使用 path-style 对象存储访问方式
sink.buffer-size1MBN写数据缓存 buffer 大小,单位字节。不建议修改,默认配置即可
sink.buffer-count3N写数据缓存 buffer 个数。不建议修改,默认配置即可
sink.max-retries3NCommit 失败后的最大重试次数,默认 3 次
sink.enable.batch-modeFALSEN是否使用攒批模式写入 Doris。开启后写入时机不依赖 Checkpoint,通过 sink.buffer-flush.max-rowssink.buffer-flush.max-bytessink.buffer-flush.interval 参数来控制写入时机。同时开启后将不保证 Exactly-Once 语义,可借助 Unique 模型做到幂等
sink.flush.queue-size2N攒批模式下,缓存的队列大小
sink.buffer-flush.max-rows500000N攒批模式下,单个批次最多写入的数据行数
sink.buffer-flush.max-bytes100MBN攒批模式下,单个批次最多写入的字节数
sink.buffer-flush.interval10sN攒批模式下,异步刷新缓存的间隔
sink.ignore.update-beforeTRUEN是否忽略 update-before 事件,默认忽略

监控指标

Flink 提供了多种 Metrics 用于监测 Flink 集群的指标。以下为 Flink Doris Connector 新增的监控指标:

NameMetric TypeDescription
totalFlushLoadBytesCounter已经刷新导入的总字节数
flushTotalNumberRowsCounter已经导入处理的总行数
totalFlushLoadedRowsCounter已经成功导入的总行数
totalFlushTimeMsCounter已经成功导入完成的总时间
totalFlushSucceededNumberCounter已经成功导入的次数
totalFlushFailedNumberCounter失败导入的次数
totalFlushFilteredRowsCounter数据质量不合格的总行数
totalFlushUnselectedRowsCounter被 where 条件过滤的总行数
beginTxnTimeMsHistogram向 FE 请求开始一个事务所花费的时间,单位毫秒
putDataTimeMsHistogram向 FE 请求获取导入数据执行计划所花费的时间
readDataTimeMsHistogram读取数据所花费的时间
writeDataTimeMsHistogram执行写入数据操作所花费的时间
commitAndPublishTimeMsHistogram向 FE 请求提交并且发布事务所花费的时间
loadTimeMsHistogram导入完成的时间