/

Data Platforms

MySQL to ClickHouse: The Complete Replication Guide

Intergalactic Data Labs

โ€”

โ€”

12 min read

Table of contents

Summarize

As of October 2026 there are four workable ways to replicate MySQL to ClickHouse. ClickHouse can read MySQL through its table engine, ClickHouse Cloud can stream the binlog with ClickPipes, or Debezium can relay changes through Kafka. A replication tool such as Filament, PeerDB, Airbyte, or Estuary can also copy tables on a schedule. What decides between them is deletes, where ClickHouse runs, and the cost unit.

Approach

Deletes land

Works on

Cost unit

MySQL table engine

No copy at all

Cloud and self-hosted

ClickHouse compute

ClickPipes MySQL CDC

Yes, from binlog

ClickHouse Cloud only

Per GB plus compute hours

Debezium plus Kafka

Partly, sink skips deletes

Cloud and self-hosted

Your Kafka stack

Batch or incremental tool

Only on full reload

Cloud and self-hosted

Rows, GB, credits, or free

What decides a MySQL to ClickHouse replication setup?

Five constraints settle this job before any tool comes into it.

Deletes. MySQL analysts delete rows all the time, and ClickHouse has no cheap row-level delete. A lightweight DELETE "is implemented as a mutation that marks rows as deleted but does not immediately physically delete them," per the ClickHouse docs. Only binlog readers see a delete at all, and even they miss cascading deletes, which MySQL does not write to the binlog, as the ClickPipes FAQ notes.

Duplicates. Every upsert path lands on ReplacingMergeTree, and the engine docs are blunt that "Data deduplication occurs only during a merge. Merging occurs in the background at an unknown time, so you can't plan for it." Dashboards either query with FINAL or tolerate duplicates.

Binlog hygiene. Any log-based path needs binlog_format set to ROW, which the MySQL 8.4 manual confirms is the default, plus binlog_row_image set to FULL and retention that outlasts the initial snapshot. MySQL keeps logs for 30 days by default, while Amazon RDS defaults to zero hours, per the Airbyte MySQL docs.

Where ClickHouse runs. ClickPipes and the Fivetran destination work only against ClickHouse Cloud. Self-hosted clusters need a tool that speaks the native protocol.

Cost shape. The managed paths bill per gigabyte or per row. The self-hosted tools cost nothing but compute.

How can you replicate MySQL to ClickHouse?

Every tool that runs this exact route today sits in the table below. Hevo, Stitch, and OLake read MySQL but have no ClickHouse destination, so they are out. MaterializedMySQL, the old in-engine replicator, "has been removed and is no longer available" since ClickHouse 25.1, per the changelog.

Tool

MySQL modes

ClickHouse target

License

Pricing unit

MySQL table engine

Live read-through

Any

ClickHouse core

Compute

ClickPipes

Bulk and binlog CDC

Cloud only

Managed

$0.20 per GB on CDC

PeerDB

Binlog CDC and query

Self-hosted and Cloud

AGPLv3 file

Self-hosted

Debezium plus Kafka sink

Binlog CDC

Any 23.3 or newer

Apache 2.0

Self-hosted

Airbyte

Full, incremental, CDC

Any 21.8 or newer

ELv2

Credits or capacity

Fivetran

Binlog CDC

Cloud only

Proprietary

Monthly active rows

Estuary Flow

Binlog CDC

Self-hosted and Cloud

BSL

$0.50 per GB

dlt

Query based

Any

Apache 2.0

Free, dltHub paid

Sling

Full, incremental, paid CDC

Any

GPLv3

Pro token for CDC

ingestr

Full, incremental, CDC

Any

FSL-1.1

Free

Filament

Full and incremental

Self-hosted and Cloud

Apache 2.0

Free, alpha sink

The MySQL table engine is the zero-infrastructure answer. It "allows you to perform SELECT and INSERT queries on data that is stored on a remote MySQL server," per the engine docs. The mysql() function page shows an INSERT SELECT with a max(id) predicate as a crude incremental copy. Only simple comparisons push down, nothing tracks updates or deletes, and every query scans the source.

