Skip to main content
Skip to main content

Flink Connector

ClickHouse Supported

This is the official Apache Flink Sink Connector supported by ClickHouse. It is built using Flink's AsyncSinkBase and the official ClickHouse java client.

The connector supports Apache Flink's DataStream API. Table API support is planned for a future release.

Requirements​

  • Java 11+ (for Flink 1.17+) or 17+ (for Flink 2.0+)
  • Apache Flink 1.17+

The connector is split into two artifacts to support both Flink 1.17+ and Flink 2.0+. Choose the artifact that matches your desired Flink version:

Flink VersionArtifactClickHouse Java Client VersionRequired Java
latestflink-connector-clickhouse-2.0.00.9.5Java 17+
2.0.1flink-connector-clickhouse-2.0.00.9.5Java 17+
2.0.0flink-connector-clickhouse-2.0.00.9.5Java 17+
1.20.2flink-connector-clickhouse-1.170.9.5Java 11+
1.19.3flink-connector-clickhouse-1.170.9.5Java 11+
1.18.1flink-connector-clickhouse-1.170.9.5Java 11+
1.17.2flink-connector-clickhouse-1.170.9.5Java 11+
Note

The connector has not been tested against Flink versions earlier than 1.17.2

Installation & setup​

Import as a dependency​

<dependency>
    <groupId>com.clickhouse.flink</groupId>
    <artifactId>flink-connector-clickhouse-2.0.0</artifactId>
    <version>{{ stable_version }}</version>
    <classifier>all</classifier>
</dependency>
<dependency>
    <groupId>com.clickhouse.flink</groupId>
    <artifactId>flink-connector-clickhouse-1.17</artifactId>
    <version>{{ stable_version }}</version>
    <classifier>all</classifier>
</dependency>

Download the binary​

The name pattern of the binary JAR is:

flink-connector-clickhouse-${flink_version}-${stable_version}-all.jar

where:

You can find all available released JAR files in the Maven Central Repository.

Using the DataStream API​

Snippet​

Let's say you want to insert raw CSV data into ClickHouse:

public static void main(String[] args) {
    // Configure ClickHouseClient
    ClickHouseClientConfig clickHouseClientConfig = new ClickHouseClientConfig(url, username, password, database, tableName);

    // Create an ElementConverter
    ElementConverter<String, ClickHousePayload> convertorString = new ClickHouseConvertor<>(String.class);

    // Create the sink and set the format using `setClickHouseFormat`
    ClickHouseAsyncSink<String> csvSink = new ClickHouseAsyncSink<>(
            convertorString,
            MAX_BATCH_SIZE,
            MAX_IN_FLIGHT_REQUESTS,
            MAX_BUFFERED_REQUESTS,
            MAX_BATCH_SIZE_IN_BYTES,
            MAX_TIME_IN_BUFFER_MS,
            MAX_RECORD_SIZE_IN_BYTES,
            clickHouseClientConfig
    );

    csvSink.setClickHouseFormat(ClickHouseFormat.CSV);

    // Finally, connect your DataStream to the sink.
    final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

    Path csvFilePath = new Path(fileFullName);
    FileSource<String> csvSource = FileSource
            .forRecordStreamFormat(new TextLineInputFormat(), csvFilePath)
            .build();

    env.fromSource(
            csvSource,
            WatermarkStrategy.noWatermarks(),
            "GzipCsvSource"
    ).sinkTo(csvSink);
}

More examples and snippets can be found in our tests:

Quick start example​

We have created maven-based example for an easy start with the ClickHouse Sink:

For more detailed instructions, see the Example Guide

DataStream API connection options​

ClickHouse client options​

ParametersDescriptionDefault ValueRequired
urlFully qualified Clickhouse URLN/AYes
usernameClickHouse database usernameN/AYes
passwordClickHouse database passwordN/AYes
databaseClickHouse database nameN/AYes
tableClickHouse table nameN/AYes
optionsMap of Java client configuration optionsEmpty mapNo
serverSettingsMap of ClickHouse server session settingsEmpty mapNo
enableJsonSupportAsStringClickHouse server setting to expect a JSON formatted String for the JSON data typetrueNo

options and serverSettings should be passed to the client as Map<String, String>. An empty map for either will use client or server defaults, respectively.

Note

All available Java client options are listed in ClientConfigProperties.java and this documentation page.

All available server session settings are listed in this documentation page.

For example:

Map<String, String> javaClientOptions = Map.of(
    ClientConfigProperties.CA_CERTIFICATE.getKey(), "<my_CA_cert>",
    ClientConfigProperties.SSL_CERTIFICATE.getKey(), "<my_SSL_cert>",
    ClientConfigProperties.CLIENT_NETWORK_BUFFER_SIZE.getKey(), "30000",
    ClientConfigProperties.HTTP_MAX_OPEN_CONNECTIONS.getKey(), "5"
);

Map<String, String> serverSettings = Map.of(
    "insert_deduplicate", "1"
);

