โ† Previous: Module 8 - Disaster Recovery ๐Ÿ  Home Next Module โ†’

๐Ÿ“Š MODULE 9

Kafka-Based Real-Time Ingestion
Duration: 4-6 hours | Week 9
๐Ÿ“– What is Kafka-Based Real-Time Ingestion?

Kafka Engine in ClickHouse

The Kafka table engine allows ClickHouse to consume messages directly from Apache Kafka topics in real-time. It provides a bridge between Kafka streams and ClickHouse's analytical processing capabilities, enabling sub-second latency data ingestion at scale.

Key Components

๐Ÿ“จ

Kafka Engine

Native ClickHouse engine that consumes from Kafka without external tools

๐Ÿ”„

Materialized Views

Automatically transform and persist messages to MergeTree tables

๐Ÿ‘ฅ

Consumer Groups

Distribute load across multiple ClickHouse instances

๐Ÿ“

Message Formats

Support JSON, Avro, Protobuf, CSV and more formats

Kafka Engine vs Kafka Connect Sink

Feature Kafka Engine (Native) Kafka Connect Sink
Architecture Built-in ClickHouse engine Separate connector service
Latency Lower (direct consumption) Higher (extra hop)
Complexity Simpler setup More operational overhead
Scaling Leverages ClickHouse cluster Requires separate connector cluster
Monitoring Built-in ClickHouse metrics Separate monitoring needed
Best For Direct Kafka โ†’ ClickHouse flows Multi-destination data pipelines
Recommended โœ… For most use cases When you need multi-sink architecture

Kafka โ†’ ClickHouse Pipeline Architecture

End-to-End Streaming Pipeline

1. Producer Layer
Application / Microservices / IoT Devices
โ†“ Publish Messages โ†“
2. Kafka Topic
Partition 0
Offset: 12,450
Partition 1
Offset: 12,389
Partition 2
Offset: 12,501
โ†“ Consume โ†“
3. Kafka Engine (ClickHouse)
Consumer Group: clickhouse-events
Consumers: 3 (parallel consumption)
โ†“ Transform โ†“
4. Materialized View
Parse JSON โ†’ Extract fields โ†’ Enrich data โ†’ Filter
โ†“ Persist โ†“
5. MergeTree Table (Storage)
Compressed, Indexed, Partitioned, Query-ready
โ†“ Analyze โ†“
6. SQL Queries
Real-time Analytics / Dashboards / Reports

Architecture Overview

Typical Data Flow:

Application/Producer โ†’ Kafka Topic โ†“ Kafka Engine (Reads Topic) โ†“ Materialized View (Transform) โ†“ MergeTree Table (Persist) โ†“ SELECT Queries (Analyze)

Key Advantage: Everything happens within ClickHouse - no external dependencies for the core ingestion pipeline.

How Kafka Engine Works

Message Consumption

  • Reads from Kafka topics as a virtual table
  • Supports multiple partitions
  • Manages consumer group offsets
  • Can handle backpressure
  • Supports exactly-once semantics

Offset Management

  • Kafka stores offset state
  • ClickHouse can resume from last offset
  • Supports offset reset strategies
  • Manual offset management possible
  • No duplicate handling by default
๐Ÿš€ Quick Start: Kafka Engine Setup

1. Prerequisites

  • Apache Kafka 0.9+ (installed and running)
  • ClickHouse 20.7+
  • Network connectivity between ClickHouse and Kafka brokers

2. Create Kafka Table

3. Create Materialized View for Persistence

4. Test with Sample Data

5. Verify Setup

๐Ÿ’ป Kafka Engine Commands & Configuration

Basic Kafka Table Creation

Message Format Handlers

JSON Format

kafka_format = 'JSONEachRow'
-- Each Kafka message: one JSON object

CSV Format

kafka_format = 'CSV'
kafka_skip_broken_messages = 1

Avro Format

kafka_format = 'Avro'
kafka_schema = 'schema_registry_url'

Protobuf Format

kafka_format = 'Protobuf'
kafka_schema_registry_url = 'http://...'

Consumer Group Configuration

Monitoring Kafka Engine

โœจ Kafka Ingestion Best Practices

1. Materialized Views Design

