Visitar URL original
GitHub - ClickHouse/flink-connector-clickhouse: Official Apache Flink connector for ClickHouse · GitHub
Skip to content

About

Official Apache Flink connector for ClickHouse

Topics

Resources

Contributing

Stars

29 stars

Watchers

5 watching

Forks

Latest commit

 

History

388 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ClickHouse Flink Connector

Table of Contents

About The Project

This is a repo of ClickHouse official Apache Flink Connector supported by the ClickHouse team. The connector supports two main Apache Flink APIs:

  • DataStream API
  • Table API / Flink SQL (inserts only, no updates or deletes)

Supported Flink Versions

Version Dependency ClickHouse Client Version Required Java
latest flink-connector-clickhouse-2.0.0 0.9.5 Java 17+
2.0.1 flink-connector-clickhouse-2.0.0 0.9.5 Java 17+
2.0.0 flink-connector-clickhouse-2.0.0 0.9.5 Java 17+
1.20.2 flink-connector-clickhouse-1.17 0.9.5 Java 11+
1.19.3 flink-connector-clickhouse-1.17 0.9.5 Java 11+
1.18.1 flink-connector-clickhouse-1.17 0.9.5 Java 11+
1.17.2 flink-connector-clickhouse-1.17 0.9.5 Java 11+

Installation

For Flink 2.0.0+

Maven

<dependency>
    <groupId>com.clickhouse.flink</groupId>
    <artifactId>flink-connector-clickhouse-2.0.0</artifactId>
    <version>0.2.0</version>
    <classifier>all</classifier>
</dependency>

For Flink 1.17+

Maven

<dependency>
    <groupId>com.clickhouse.flink</groupId>
    <artifactId>flink-connector-clickhouse-1.17</artifactId>
    <version>0.2.0</version>
    <classifier>all</classifier>
</dependency>

DataStream API

Snippet

Configure ClickHouseClient

ClickHouseClientConfig clickHouseClientConfig = new ClickHouseClientConfig(url, username, password, database, tableName);

If you are planning to insert RAW CSV data as is

Create a ClickHouseConvertor

ClickHouseConvertor<String> convertorString = new ClickHouseConvertor<>(String.class);

Build the sink (optional knobs have sensible defaults — set only what you need):

ClickHouseAsyncSink<String> csvSink = ClickHouseAsyncSink.<String>builder()
        .setElementConverter(convertorString)
        .setClickHouseClientConfig(clickHouseClientConfig)
        .setClickHouseFormat(ClickHouseFormat.CSV)
        .setMaxBatchSize(MAX_BATCH_SIZE)
        .setMaxInFlightRequests(MAX_IN_FLIGHT_REQUESTS)
        .setMaxBufferedRequests(MAX_BUFFERED_REQUESTS)
        .setMaxBatchSizeInBytes(MAX_BATCH_SIZE_IN_BYTES)
        .setMaxTimeInBufferMS(MAX_TIME_IN_BUFFER_MS)
        .setMaxRecordSizeInBytes(MAX_RECORD_SIZE_IN_BYTES)
        .build();

Finally, connect your DataStream to the sink.

data.sinkTo(csvSink);

More examples and snippets can be found in our tests flink-connector-clickhouse-1.17 and flink-connector-clickhouse-2.0.0

Example

We have created maven based example for easy start with ClickHouse Sink Different versions for Flink

Java (Maven)

Scala (sbt)

For more detailed instructions, see the Example Guide

Table API

The connector registers itself as the clickhouse SQL connector: create a table with 'connector' = 'clickhouse' and INSERT INTO it from Flink SQL or the Table API (TableDescriptor.forConnector("clickhouse")). The sink is insert-only: append queries work, update-producing queries (e.g. a plain GROUP BY aggregation) are rejected by the planner — use ReplacingMergeTree/AggregatingMergeTree plus FINAL/argMax for upsert-style needs.

At planning time the connector reads the real column types from the target ClickHouse table and validates the Flink schema against them, so typos, type mismatches, narrowing and unsupported types fail at job submission with a precise message instead of at the first flush. Columns are matched by name, case-sensitively; ClickHouse columns you leave out of the Flink schema get their server-side DEFAULT, or the type's default value (0/''/empty) if they have none — the latter is logged as a warning at planning. A nullable Flink column can only target a Nullable(...) ClickHouse column — declare columns NOT NULL when the target column isn't Nullable (in Flink SQL, columns and collection elements are nullable unless declared otherwise).