ClickHouseClientConfig clickHouseClientConfig = new ClickHouseClientConfig(
    url,
    username,
    password,
    database,
    tableName,
    javaClientOptions,
    serverSettings,
    false // enableJsonSupportAsString
);

Sink options​

The following options come directly from Flink's AsyncSinkBase:

ParametersDescriptionDefault ValueRequired
maxBatchSizeMaximum number of records inserted in a single batchN/AYes
maxInFlightRequestsThe maximum number of in flight requests allowed before the sink applies backpressureN/AYes
maxBufferedRequestsThe maximum number of records that may be buffered in the sink before backpressure is appliedN/AYes
maxBatchSizeInBytesThe maximum size (in bytes) a batch may become. All batches sent will be smaller than or equal to this sizeN/AYes
maxTimeInBufferMSThe maximum time a record may stay in the sink before being flushedN/AYes
maxRecordSizeInBytesThe maximum record size that the sink will accept, records larger than this will be automatically rejectedN/AYes

Supported data types​

The table below provides a quick reference for converting data types when inserting from Flink into ClickHouse.

Java TypeClickHouse TypeSupportedSerialization Method
byte/ByteInt8✅DataWriter.writeInt8
short/ShortInt16✅DataWriter.writeInt16
int/IntegerInt32✅DataWriter.writeInt32
long/LongInt64✅DataWriter.writeInt64
BigIntegerInt128✅DataWriter.writeInt128
BigIntegerInt256✅DataWriter.writeInt256
short/ShortUInt8✅DataWriter.writeUInt8
int/IntegerUInt8✅DataWriter.writeUInt8
int/IntegerUInt16✅DataWriter.writeUInt16
long/LongUInt32✅DataWriter.writeUInt32
long/LongUInt64✅DataWriter.writeUInt64
BigIntegerUInt64✅DataWriter.writeUInt64
BigIntegerUInt128✅DataWriter.writeUInt128
BigIntegerUInt256✅DataWriter.writeUInt256
BigDecimalDecimal✅DataWriter.writeDecimal
BigDecimalDecimal32✅DataWriter.writeDecimal
BigDecimalDecimal64✅DataWriter.writeDecimal
BigDecimalDecimal128✅DataWriter.writeDecimal
BigDecimalDecimal256✅DataWriter.writeDecimal
float/FloatFloat✅DataWriter.writeFloat32
double/DoubleDouble✅DataWriter.writeFloat64
boolean/BooleanBoolean✅DataWriter.writeBoolean
StringString✅DataWriter.writeString
StringFixedString✅DataWriter.writeFixedString
LocalDateDate✅DataWriter.writeDate
LocalDateDate32✅DataWriter.writeDate32
LocalDateTimeDateTime✅DataWriter.writeDateTime
ZonedDateTimeDateTime✅DataWriter.writeDateTime
LocalDateTimeDateTime64✅DataWriter.writeDateTime64
ZonedDateTimeDateTime64✅DataWriter.writeDateTime64
int/IntegerTime❌N/A
long/LongTime64❌N/A
byte/ByteEnum8✅DataWriter.writeInt8
int/IntegerEnum16✅DataWriter.writeInt16
java.util.UUIDUUID✅DataWriter.writeIntUUID
StringJSON✅DataWriter.writeJSON
Array<Type>Array<Type>✅DataWriter.writeArray
Map<K,V>Map<K,V>✅DataWriter.writeMap
Tuple<Type,..>Tuple<T1,T2,..>✅DataWriter.writeTuple
ObjectVariant❌N/A

Notes:

  • A ZoneId must be provided when performing date operations.
  • Precision and scale must be provided when performing decimal operations.
  • In order for ClickHouse to parse a Java String as JSON, you need to enable enableJsonSupportAsString in ClickHouseClientConfig.
  • The connector requires an ElementConvertor to map elements in the input DataStream to ClickHouse payloads. To this end, the connector provides ClickHouseConvertor and POJOConvertor, which you can use to implement this mapping using the DataWriter serialization methods above.

Supported input formats​

You can find the list of available ClickHouse input formats in this documentation page and ClickHouseFormat.java.

To specify the format the connector should use to serialize your DataStream to ClickHouse payloads, use the setClickHouseFormat function. For example:

ClickHouseAsyncSink<String> csvSink = new ClickHouseAsyncSink<>(
        convertorString,
        MAX_BATCH_SIZE,
        MAX_IN_FLIGHT_REQUESTS,
        MAX_BUFFERED_REQUESTS,
        MAX_BATCH_SIZE_IN_BYTES,
        MAX_TIME_IN_BUFFER_MS,
        MAX_RECORD_SIZE_IN_BYTES,
        clickHouseClientConfig
);
csvSink.setClickHouseFormat(ClickHouseFormat.CSV);
Note

By default, the connector will use either RowBinaryWithDefaults or RowBinary if setSupportDefault in ClickHouseClientConfig is explicitly set to true or false, respectively.