โŒ Bad: Direct Kafka Queries
-- DON'T query Kafka table directly
SELECT * FROM kafka_events LIMIT 1000;

-- Problem: Messages are consumed &
-- won't be replayed. Lost data!
โœ… Good: Use Materialized Views

2. Consumer Group Strategy

Single Consumer Group per Table

  • Recommended: One consumer group per table/topic pair
  • Avoids offset conflicts between different pipelines
  • Simplifies monitoring and debugging
  • Each consumer group tracks its own offset

3. Backpressure & Throughput Tuning

Handle High Message Rates

  • Increase kafka_num_consumers for parallelism
  • Adjust kafka_max_block_size for batching
  • Tune kafka_poll_timeout_ms to balance latency vs throughput
  • Monitor ClickHouse resource usage (CPU, memory, disk I/O)

4. Error Handling & Dead Letter Queues

Graceful Error Handling

5. Offset Management Strategies

Offset Reset Strategies

  • smallest: Start from beginning (replay all)
  • largest: Start from end (only new messages)
  • offset: Specify exact offset
kafka_auto_offset_reset = 'smallest'

Resuming from Offset

  • ClickHouse auto-manages offsets in Kafka
  • Restart automatically resumes from last committed offset
  • No duplicate handling: setup application-level deduplication

6. Message Format Best Practices

Choosing the Right Format

  • JSON: Simple, human-readable, good for flexible schemas
  • Avro: Efficient, schema evolution, binary format
  • Protobuf: Compact, strongly typed, version-safe
  • CSV: Lightweight, fixed schema

7. Scaling Kafka Ingestion

Horizontal Scaling Patterns

  • Multiple ClickHouse consumers: Same consumer group spreads load
  • Multiple Kafka partitions: Match partitions to consumer count
  • Distributed tables: Scale across cluster with Distributed engine
๐ŸŽฏ When to Use Kafka Engine

โœ… Ideal For

  • Real-time streaming analytics
  • Event sourcing systems
  • High-volume event ingestion (millions/sec)
  • Applications with Kafka infrastructure
  • Multi-source data consolidation
  • Continuous data pipelines
  • IoT sensor data streams
  • Log aggregation and analysis
  • Real-time monitoring dashboards

โŒ Not Ideal For

  • Batch imports (use INSERT INTO)
  • One-time data migrations
  • Low-volume infrequent events
  • When you don't have Kafka
  • Exactly-once delivery requirements (use ACID DB)
  • Complex multi-step transformations
  • Transactional workloads

Comparison with Alternatives

Method Latency Throughput Complexity Best For
Kafka Engine Sub-second Millions/sec Medium Real-time streaming
INSERT INTO Depends Thousands/sec Low Batch loading
Kafka Connect Seconds Millions/sec High Multi-destination pipelines
S3 Import Minutes High Low Batch from cloud storage
JDBC/ODBC Seconds Thousands/sec Low Pull from external DB
๐Ÿ† Real-World Results & Case Studies
10M+

Events/Second

Sustained ingestion from Kafka with single cluster

< 500ms

End-to-End Latency

From Kafka to query-ready in ClickHouse

99.9%

Uptime

Reliable ingestion with minimal operational overhead

10-50x

Cost Reduction

vs traditional data warehouses with Kafka ingestion

Case Study 1: E-Commerce Event Analytics

Challenge

Process 5 million user events/day from mobile apps, web, backend services. Provide real-time dashboards with <1s query latency.

Solution

  • 7 Kafka topics (one per event type)
  • ClickHouse 3-node cluster (3 shards, 2 replicas each)
  • Kafka Engine with materialized views
  • TTL for data retention (90 days)

Results

  • Ingestion latency: 200-500ms p99
  • Query latency: 100-300ms for dashboard queries
  • Storage: 50TB raw compressed to 2TB
  • Cost: 70% reduction vs previous Elasticsearch stack

Case Study 2: Real-Time Monitoring System

Challenge

Monitor 10,000 servers generating 2M metrics/second. Alert on anomalies in real-time.

Solution

  • Custom metrics producer โ†’ Kafka topic
  • 5-node ClickHouse cluster
  • Separate consumer groups per metric type
  • ReplacingMergeTree for latest metric state

