Skip to main content
Last updated on

Writing Data to Doris

Flink Doris Connector batches data in Flink and writes it to Doris through Stream Load. This page covers the write modes, writing with FlinkSQL, common write scenarios, and the Sink options. For writing with the DataStream API, see DataStream API.

Write Modes

When writing data, Flink Doris Connector batches data in Flink memory and then bulk imports it into Doris via Stream Load. The Connector provides two batching modes, with streaming writes based on Flink Checkpoint as the default:

Comparison ItemStreaming WriteBatch Write
Trigger conditionDepends on Flink Checkpoint, writes to Doris with each Checkpoint cyclePeriodically commits based on time and data-volume thresholds within the Connector
ConsistencyExactly-OnceAt-Least-Once; Exactly-Once can be guaranteed with the primary key model
LatencyLimited by Checkpoint interval, usually higherIndependent batch processing mechanism, flexible to adjust
Fault tolerance and recoveryFully consistent with Flink state recoveryRelies on external deduplication logic (such as Doris primary key deduplication)
  • Streaming write (default): Checkpoint must be enabled. Upstream data is continuously written to Doris throughout the Checkpoint period instead of being kept in memory, and two-phase commit guarantees Exactly-Once. When the job restarts, it must recover from the latest Checkpoint/Savepoint; otherwise the write fails with a duplicate label error (see FAQ).
  • Batch write: Supported since Connector 1.5.0. It does not depend on Checkpoint: data is cached in memory and flushed according to sink.buffer-flush.max-rows, sink.buffer-flush.max-bytes, and sink.buffer-flush.interval. Enable it with 'sink.enable.batch-mode' = 'true'. Exactly-Once is not guaranteed; idempotent writes can be achieved with the Unique model.

Besides Stream Load, you can set 'sink.write-mode' = 'TVF' to stage data in S3-compatible object storage first and then load it into Doris through the S3 table-valued function. See Writing with S3 TVF.

Use Flink's Datagen to simulate data continuously produced by upstream:

-- Enable 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' Add this configuration to use batch write
);

INSERT INTO student_sink SELECT * FROM student_source;

Writing with S3 TVF

TVF write mode first stages data in JSON format in S3 object storage and then imports it into Doris through the S3 table-valued function. Use this mode when object storage is the preferred data transfer path or when the Stream Load network path is unavailable.

Before use, ensure that both Flink and Doris can access S3 and that the target table already exists in 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);

The Connector does not automatically delete staged objects from S3. Configure an object-storage lifecycle policy as needed.

Common Write Scenarios

Partial Column Updates

Set the Stream Load parameter partial_columns to enable partial column updates and list the columns to update in columns. For the table-side requirements, see Partial Column Update.

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', -- Columns to update
'sink.properties.partial_columns' = 'true' -- Enable partial column update
);

Importing Bitmap Data

Convert an integer column to Bitmap with the to_bitmap function in sink.properties.columns:

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

Deleting Data Based on a Specified Column

Messages in Kafka often use a specific field to mark the operation type, for example {"op_type":"delete",data:{...}}. For this kind of data, you may want to delete records where op_type=delete.

By default, DorisSink distinguishes event types based on RowKind. In the CDC case, the event type can be obtained directly, and the hidden column __DORIS_DELETE_SIGN__ is assigned a value to achieve deletion. For Kafka, the application logic must determine the value, which is then explicitly passed in for the hidden column.

-- For example, upstream data: {"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 means do not get the event type from RowKind
'sink.properties.columns' = 'id, name, __DORIS_DELETE_SIGN__' -- Explicitly specify the import columns of 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;

Writing CDC Changelog Data

When the upstream is Flink CDC or Debezium-format change data, DorisSink distinguishes insert, update, and delete events by RowKind, and delete events are synchronized to Doris through sink.enable-delete (Unique model required). For a complete example, see Full-Database Sync (Flink CDC); with the DataStream API, use JsonDebeziumSchemaSerializer as described in Debezium Format.

Options

The following are the Sink options. The general connection options are listed in Connection Options and TLS.

KeyDefault ValueRequiredComment
sink.label-prefix--YThe label prefix used for imports. In 2PC scenarios, it must be globally unique to guarantee Flink EOS semantics.
sink.properties.*--NStream Load import parameters. For example: 'sink.properties.column_separator' = ', ' defines the column separator; 'sink.properties.escape_delimiters' = 'true' indicates that special characters are used as separators, and \x01 will be converted to the binary 0x01; for JSON-format imports: 'sink.properties.format' = 'json', 'sink.properties.read_json_by_line' = 'true'. For detailed parameters, see Stream Load. Group Commit mode: 'sink.properties.group_commit' = 'sync_mode' sets group commit to synchronous mode. Flink Connector supports configuring group commit for imports starting from 1.6.2. For detailed usage and limitations, see Group Commit. Since 26.1.0, gz compression is enabled by default for Stream Load; it can be disabled by setting 'sink.properties.compress_type' = ''.
sink.enable-deleteTRUENWhether to enable deletion. This option requires the Doris table to have batch deletion enabled (enabled by default in Doris 0.15+) and only supports the Unique model.
sink.enable-2pcTRUENWhether to enable two-phase commit (2pc). The default is true, which guarantees Exactly-Once semantics. For information on two-phase commit, see Stream Load 2PC.
sink.write-modeSTREAM_LOADNWrite mode. Supported values: STREAM_LOAD, STREAM_LOAD_BATCH, and TVF.
sink.s3.endpoint--TVF write mode onlyS3-compatible object-storage endpoint.
sink.s3.region--TVF write mode onlyObject-storage region.
sink.s3.bucket--TVF write mode onlyBucket used to stage files.
sink.s3.prefix--TVF write mode onlyObject key prefix for staged files. The prefix cannot contain glob characters.
sink.s3.access-key--TVF write mode onlyObject-storage access key.
sink.s3.secret-key--TVF write mode onlyObject-storage secret key.
sink.s3.path-style-accessFALSENWhether TVF mode uses path-style object-storage access.
sink.buffer-size1MBNBuffer size for the write data cache, in bytes. Modifying this is not recommended; the default configuration is sufficient.
sink.buffer-count3NNumber of write data cache buffers. Modifying this is not recommended; the default configuration is sufficient.
sink.max-retries3NThe maximum number of retries after a Commit failure. The default is 3.
sink.enable.batch-modeFALSENWhether to use batch mode to write to Doris. When enabled, the write timing does not depend on Checkpoint and is controlled by the sink.buffer-flush.max-rows, sink.buffer-flush.max-bytes, and sink.buffer-flush.interval parameters. Once enabled, Exactly-Once semantics is no longer guaranteed. The Unique model can be used to achieve idempotency.
sink.flush.queue-size2NIn batch mode, the size of the cache queue.
sink.buffer-flush.max-rows500000NIn batch mode, the maximum number of rows written in a single batch.
sink.buffer-flush.max-bytes100MBNIn batch mode, the maximum number of bytes written in a single batch.
sink.buffer-flush.interval10sNIn batch mode, the interval for asynchronous cache flushing.
sink.ignore.update-beforeTRUENWhether to ignore update-before events. The default is to ignore them.

Monitoring Metrics

Flink provides various Metrics for monitoring Flink cluster metrics. The following are the new monitoring metrics added by Flink Doris Connector:

NameMetric TypeDescription
totalFlushLoadBytesCounterThe total number of bytes that have been flushed and imported.
flushTotalNumberRowsCounterThe total number of rows that have been imported and processed.
totalFlushLoadedRowsCounterThe total number of rows that have been successfully imported.
totalFlushTimeMsCounterThe total time elapsed for successfully completing the imports.
totalFlushSucceededNumberCounterThe number of successful imports.
totalFlushFailedNumberCounterThe number of failed imports.
totalFlushFilteredRowsCounterThe total number of rows whose data quality is unqualified.
totalFlushUnselectedRowsCounterThe total number of rows filtered out by the where condition.
beginTxnTimeMsHistogramThe time taken to request FE to begin a transaction, in milliseconds.
putDataTimeMsHistogramThe time taken to request FE to obtain the import data execution plan.
readDataTimeMsHistogramThe time taken to read data.
writeDataTimeMsHistogramThe time taken to perform the data write operation.
commitAndPublishTimeMsHistogramThe time taken to request FE to commit and publish the transaction.
loadTimeMsHistogramThe time taken to complete the import.