ClickPipes is the managed answer and the one ClickHouse now recommends. The MySQL connector reached GA and started billing on September 1, 2026, per gigabyte moved plus compute per hour, per the billing page. It reads RDS, Aurora, Cloud SQL, self-hosted MySQL, and MariaDB, and it propagates added columns but not dropped ones, per the schema changes page. It requires ClickHouse Cloud.

PeerDB is the open-source engine under ClickPipes, which ClickHouse acquired in July 2024, per the acquisition post. Its connector matrix lists MySQL to ClickHouse with CDC and watermark replication. The LICENSE file is AGPLv3 even though the README badge says ELv2, and it ships as a Docker Compose stack with MinIO staging.

Debezium plus the ClickHouse Kafka Connect sink is the self-hosted streaming answer. The Debezium MySQL connector, on its 3.7 release since late September 2026, reads the binlog and emits row-level change events to Kafka. The ClickHouse sink ships with exactly-once semantics but states plainly that "Deletes aren't supported," so you map Debezium envelopes into ReplacingMergeTree yourself.

Airbyte runs the route with a certified MySQL source and a certified ClickHouse destination, per its metadata file. The destination uses ReplacingMergeTree for dedup, per the destination docs. Two documented limits matter. A column removed in MySQL is dropped with its history in ClickHouse, and on self-managed multi-replica clusters data lands only on the node that received the insert.

Fivetran reads the MySQL binlog from MySQL 5.5 onward, per its MySQL docs, and its ClickHouse Cloud destination went GA in May 2026, per the destination changelog. It writes SharedReplacingMergeTree tables versioned by _fivetran_synced and bills by monthly active rows. It does not support self-hosted ClickHouse or primary key changes.

Estuary Flow captures the binlog and "writes to ClickHouse natively, supports hard and soft deletes, and works with both self-hosted and ClickHouse Cloud deployments," per its materialization docs. Data movement costs $0.50 per GB plus a monthly connector fee, per the pricing page, and the platform is BSL licensed.

dlt reaches MySQL through its SQLAlchemy based sql_database source and supports every write disposition on ClickHouse, including an atomic EXCHANGE TABLES replace, per the destination docs. There is no MySQL binlog CDC, and JSON, time, and binary columns all land as text.

Sling runs full and incremental copies on the free CLI and puts MySQL binlog CDC behind a Pro Max token, per its change capture docs. ingestr does the same job in one Go binary with a mysql+cdc:// URI scheme, per its MySQL docs, under an FSL-1.1 license.

Where does Filament fit on this route?

Filament runs MySQL to ClickHouse as a scheduled full or incremental copy, and not as a change stream. Its ClickHouse sink is alpha on the connector roadmap, and the page is direct about the limit. "CDC and incremental delete are not supported because ClickHouse does not offer efficient row-level deletes for this sink." Galaxy builds Filament, so weigh what follows accordingly.

Measured against the five criteria above, it wins on four and concedes one.

Criterion

Filament on this route

Evidence

Deletes

Full reload only

Sink page, no CDC or incremental delete

Duplicates

Replace mode sidesteps them

Atomic EXCHANGE TABLES at commit

Binlog hygiene

Not needed for full or incremental

Query-based reads with keyset paging

Where ClickHouse runs

Self-hosted and Cloud, native protocol

TLS on by default, LZ4 compression

Cost shape

Apache 2.0, no pricing page

Runs as a binary, Go library, or Helm chart

Deletes are the concession. If rows must vanish from ClickHouse within seconds of leaving MySQL, use ClickPipes, Estuary, or PeerDB. Filament's incremental mode pages by an updated_at cursor, and the replication modes guide says such a route "cannot see a hard delete."

Replace mode is the strength. The sink loads each table into a staging table and swaps it in with an atomic EXCHANGE TABLES at commit. "Failed replace runs leave the destination exactly as it was," per the sink page. A nightly rebuild of a 50 million row orders table never shows readers a half-loaded table and never leaves duplicate keys behind.

The read side is built for big InnoDB tables. The MySQL source splits a table into as many as 64 primary key ranges and saves the last key so an interrupted read resumes. Each incremental run is bounded at a high watermark inside a consistent snapshot. Before every write the pipeline computes a CRC32-C checksum over the batch and fails the run on a mismatch, per the integrity guide.

There is no public benchmark for this route. The benchmark overview lists six measured routes and none end in ClickHouse, so this guide makes no speed claim.

How to replicate MySQL to ClickHouse with Filament

The walkthrough below uses the local context, which "needs no external services" and runs in process with SQLite, per the CLI guide.

Install the CLI. One command installs the binary, per the installation page.

curl -fsSL https://getgalaxy.io/filament/install | sh

curl -fsSL https://getgalaxy.io/filament/install | sh

curl -fsSL https://getgalaxy.io/filament/install | sh

Prepare MySQL. Filament needs MySQL 8.0 or newer, and full and incremental reads need only SELECT. For incremental reads add an index that starts with the cursor column to avoid a filesort.

CREATE INDEX orders_incremental_idx ON orders (updated_at, id)
CREATE INDEX orders_incremental_idx ON orders (updated_at, id)
CREATE INDEX orders_incremental_idx ON orders (updated_at, id)

Prepare ClickHouse. The sink authenticates against the default database and creates the destination database from there, so grant the user access to both.

Declare the source, sink, and pipeline. The docs show no MySQL to ClickHouse example, so this file is assembled from the documented keys on the source and sink pages.

version: 1
sources:
  shop_mysql:
    type: mysql
    config:
      connection_method: url
      dsn: env:MYSQL_DSN
      database: shop
      shard_pages: 16384
sinks:
  analytics_ch:
    type: clickhouse
    config:
      connection_method: fields
      host: abc123.us-east-2.aws.clickhouse.cloud
      port: 9440
      protocol: native
      username: filament
      password: env:CLICKHOUSE_PASSWORD
      secure: true
      database: shop_raw
pipelines:
  shop-nightly:
    source:
      ref: shop_mysql
    sink:
      ref: analytics_ch
    resources:
      - orders
      - order_items
      - customers
    sync_mode: full
    write_mode

version: 1
sources:
  shop_mysql:
    type: mysql
    config:
      connection_method: url
      dsn: env:MYSQL_DSN
      database: shop
      shard_pages: 16384
sinks:
  analytics_ch:
    type: clickhouse
    config:
      connection_method: fields
      host: abc123.us-east-2.aws.clickhouse.cloud
      port: 9440
      protocol: native
      username: filament
      password: env:CLICKHOUSE_PASSWORD
      secure: true
      database: shop_raw
pipelines:
  shop-nightly:
    source:
      ref: shop_mysql
    sink:
      ref: analytics_ch
    resources:
      - orders
      - order_items
      - customers
    sync_mode: full
    write_mode

version: 1
sources:
  shop_mysql:
    type: mysql
    config:
      connection_method: url
      dsn: env:MYSQL_DSN
      database: shop
      shard_pages: 16384
sinks:
  analytics_ch:
    type: clickhouse
    config:
      connection_method: fields
      host: abc123.us-east-2.aws.clickhouse.cloud
      port: 9440
      protocol: native
      username: filament
      password: env:CLICKHOUSE_PASSWORD
      secure: true
      database: shop_raw
pipelines:
  shop-nightly:
    source:
      ref: shop_mysql
    sink:
      ref: analytics_ch
    resources:
      - orders
      - order_items
      - customers
    sync_mode: full
    write_mode

Validate and run. The CLI "applies it when it changes and creates or updates the declared connections and pipelines," per the CLI guide.




Switch to incremental once the first load is in. Change sync_mode to incremental and write_mode to upsert. The first incremental run captures a watermark and backfills by primary key, and later runs page by the compound watermark. The sink then creates ReplacingMergeTree tables, so read them with FINAL. Add a per-resource lookback if late commits are common, since upsert re-reads the overlap safely.

Schedule it. A pipeline carries one schedule with a cron expression, an IANA timezone, and an overlap policy of SKIP or ALLOW, per the pipelines API. Set it in the web UI or SDK once the pipeline runs on the Helm chart, which the Kubernetes guide covers, and run exactly one control-plane replica.

Which option fits which job?

Pick by the constraint that breaks first.

Job

Pick

Why

Nightly rebuilt analytics tables, any ClickHouse

Filament

Atomic replace, parallel reads, no metered bill

Hourly fresh tables where deletes can wait for a reload

Filament or dlt

Incremental upsert on a cron

Deletes within seconds on ClickHouse Cloud

ClickPipes

Managed binlog CDC with GA support

Deletes within seconds on self-hosted ClickHouse

Estuary or PeerDB

Native writes with delete handling

Kafka already in production

Debezium plus Kafka sink

Reuse the bus, map deletes yourself

Small lookup tables, no copy wanted

MySQL table engine

Zero infrastructure

Many SaaS sources alongside MySQL

Airbyte or Fivetran

Breadth of catalog

Filament is the pick for the engineer who owns both databases, rebuilds or upserts analytics tables on a schedule, and wants no gigabyte meter and no Kafka. ClickPipes wins once sub-minute deletes on ClickHouse Cloud are the requirement, and Estuary or PeerDB win that requirement on a self-hosted cluster.

Related guides

Frequently asked questions

What is the best way to replicate MySQL to ClickHouse?

It depends on whether you need deletes. ClickPipes on ClickHouse Cloud and Debezium through Kafka replay the MySQL binlog, so they carry deletes. Batch tools such as Filament, Airbyte, dlt, and Sling copy whole tables or page by an updated_at cursor, which is cheaper to run but cannot see a hard delete without a full reload.

Does ClickHouse support native MySQL CDC?

Only on ClickHouse Cloud. The MySQL ClickPipe reached general availability in September 2026 and streams binlog changes from RDS, Aurora, Cloud SQL, self-hosted MySQL, and MariaDB. The old MaterializedMySQL database engine for self-hosted servers was removed in ClickHouse 25.1, so self-hosted users need an external tool.

Can ClickHouse query MySQL without copying the data?

Yes. The MySQL table engine and the mysql() table function run SELECT statements against a remote MySQL server on every query. Only simple comparison predicates are pushed down, so each query costs MySQL a scan, which makes this fine for small lookup tables and wrong for analytics on large ones.

Do I need Kafka to move MySQL data to ClickHouse?

No. Kafka is only required for the Debezium path, where the MySQL connector writes change events to topics and the ClickHouse Kafka Connect sink reads them. ClickPipes, PeerDB, Airbyte, Fivetran, Estuary, Filament, dlt, Sling, and ingestr all connect MySQL to ClickHouse without a message bus in between.

Why do my ClickHouse queries need FINAL after replication?

Every tool that writes upserts to ClickHouse uses ReplacingMergeTree, and that engine removes duplicate keys only when parts merge in the background. Until a merge runs, a key can have several rows. Adding FINAL to the SELECT collapses them at query time at the cost of extra work per query.

What MySQL settings does binlog replication need?

The server needs binlog_format set to ROW, binlog_row_image set to FULL, and binlog retention long enough to cover the initial snapshot. Retention defaults to 30 days on MySQL but to zero hours on Amazon RDS. The replication user needs the REPLICATION SLAVE and REPLICATION CLIENT privileges. GTID mode is optional but survives failover.

Does Filament replicate MySQL to ClickHouse with CDC?

Not today. Filament reads the MySQL binlog for sinks that can apply row-level deletes, but its ClickHouse sink supports full replace, full append, and upsert modes only. Pick it when a scheduled full or incremental copy is what you want, and pick a log-based tool when deletes must land in ClickHouse within seconds.

How does Filament handle duplicate rows in ClickHouse?

Upsert modes create a ReplacingMergeTree table keyed on the source primary key, so repeated rows from a resumed run converge by key once parts merge. Replace mode avoids the question entirely by loading into a staging table and swapping it in with one atomic EXCHANGE TABLES at commit.

More articles

Stay up to date with what weโ€™re building

Stay up to date with what weโ€™re building

Stay up to date with what weโ€™re building

Questions

Answered

FAQ

What does Galaxy do?

What is Filament?

What is enterprise context management?

What does working with Galaxy look like?

How do you handle security and compliance?

Why does Galaxy build in the open?

Own your knowledge stack

Own your knowledge stack

Copyright ยฉ 2026 Galaxy. All rights reserved.