Metrics​

The connector exposes the following additional metrics on top of Flink's existing metrics:

MetricDescriptionTypeStatus
numBytesSendTotal number of bytes sent to ClickHouse in the request payload. Note: this metric measures the serialized data size sent over the network and might differ from ClickHouse's written_bytes in system.query_log, which reflects the actual bytes written to storage after processingCounter✅
numRecordSendTotal number of records sent to ClickHouseCounter✅
numRequestSubmittedTotal number of requests sent (actual number of flushes performed)Counter✅
numOfDroppedBatchesTotal number of batches dropped due to non-retryable failuresCounter✅
numOfDroppedRecordsTotal number of records dropped due to non-retryable failuresCounter✅
totalBatchRetriesTotal number of batch retries due to retryable failuresCounter✅
writeLatencyHistogramHistogram of successful write latency distribution (ms)Histogram✅
writeFailureLatencyHistogramHistogram of failed write latency distribution (ms)Histogram✅
triggeredByMaxBatchSizeCounterTotal number of flushes triggered by reaching maxBatchSizeCounter✅
triggeredByMaxBatchSizeInBytesCounterTotal number of flushes triggered by reaching maxBatchSizeInBytesCounter✅
triggeredByMaxTimeInBufferMSCounterTotal number of flushes triggered by reaching maxTimeInBufferMSCounter✅
actualRecordsPerBatchHistogram of actual batch size distributionHistogram✅
actualBytesPerBatchHistogram of actual bytes per batch distributionHistogram✅

Limitations​

  • The sink currently provides an at-least-once delivery guarantee. Work toward exactly-once semantics is being tracked here.
  • The sink does not yet support a dead-letter queue (DLQ) for buffering unprocessable records. In the meantime, the connector will attempt to re-insert records that fail and drop them if unsuccessful. This feature is being tracked here.
  • The sink does not yet support creation via Flink's Table API or Flink SQL. This feature is being tracked here.

ClickHouse version compatibility and security​

  • The connector is tested against a range of recent ClickHouse versions, including latest and head, via a daily CI workflow. The tested versions are updated periodically as new ClickHouse releases become active. See here for the versions the connector is tested against daily.
  • See the ClickHouse security policy for known security vulnerabilities and how to report a vulnerability.
  • We recommend upgrading the connector continuously to not miss security fixes and new improvements.
  • If you have an issue with migration, please create a GitHub issue and we will respond!
  • For optimal performance, ensure your DataStream element type is not a Generic type - see here for Flink's type distinction. Non-generic elements will avoid the serialization overhead incurred by Kryo and improve throughput to ClickHouse.
  • We recommend setting maxBatchSize to at least 1000 and ideally between 10,000 to 100,000. See this guide on bulk inserts for more information.
  • To do OLTP-style deduplication or upsert to ClickHouse, refer to this documentation page. Note: this is not to be confused with batch deduplication that happens on retries.

Troubleshooting​

CANNOT_READ_ALL_DATA​

The following error may occur:

com.clickhouse.client.api.ServerException: Code: 33. DB::Exception: Cannot read all data. Bytes read: 9205. Bytes expected: 1100022.: (at row 9) : While executing BinaryRowInputFormat. (CANNOT_READ_ALL_DATA)

Cause: Most commonly, the CANNOT_READ_ALL_DATA error means that your ClickHouse table schema has diverged from your Flink record schema. This can happen when one or the other is changed in a backwards incompatible way.

Solution: Update the schema in either (or both) your ClickHouse table or connector input data type so they are compatible. If needed, refer to the type mapping for how to resolve Java types to ClickHouse types. Note: if there are still records in flight, you will need to reset Flink state when you restart the connector.

Low throughput​

You may experience that the connector's throughput does not scale with the job's parallelism (Flink task count) when writing to ClickHouse.

Cause: ClickHouse's background part merging process may be slowing down inserts. This can happen when the configured batch size is too small, the connector is flushing too frequently, or a combination of the two.

Solution: Monitor the numRequestSubmitted and actualRecordsPerBatch metrics to help determine how to tune your batch size (maxBatchSize) and how frequently to flush. Also, see Advanced and recommended usage for batch sizing recommendations.

I am missing rows in my ClickHouse table​

Cause: The batch(es) were dropped either because of a non-retryable failure or they could not be inserted in the configured number of retries (settable via ClickHouseClientConfig.setNumberOfRetries()). Note: by default, the connector will attempt to re-insert a batch up to 3 times before dropping it.

Solution: Inspect the TaskManager logs and/or stack trace(s) for the root cause.

Contributing and support​

If you'd like to contribute to the project or report any issues, we welcome your input! Visit our GitHub repository to open an issue, suggest improvements, or submit a pull request.

Contributions are welcome! Please check the contribution guide in the repository before starting. Thank you for helping improve the ClickHouse Flink connector!