Results

  • Ingestion: 2M events/sec (within SLA)
  • Alert latency: 5-10 seconds
  • Data retention: 30 days (automatic TTL)
  • Operational cost: $50K/month (vs $300K+ for commercial TSDB)

Case Study 3: User Behavior Analytics

Challenge

Analyze 100M user interactions/day. Support ad-hoc exploration with complex queries.

Solution

  • Multiple Kafka topics for different event types
  • Data enrichment via materialized view joins
  • Distributed ClickHouse cluster (6 nodes)
  • ReplicatedMergeTree for fault tolerance

Results

  • Query performance: 0.5-2s for any analysis
  • Data freshness: ~1 minute from event to query
  • Enabled new self-service analytics platform
  • Reduced dependency on data scientists for ad-hoc queries
๐Ÿ“‹ Production-Ready Templates

Template 1: JSON Event Streaming

Template 2: Metrics/Time-Series from Kafka

Template 3: Logs Aggregation from Kafka

๐Ÿ”ฅ Advanced Patterns

1. Data Enrichment with Materialized Views

2. Real-Time Aggregation with SummingMergeTree

3. Deduplication with ReplacingMergeTree

4. Distributed Kafka Ingestion Across Cluster

๐Ÿ“š Resources & References

๐Ÿ“˜ View in Notion

Access this module in your Notion workspace for note-taking and tracking your progress.

Open in Notion โ†’

๐Ÿ› ๏ธ Tools & Integrations

๐ŸŽฏ Next Steps

After completing Module 9, you should be comfortable with:

  • โœ… Understanding Kafka Engine architecture and setup
  • โœ… Creating Kafka tables and materialized views
  • โœ… Configuring consumer groups and offset management
  • โœ… Handling different message formats (JSON, Avro, Protobuf)
  • โœ… Implementing error handling and monitoring
  • โœ… Optimizing for high throughput and low latency

๐Ÿณ Hands-on Docker Demo for this Module

A self-contained ClickHouse stack you can run locally. Every step is reproducible; the README below walks through each concept with diagrams and measured outcomes.

cd code-examples/demos/module-9-kafka
./up.sh        # bring stack up, wait for health
./run.sh       # run the demo (idempotent โ€” re-runnable)
./down.sh      # tear down + drop volumes

๐Ÿ“บ Live demo capture

Recorded with vhs: the actual terminal output from ./run.sh running on this very stack.

Module demo GIF
Animated GIF, ~1198 KB. Right-click โ†’ open in new tab to view full-size.

โœจ Animated concept diagrams

Inline SVG animations โ€” no plugins, native browser playback. Each visualises a key flow this module covers.

Kafka pipeline
Kafka pipeline

๐Ÿ—‚๏ธ Architecture diagrams

Rendered from the README's Mermaid sources. Click any to open the source SVG full-size.

Subgraph kafka kafka topic events br n p
Subgraph kafka kafka topic events br n p
Actor producer as producer
Actor producer as producer

๐Ÿ“š ClickHouse reference visuals

Curated from the official ClickHouse documentation and engineering blog (and Altinity's docs/blog) โ€” the same diagrams the broader CH community uses to explain these concepts. Click any image to read the source.

Kafka engine table -> materialized view -> MergeTree storage flow
ClickHouse Blog Kafka engine table -> materialized view -> MergeTree storage flow
Confluent Cloud + Kafka Connect sink into ClickHouse architecture
ClickHouse Blog Confluent Cloud + Kafka Connect sink into ClickHouse architecture
ClickPipes managed Kafka ingestion architecture
ClickHouse Blog ClickPipes managed Kafka ingestion architecture
ClickPipes overall ingestion architecture (Kafka, Kinesis, object stores)
ClickHouse Blog ClickPipes overall ingestion architecture (Kafka, Kinesis, object stores)
Streaming ingestion architecture (same shape as Kafka pipeline)
ClickHouse Blog Streaming ingestion architecture (same shape as Kafka pipeline)
Animated: end-to-end Kafka -> ClickPipes -> ClickHouse demo
ClickHouse Blog Animated: end-to-end Kafka -> ClickPipes -> ClickHouse demo

๐Ÿ“š Full module reference

Module 9 โ€” Kafka Ingestion

Audience: anyone wiring Kafka into ClickHouse. Prerequisites: Modules 1โ€“2. Time: ~70 min reading + 30 min hands-on.

By the end you will be able to:


1. The mental model

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "The mental model".

Here is a brief excerpt from the material I'm reading:

"""
ClickHouse's Kafka engine is a **consumer**, not a destination. Selecting from it consumes messages. To make ingest durable you put a Materialized View on top of it that writes the consumed rows to a real MergeTree. Three rules to remember: 1. **Don't `SELECT * FROM events_kafka`.** A direct SELECT consumes messages and throws them away. Use it only for diagnosis with `LIMIT 1`. 2. **The MV is the durability boundary.** If you DROP the MV without replacement, the engine still consumes โ€” your data is gone. 3. **One Kafka source can feed N MVs.** A single read of the topic can power both the dur...
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

ClickHouse's Kafka engine is a consumer, not a destination. Selecting from it consumes messages. To make ingest durable you put a Materialized View on top of it that writes the consumed rows to a real MergeTree.

Three rules to remember:

  1. Don't SELECT * FROM events_kafka. A direct SELECT consumes messages and throws them away. Use it only for diagnosis with LIMIT 1.
  2. The MV is the durability boundary. If you DROP the MV without replacement, the engine still consumes โ€” your data is gone.
  3. One Kafka source can feed N MVs. A single read of the topic can power both the durable destination and any number of pre-aggregations.

2. The Kafka source table

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "The Kafka source table".

Here is a brief excerpt from the material I'm reading:

"""
Settings worth knowing: | Setting                        | Default                | What it controls                                                            | | `kafka_broker_list`            | required                | Comma-separated `host:port`. Multiple brokers is fine.                      | | `kafka_topic_list`             | required                | Comma-separated topics. Regex with `kafka_topic_regex`.                     | | `kafka_group_name`             | required                | Consumer group ID. Multiple CH instances in the same group share partitions. | | `kafka_format`...
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.
CREATE TABLE events_kafka (
    event_time DateTime,
    user_id    UInt64,
    event_type String,
    revenue    Float64,
    payload    String
)
ENGINE = Kafka
SETTINGS
    kafka_broker_list   = 'm9-kafka:29092',
    kafka_topic_list    = 'events',
    kafka_group_name    = 'ch_consumer',
    kafka_format        = 'JSONEachRow',
    kafka_num_consumers = 1,
    kafka_max_block_size = 1048576;

Settings worth knowing:

Setting Default What it controls
kafka_broker_list required Comma-separated host:port. Multiple brokers is fine.
kafka_topic_list required Comma-separated topics. Regex with kafka_topic_regex.
kafka_group_name required Consumer group ID. Multiple CH instances in the same group share partitions.
kafka_format required JSONEachRow, CSV, TabSeparated, Avro, AvroConfluent, Protobuf, โ€ฆ
kafka_num_consumers 1 Threads per topic per node.
kafka_max_block_size 1 048 576 Rows the consumer batches before emitting one block.
kafka_thread_per_consumer 0 If 1, each consumer gets its own thread (useful >1 partition).
kafka_skip_broken_messages 0 Skip N badly-formed messages per block; rest of the block proceeds.
kafka_handle_error_mode 'default' 'stream' exposes _error / _raw_message virtual columns. See ยง5.
kafka_commit_every_batch 0 Commit per batch instead of per block. Trades latency for granularity.
kafka_max_rows_per_message 1 For some formats, multiple rows per message.

Server-level defaults live in <kafka> in the server config:

<kafka>
    <num_threads>2</num_threads>
    <flush_interval_ms>7500</flush_interval_ms>
    <flush_on_rows>10000</flush_on_rows>
    <max_retries>30</max_retries>
    <retry_backoff_ms>100</retry_backoff_ms>
</kafka>

3. The Materialized View glue

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "The Materialized View glue".

Here is a brief excerpt from the material I'm reading:

"""
The MV reads the Kafka source and writes to the destination. As long as the MV exists, the engine consumes. Drop it (`DROP TABLE events_mv`) and consumption stops; CH commits no further offsets. ### Multiple MVs from one source Both MVs run from the same Kafka read. Single network/IO, two writes.
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.
CREATE TABLE events (
    event_time  DateTime,
    user_id     UInt64,
    event_type  LowCardinality(String),
    revenue     Float64,
    payload     String,
    inserted_at DateTime DEFAULT now()
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(event_time)
ORDER BY (event_type, event_time);

CREATE MATERIALIZED VIEW events_mv TO events AS
SELECT event_time, user_id, event_type, revenue, payload
FROM events_kafka;

The MV reads the Kafka source and writes to the destination. As long as the MV exists, the engine consumes. Drop it (DROP TABLE events_mv) and consumption stops; CH commits no further offsets.

Multiple MVs from one source

CREATE TABLE events_per_minute (...) ENGINE = SummingMergeTree ORDER BY (...);

CREATE MATERIALIZED VIEW events_per_minute_mv TO events_per_minute AS
SELECT toStartOfMinute(event_time) AS minute,
       event_type,
       count() AS events,
       sum(revenue) AS revenue
FROM events_kafka
GROUP BY minute, event_type;

Both MVs run from the same Kafka read. Single network/IO, two writes.


4. The end-to-end flow

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "The end-to-end flow".

Here is a brief excerpt from the material I'm reading:

"""
Failure semantics: - **Server crash mid-block** โ€” uncommitted messages are re-consumed on restart. `events` may have duplicates if the prior INSERT partially succeeded; deduplicate in the destination if it matters. - **MV crash** โ€” same as above; the MV's INSERT errored, so the offset isn't committed. - **Bad message** โ€” by default, the *whole block* fails. Enable `kafka_skip_broken_messages` or `kafka_handle_error_mode = 'stream'`.
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

Failure semantics:


5. Dead Letter Queue โ€” kafka_handle_error_mode = 'stream'

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "Dead Letter Queue โ€” kafka_handle_error_mode = 'stream'".

Here is a brief excerpt from the material I'm reading:

"""
(no excerpt โ€” section heading was "Dead Letter Queue โ€” kafka_handle_error_mode = 'stream'")
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

The default mode poisons a block of good messages if any one of them fails to parse. The 'stream' mode lets bad messages flow with two extra virtual columns:

Virtual column Type Contains
_error String Empty if the message parsed cleanly; an error string if not.
_raw_message String The raw bytes of the message.
_topic LowCardinality(String) Topic name.
_partition UInt64 Partition number.
_offset UInt64 Offset within the partition.
_timestamp Nullable(DateTime) Producer timestamp.

Then split with two MVs:

CREATE TABLE events_kafka_safe (
    event_time DateTime, user_id UInt64,
    event_type String, revenue Float64, payload String
)
ENGINE = Kafka
SETTINGS
    kafka_broker_list       = 'm9-kafka:29092',
    kafka_topic_list        = 'events',
    kafka_group_name        = 'ch_consumer_safe',
    kafka_format            = 'JSONEachRow',
    kafka_handle_error_mode = 'stream';

-- Good messages โ†’ durable destination
CREATE MATERIALIZED VIEW events_safe_mv TO events_safe AS
SELECT event_time, user_id, event_type, revenue, payload
FROM events_kafka_safe WHERE _error = '';

-- Bad messages โ†’ DLQ table for replay/inspection
CREATE TABLE events_dlq (
    received_at DateTime DEFAULT now(),
    error       String,
    raw         String,
    topic       String,
    partition   UInt32,
    offset      UInt64
) ENGINE = MergeTree ORDER BY (received_at, topic, partition);

CREATE MATERIALIZED VIEW events_dlq_mv TO events_dlq AS
SELECT now() AS received_at,
       _error AS error,
       _raw_message AS raw,
       _topic AS topic,
       _partition AS partition,
       _offset AS offset
FROM events_kafka_safe
WHERE _error != '';

Now produce a deliberately-broken message:

echo '{not valid json' | kafka-console-producer --bootstrap-server localhost:9092 --topic events

It lands in events_dlq, not in events_safe. Inspect, fix the producer, optionally re-publish from the DLQ.


6. Exactly-once via ReplacingMergeTree

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "Exactly-once via ReplacingMergeTree".

Here is a brief excerpt from the material I'm reading:

"""
(no excerpt โ€” section heading was "Exactly-once via ReplacingMergeTree")
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

Kafka offers at-least-once delivery. After a failover or replay, you might see the same message twice. To get at-most-once at the table level, dedupe on a stable key with ReplacingMergeTree:

CREATE TABLE events_unique (
    event_time  DateTime,
    user_id     UInt64,
    event_type  LowCardinality(String),
    revenue     Float64,
    payload     String,
    ingested_at DateTime DEFAULT now()    -- the version
)
ENGINE = ReplacingMergeTree(ingested_at)
ORDER BY (user_id, event_time);

CREATE MATERIALIZED VIEW events_unique_mv TO events_unique AS
SELECT event_time, user_id, event_type, revenue, payload, now() AS ingested_at
FROM events_kafka;

-- Read with FINAL or argMax to see only the latest version per key.
SELECT count() FROM events_unique FINAL;

The dedup key (user_id, event_time) must be stable across retries โ€” typically the producer's natural primary key.

Mode Throughput Correctness
Plain MergeTree highest duplicates possible after failure
ReplacingMergeTree high dedup on read (FINAL) or post-merge
AggregatingMergeTree + idempotent agg high duplicates absorbed by the aggregator

7. Format coverage

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "Format coverage".

Here is a brief excerpt from the material I'm reading:

"""
`JSONEachRow` is the demo's choice. CH supports many more: | Format          | Schema     | Notes                                                              | | `JSONEachRow`   | implicit   | One JSON object per line. Most flexible; modest overhead.          | | `JSON`          | implicit   | Single big JSON blob with `data: [...]`. Less common in Kafka.     | | `CSV` / `CSVWithNames` | implicit (or header) | Fast; no nesting.                                       | | `TabSeparated`  | implicit   | Even faster; no quoting needed.                                    | | `Protobuf`      | requi...
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

JSONEachRow is the demo's choice. CH supports many more:

Format Schema Notes
JSONEachRow implicit One JSON object per line. Most flexible; modest overhead.
JSON implicit Single big JSON blob with data: [...]. Less common in Kafka.
CSV / CSVWithNames implicit (or header) Fast; no nesting.
TabSeparated implicit Even faster; no quoting needed.
Protobuf required format_schema = 'events.proto:EventMessage'. Compact, typed.
Avro embedded One schema per message header.
AvroConfluent registry Confluent Schema Registry magic byte + ID.
Parquet embedded Often used for batch ingest, less for streaming.

Schema-bound formats need the schema mounted under /var/lib/clickhouse/format_schemas/:

CREATE TABLE events_kafka_proto (
    ...
)
ENGINE = Kafka
SETTINGS
    kafka_format = 'Protobuf',
    format_schema = 'events.proto:EventMessage',
    ...;

8. Operational SQL cheatsheet

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "Operational SQL cheatsheet".

Here is a brief excerpt from the material I'm reading:

"""
(no excerpt โ€” section heading was "Operational SQL cheatsheet")
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.
-- Lag and assignments
SELECT
    database, table, consumer_id,
    assignments.topic AS topic,
    assignments.partition_id AS partition,
    assignments.current_offset AS offset
FROM system.kafka_consumers
ARRAY JOIN assignments
WHERE database = 'm9';

-- Lifetime counters
SELECT event, value
FROM system.events
WHERE event LIKE 'Kafka%'
ORDER BY event;

-- Pause / resume consumption (without dropping the MV)
DETACH TABLE events_mv;     -- consumer keeps running but doesn't write
ATTACH TABLE events_mv;     -- resume; offsets resume from where Kafka committed

-- Hard reset offsets (replay everything)
docker exec m9-kafka kafka-consumer-groups \
    --bootstrap-server localhost:9092 \
    --group ch_consumer --reset-offsets --to-earliest \
    --topic events --execute

-- Per-consumer-group lag
docker exec m9-kafka kafka-consumer-groups \
    --bootstrap-server localhost:9092 \
    --group ch_consumer --describe

9. The hands-on demo

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "The hands-on demo".

Here is a brief excerpt from the material I'm reading:

"""
### What you get ### Container map | Service     | Container       | Host ports                    | | ClickHouse  | `m9-clickhouse` | 8123, 9000                    | | Kafka       | `m9-kafka`      | 9092 (host), 29092 (internal) | | ZooKeeper   | `m9-zk`         | (internal only)               | | Kafka UI    | `m9-kafka-ui`   | 8080                          | Internally the broker is `m9-kafka:29092` (used in `setup.sql`). From the host: `localhost:9092`. ### Execution flow โ€” what runs, in order | #  | Step                                   | What happens...
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

What you get

docker-compose.yml          m9-clickhouse + m9-kafka + m9-zk + m9-kafka-ui
configs/clickhouse-config.xml
setup.sql ยท extras.sql ยท queries.sql
produce.py                  JSON-line producer (pure stdlib)
up.sh ยท run.sh ยท down.sh

Container map

Service Container Host ports
ClickHouse m9-clickhouse 8123, 9000
Kafka m9-kafka 9092 (host), 29092 (internal)
ZooKeeper m9-zk (internal only)
Kafka UI m9-kafka-ui 8080

Internally the broker is m9-kafka:29092 (used in setup.sql). From the host: localhost:9092.

Execution flow โ€” what runs, in order

# Step What happens
0 self-bootstrap up.sh brings up the 4-container stack and waits until m9-clickhouse:/ping and m9-kafka:9092 both answer.
1 Create events topic kafka-topics --create --if-not-exists --topic events --partitions 3 --replication-factor 1.
2 setup.sql Creates database m9 and the canonical pipeline: events_kafka (Kafka engine, JSONEachRow, group ch_consumer), events (durable MergeTree), events_mv (MV moves rows from Kafka โ†’ events), events_per_minute (SummingMergeTree), events_per_minute_mv (minute-bucket aggregation). One Kafka read serves both MVs.
3 extras.sql Adds the DLQ pipeline: events_kafka_safe with kafka_handle_error_mode = 'stream' (group ch_consumer_safe); two MVs split it on _error == '' โ†’ events_safe, _error != '' โ†’ events_dlq (with topic / partition / offset preserved). Also creates events_unique (ReplacingMergeTree on (user_id, event_time)) and events_unique_mv.
4 Produce valid messages python3 produce.py --rows $ROWS (default 100k) โ†’ piped into kafka-console-producer --topic events.
5 Produce broken messages 50 deliberately malformed JSON lines โ†’ kafka-console-producer --topic events. These get rejected by the strict consumer (events_kafka) but flow through the safe consumer into events_dlq.
6 Sleep 8 s Lets the Kafka MVs catch up (default flush_interval_ms = 7500).
7 queries.sql Inspects: total rows in events, per-event-type breakdown, the SummingMergeTree minute buckets, system.kafka_consumers (assignments, offsets), system.events Kafka counters.
8 DLQ + exactly-once verification Prints events_dlq count (~50, the bad messages), events_safe count (~$ROWS), events_unique FINAL count, top-3 _error strings.

Run with bigger volume:

ROWS=500000 ./run.sh

10. Common pitfalls

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "Common pitfalls".

Here is a brief excerpt from the material I'm reading:

"""
| Symptom                                                                  | Cause                                                                            | Fix                                                                                                | | `SELECT * FROM events_kafka` returns rows once and never again           | Direct SELECT consumes messages.                                                  | Don't. Use the MV. For diagnostics: `SELECT * FROM events_kafka LIMIT 1`.                          | | MV creates millions of tiny parts                                        |...
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.
Symptom Cause Fix
SELECT * FROM events_kafka returns rows once and never again Direct SELECT consumes messages. Don't. Use the MV. For diagnostics: SELECT * FROM events_kafka LIMIT 1.
MV creates millions of tiny parts Tiny kafka_max_block_size and high message rate. Raise to 1048576 rows or 64 * 1024 * 1024 bytes. Match flush_on_rows server setting.
One bad message stops ingestion Default kafka_handle_error_mode = 'default'. kafka_skip_broken_messages = N for tolerance, or DLQ pattern.
Duplicates after Kafka rebalance Kafka redelivered uncommitted messages. Use ReplacingMergeTree on a stable key, or AggregatingMergeTree with idempotent aggregates.
"Cannot resolve hostname m9-kafka" kafka_broker_list uses internal hostname; CH can't reach it from host network. Use the hostname that's reachable from CH (the demo uses m9-kafka:29092 on the shared network).
Lag grows unbounded Single consumer can't keep up with topic throughput. Increase kafka_num_consumers, or scale CH replicas in the same group.
Consumer group resets every restart Group ID changes (e.g. random suffix). Use a stable kafka_group_name. Treat it like a database name.
Offsets stuck on a corrupted message The block keeps failing; CH retries forever. Skip the offset via the kafka CLI tools, or switch to 'stream' mode + DLQ.

11. Tuning knobs by goal

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "Tuning knobs by goal".

Here is a brief excerpt from the material I'm reading:

"""
| Goal                                  | Knobs                                                                                       | | Throughput                            | `kafka_num_consumers`โ†‘, `kafka_max_block_size`โ†‘, multiple CH replicas in same group         | | Latency                               | `flush_interval_ms`โ†“, `flush_on_rows`โ†“, `kafka_max_block_size`โ†“                              | | Tolerance to bad messages             | `kafka_handle_error_mode = 'stream'` + DLQ MV                                                | | RPO=0 (no message loss)               | producer `ac...
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.
Goal Knobs
Throughput kafka_num_consumersโ†‘, kafka_max_block_sizeโ†‘, multiple CH replicas in same group
Latency flush_interval_msโ†“, flush_on_rowsโ†“, kafka_max_block_sizeโ†“
Tolerance to bad messages kafka_handle_error_mode = 'stream' + DLQ MV
RPO=0 (no message loss) producer acks=all + kafka_commit_every_batch=1 + dedup on consumer
Smaller storage footprint events.payload with CODEC(ZSTD), partition by day, drop column-level TTL

12. Talking points for the live session

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "Talking points for the live session".

Here is a brief excerpt from the material I'm reading:

"""
1. **Kafka engine is a *consumer*, not a destination.** Demonstrate by `SELECT * FROM events_kafka LIMIT 5` and watching messages disappear. 2. **MV is the durability boundary.** Drop the MV, produce more messages, show that they're consumed and discarded. 3. **DLQ in 30 seconds.** Show drill 5 (broken JSON) and the `events_dlq` table. 4. **Exactly-once is a property of the destination,** not the source. `ReplacingMergeTree` is how CH gets there. 5. **Schema-bound formats** (Protobuf/Avro) for production. Mention `format_schema_path` and the AvroConfluent magic byte. 6. **Operational ergonomic...
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.
  1. Kafka engine is a consumer, not a destination. Demonstrate by SELECT * FROM events_kafka LIMIT 5 and watching messages disappear.
  2. MV is the durability boundary. Drop the MV, produce more messages, show that they're consumed and discarded.
  3. DLQ in 30 seconds. Show drill 5 (broken JSON) and the events_dlq table.
  4. Exactly-once is a property of the destination, not the source. ReplacingMergeTree is how CH gets there.
  5. Schema-bound formats (Protobuf/Avro) for production. Mention format_schema_path and the AvroConfluent magic byte.
  6. Operational ergonomics: system.kafka_consumers, kafka-cli group-describe, DETACH/ATTACH MV for pause/resume.

13. Going deeper

๐Ÿ’ฌ Discuss with AI โ€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse + Kafka Ingestion". I want to deeply understand the section: "Going deeper".

Here is a brief excerpt from the material I'm reading:

"""
- **Module 2** โ€” the engines this module writes into. - **Module 6** โ€” query optimisation for the high-volume `events` table. - ClickHouse docs: <https://clickhouse.com/docs/en/integrations/kafka> - Confluent's "Kafka Connect ClickHouse Sink" for the *opposite* direction (CH โ†’ Kafka).
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** โ€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** โ€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** โ€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** โ€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** โ€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** โ€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

๐Ÿ“Š ClickHouse Knowledge Transfer

Module 9 of 9 | Duration: 6-8 weeks total

Created for comprehensive ClickHouse training | 2026

โ† Previous: Module 8 - Disaster Recovery ๐Ÿ  Home Next Module โ†’