An INSERT INTO t (a, b) column list is honoured: the columns the statement leaves out stay out of the request, so the server applies their DEFAULT instead of receiving the planner's padding NULL. This needs Flink 1.18 or newer — Flink 1.17 does not report the column list to the sink, so a partial insert there still writes an explicit NULL into every omitted column.

MATERIALIZED, ALIAS and EPHEMERAL columns take no value from an insert. You may still declare them in the Flink schema as long as every statement omits them through a column list — declare them nullable, since Flink refuses to leave a NOT NULL column out of one. A statement that would write such a column fails at planning and names it.

Snippet

CREATE TABLE ch_events (
    event_id   BIGINT NOT NULL,
    amount     DECIMAL(18, 4) NOT NULL,
    created_at TIMESTAMP(3) NOT NULL
) WITH (
    'connector' = 'clickhouse',
    'url'       = 'http://localhost:8123',
    'username'  = 'default',
    'password'  = '',
    'database'  = 'analytics',
    'table'     = 'events'
    -- + optional batching/retry options, see below
);

INSERT INTO ch_events SELECT event_id, amount, created_at FROM kafka_src;

Connector options

Connection options (required unless noted): url, username, password (defaults to ''), database, table.

Batching / backpressure

Option Default DataStream equivalent
sink.buffer-flush.max-rows 500 builder.setMaxBatchSize()
sink.buffer-flush.max-bytes 5mb builder.setMaxBatchSizeInBytes()
sink.buffer-flush.interval 5s builder.setMaxTimeInBufferMS()
sink.max-in-flight-requests 50 builder.setMaxInFlightRequests()
sink.max-buffered-requests 10000 builder.setMaxBufferedRequests()
sink.record.max-bytes 1mb builder.setMaxRecordSizeInBytes()
sink.parallelism same as its input (usually the job's default parallelism) sinkTo(sink).setParallelism(n)

Reliability

Option Default DataStream equivalent
sink.max-retries -1 (retry forever) config.setRetryPolicy()
sink.batch-failure-strategy stop-flink (drop-batch) config.setBatchFailureStrategy()

Type / compatibility

Option Default Effect
sink.timezone UTC zone in which TIMESTAMP (no time zone) wall-clock values are interpreted; DST gap wall clocks shift forward, ambiguous fall-back wall clocks take the earlier offset
sink.ignore-unknown-flink-columns false true drops Flink columns absent from the ClickHouse table instead of failing
sink.strict-numeric-mapping false true rejects at planning every numeric type pair whose values may not all fit the column (a narrower or unsigned integer column, a DECIMAL(p,0) at an integer's digit boundary or into an unsigned integer), instead of range-checking each value at write time; lossless widening stays allowed

Passthrough: clickhouse.client.<key> options are forwarded to the ClickHouse client, clickhouse.server.<key> become per-query server settings. The connector sends print_pretty_type_names = 0 with its schema introspection only, so the type names it reads are canonical (named Tuple columns are otherwise pretty-printed across lines and rejected in the insert header) — this makes ClickHouse 23.10 the SQL sink's minimum server version, as older servers reject the unknown setting — and pins input_format_null_as_default, input_format_defaults_for_omitted_fields and (for JSON columns) input_format_binary_read_json_as_string on every insert; a user copy of any of these settings, in either spelling, is rejected. The client option async is pinned per insert the same way, so it is rejected too.

Flink STRING into a ClickHouse JSON column works out of the box — the connector enables the client's JSON-as-string mode automatically exactly when a JSON column is mapped.

Type mapping

Lossless widening is implicit; a pair that would round a value (DOUBLE into Float32, a narrower Decimal scale or timestamp precision) is rejected at planning. A pair whose values may not all fit the column (the "Notes" below) is accepted and every value is checked at write time, failing the job naming the column, value and range; with 'sink.strict-numeric-mapping' = 'true' the numeric pairs among them are rejected at planning instead, since a wider column can always take their place. Any other pair fails at planning naming the column and both types.

Flink SQL type ClickHouse column types Notes
BOOLEAN Bool
TINYINT / SMALLINT / INT / BIGINT any Int8..Int256 or UInt8..UInt256 a narrower or unsigned column is range-checked per record
DECIMAL(p, s) a Decimal(p', s') it fits; with s = 0 also any Int8..Int256 / UInt8..UInt256 whose digits cover p boundary precisions (DECIMAL(19, 0) → Int64, DECIMAL(20, 0) → UInt64) and unsigned targets are range-checked per record
FLOAT Float32, Float64
DOUBLE Float64 Float32 would round — CAST to FLOAT to round explicitly
CHAR / VARCHAR / STRING String, FixedString(n), UUID, JSON FixedString length checked in UTF-8 bytes per record, whatever the declared Flink length (Flink does not enforce it by default); UUID text checked per record
DATE Date, Date32 range-checked per record: Date 1970-01-01..2149-06-06, Date32 1900-01-01..2299-12-31
TIMESTAMP(p) / TIMESTAMP_LTZ(p) DateTime (p = 0), DateTime64(s >= p) range-checked per record: DateTime 1970-01-01..2106-02-07, DateTime64 1900-01-01..2299-12-31 (2262-04-11 at scale 9). Flink's default TIMESTAMP is precision 6 — declare TIMESTAMP(3) for DateTime64(3). TIMESTAMP is a wall clock in sink.timezone; TIMESTAMP_LTZ an instant
ARRAY<t> Array(T) only Array(Nullable(T)) can carry nested NULLs
MAP<k, v> Map(K, V) string/integer/decimal keys except UInt64; values not Nullable
MULTISET<t> Map(T, I) for any integer I counts become the values; the value type must not be Nullable. A count is a non-negative INT, so Int32/UInt32 and wider always fit; Int8, Int16, UInt8, UInt16 are range-checked per record and rejected under sink.strict-numeric-mapping
ROW<...> Tuple(...) positional — a ROW whose field names are a named Tuple's element names in another order is rejected at planning; fields/elements not nullable

Unsupported: BINARY/VARBINARY, TIME, TIMESTAMP WITH TIME ZONE, INTERVAL; ClickHouse Enum (#43), Variant (#60), Time (#91), IPv4/6, Dynamic, geo — exclude such columns and let server defaults fill them.

Everywhere: nullable Flink columns need Nullable(...) targets — except Nullable(UInt8/16/32/64), blocked until issue #144 — and composites must be NOT NULL. LowCardinality is transparent; SimpleAggregateFunction(f, T) matches as T, top-level only.

Notes for SQL users:

  • Operator names are planner-generated (e.g. Sink: ch_events[3]), which affects metric identifiers; numRecordsSend counts at flush, so retried batches double-count versus SELECT count().
  • A compiled plan does not freeze the ClickHouse mapping: an ALTER TABLE between planning and execution fails at the first flush (same exposure as the DataStream path).
  • The community itinycheng connector also registers the clickhouse identifier; having both jars on the classpath fails with Flink's "multiple factories" error — keep only one.
  • SQL gateways without ClickHouse network access can deploy in application mode, where planning runs on the JobManager inside the data plane.

Example

See the round-trip integration test ClickHouseTableApiIntegrationTests for a complete DDL + INSERT INTO example against a real ClickHouse.

Supported ClickHouse Types

This is the DataStream (Java value) view; for the Flink SQL / Table API pairing rules see Type mapping.

Java Type ClickHouse Type Supported Serialize Method
byte/Byte Int8 ✅ DataWriter.writeInt8
short/Short Int16 ✅ DataWriter.writeInt16
int/Integer Int32 ✅ DataWriter.writeInt32
long/Long Int64 ✅ DataWriter.writeInt64
BigInteger Int128 ✅ DataWriter.writeInt124
BigInteger Int256 ✅ DataWriter.writeInt256
short/Short UInt8 ✅ DataWriter.writeUInt8
int/Integer UInt8 ✅ DataWriter.writeUInt8
int/Integer UInt16 ✅ DataWriter.writeUInt16
long/Long UInt32 ✅ DataWriter.writeUInt32
long/Long UInt64 ✅ DataWriter.writeUInt64
BigInteger UInt64 ✅ DataWriter.writeUInt64
BigInteger UInt128 ✅ DataWriter.writeUInt128
BigInteger UInt256 ✅ DataWriter.writeUInt256
BigDecimal Decimal ✅ DataWriter.writeDecimal
BigDecimal Decimal32 ✅ DataWriter.writeDecimal
BigDecimal Decimal64 ✅ DataWriter.writeDecimal
BigDecimal Decimal128 ✅ DataWriter.writeDecimal
BigDecimal Decimal256 ✅ DataWriter.writeDecimal
float/Float Float ✅ DataWriter.writeFloat32
double/Double Double ✅ DataWriter.writeFloat64
boolean/Boolean Boolean ✅ DataWriter.writeBoolean
String String ✅ DataWriter.writeString
String FixedString ✅ DataWriter.writeFixedString
LocalDate Date ✅ DataWriter.writeDate
LocalDate Date32 ✅ DataWriter.writeDate32
LocalDateTime DateTime ✅ DataWriter.writeDateTime
ZonedDateTime DateTime ✅ DataWriter.writeDateTime
LocalDateTime DateTime64 ✅ DataWriter.writeDateTime64
ZonedDateTime DateTime64 ✅ DataWriter.writeDateTime64
int/Integer Time ❌ N/A
long/Long Time64 ❌ N/A
byte/Byte Enum8 ✅ DataWriter.writeInt8
int/Integer Enum16 ✅ DataWriter.writeInt16
java.util.UUID UUID ✅ DataWriter.writeIntUUID
String JSON ✅ DataWriter.writeJSON
Array Array ✅ DataWriter.writeArray
Map<K,V> Map<K,V> ✅ DataWriter.writeMap
Tuple<Type,..> Tuple<T1,T2,..> ✅ DataWriter.writeTuple
Object Variant ❌ N/A
(inner type T) SimpleAggregateFunction(f, T) ✅ writer for inner type T
  • A ZoneId must also be provided when performing date operations.
  • Precision and scale must also be provided when performing decimal operations.
  • To use JSON type as a string, you need to enable enableJsonSupportAsString in ClickHouseClientConfig .
  • SimpleAggregateFunction(f, T) is wire-encoded exactly as its inner type T, so bind the Java value for T and ignore the aggregate function f. Any T supported above works, including Nullable, LowCardinality, Decimal, Array, Map and Tuple.

Configuration Options

Client configuration

Parameters Description Default Value
url fully qualified URL N/A
username ClickHouse database username N/A
password ClickHouse database password N/A
database ClickHouse database name N/A
table ClickHouse table name N/A

Sink configuration

Our Sink is built on top of Flink’s AsyncSinkBase

Parameters Description Default Value
maxBatchSize Maximum number of records inserted in a single batch N/A
maxInFlightRequests The maximum number of in flight requests allowed before the sink applies backpressure N/A
maxBufferedRequests The maximum number of records that may be buffered in the sink before backpressure is applied N/A
maxBatchSizeInBytes The maximum size (in bytes) a batch may become. All batches sent will be smaller than or equal to this size N/A
maxTimeInBufferMS The maximum time a record may stay in the sink before being flushed N/A
maxRecordSizeInBytes The maximum record size that the sink will accept, records larger than this will be automatically rejected N/A

Sink Metrics

Our Sink exposes additional metrics on top of Flink's existing metrics:

Metric Description Type Status
numBytesSend Total number of bytes sent to ClickHouse Counter ✅
numRecordSend Total number of records sent to ClickHouse Counter ✅
numRequestSubmitted Total number of requests sent (actual number of flushes performed) Counter ✅
numOfDroppedBatches Total number of batches dropped due to non-retryable failures Counter ✅
numOfDroppedRecords Total number of records dropped due to non-retryable failures Counter ✅
totalBatchRetries Total number of batch retries due to retryable failures Counter ✅
writeLatencyHistogram Histogram of write latency distribution Histogram ✅
writeFailureLatencyHistogram Histogram of write failure latency distribution Histogram ✅
triggeredByMaxBatchSizeCounter Sink flushes triggered by reaching maxBatchSize Counter ✅
triggeredByMaxBatchSizeInBytesCounter Sink flushes triggered by reaching maxBatchSizeInBytes Counter ✅
triggeredByMaxTimeInBufferMSCounter Sink flushes triggered by reaching maxTimeInBufferMS Counter ✅
actualRecordsPerBatchHistogram Histogram of actual batch size distribution Histogram ✅
actualBytesPerBatchHistogram Histogram of actual bytes per batch distribution Histogram ✅
actualTimeInBufferHistogram Histogram of actual time in buffer before flush distribution Histogram ❌

Limitations

  • Currently the sink does not support exactly-once semantics

Compatibility

  • All projects in this repo are tested with all active LTS versions of ClickHouse.
  • The Table API / SQL sink requires ClickHouse 23.10 or newer (see "Passthrough" under Connector options); the DataStream API has no such floor.
  • Support policy
  • We recommend upgrading the connector continuously to not miss security fixes and new improvements
    • If you have an issue with migration - create and issue and we will respond!

Contributing

Please see our contributing guide.

About

Official Apache Flink connector for ClickHouse

Topics

Resources

Contributing

Stars

29 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages