Introduction

ClickHouse is an open-source column-oriented database management system designed for online analytical processing (OLAP). It’s optimized for fast analytical queries on large datasets. Understanding ClickHouse is essential for system design interviews involving analytics, reporting, and data warehousing.

This guide covers:

  • ClickHouse Fundamentals: Columnar storage, tables, and engines
  • Query Language: SQL queries and aggregations
  • Materialized Views: Pre-computed aggregations
  • Distributed Queries: Cluster configuration and sharding
  • Best Practices: Schema design, partitioning, and optimization

What is ClickHouse?

ClickHouse is a column-oriented database that:

  • Columnar Storage: Optimized for analytical queries
  • High Performance: Sub-second queries on billions of rows
  • Compression: Efficient data compression
  • Scalability: Horizontal scaling with clustering
  • SQL Interface: Standard SQL query language

Key Concepts

Table: Collection of columns and rows

Engine: Storage engine (MergeTree, ReplicatedMergeTree, etc.)

Partition: Logical division of data by key

Shard: Physical division of data across servers

Replica: Copy of data for high availability

Architecture

High-Level Architecture

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Client    │────▶│   Client    │────▶│   Client    │
│ Application │     │ Application │     │ Application │
└──────┬──────┘     └──────┬──────┘     └──────┬──────┘
       │                    │                    │
       └────────────────────┴────────────────────┘
                            │
                            │ SQL Queries
                            │
                            ▼
              ┌─────────────────────────┐
              │   ClickHouse Cluster     │
              │                         │
              │  ┌──────────┐           │
              │  │ Server   │           │
              │  │  Node 1  │           │
              │  └────┬─────┘           │
              │       │                 │
              │  ┌────┴─────┐           │
              │  │ Server   │           │
              │  │  Node 2  │           │
              │  └──────────┘           │
              │                         │
              │  ┌───────────────────┐  │
              │  │  Distributed      │  │
              │  │  Tables           │  │
              │  └───────────────────┘  │
              └─────────────────────────┘

Explanation:

  • Client Applications: Applications that query ClickHouse for analytical workloads (e.g., business intelligence tools, analytics platforms, reporting systems).
  • ClickHouse Cluster: A collection of ClickHouse server nodes that work together to store and query large volumes of data.
  • Server Nodes: Individual ClickHouse servers that store data and execute queries. Can be organized in clusters for scalability.
  • Distributed Tables: Logical tables that span multiple nodes, enabling parallel query execution and horizontal scaling.

Core Architecture

┌─────────────────────────────────────────────────────────┐
│              ClickHouse Cluster                          │
│                                                           │
│  ┌──────────────────────────────────────────────────┐    │
│  │         ClickHouse Server 1                      │    │
│  │  (Query Processing, Storage)                      │    │
│  └──────────────────────────────────────────────────┘    │
│                          │                                │
│  ┌──────────────────────────────────────────────────┐    │
│  │         ClickHouse Server 2                      │    │
│  │  (Query Processing, Storage)                      │    │
│  └──────────────────────────────────────────────────┘    │
│                          │                                │
│  ┌──────────────────────────────────────────────────┐    │
│  │         Distributed Table                         │    │
│  │  (Query Routing, Result Merging)                  │    │
│  └──────────────────────────────────────────────────┘    │
└───────────────────────────────────────────────────────────┘

Table Engines

MergeTree

Basic Table:

CREATE TABLE events (
    id UInt64,
    timestamp DateTime,
    user_id UInt32,
    event_type String,
    amount Decimal(10, 2)
) ENGINE = MergeTree()
ORDER BY (timestamp, user_id)
PARTITION BY toYYYYMM(timestamp);

Key Features:

  • Primary key for sorting
  • Partitioning for data management
  • Automatic merging of parts

ReplicatedMergeTree

Replicated Table:

CREATE TABLE events (
    id UInt64,
    timestamp DateTime,
    user_id UInt32,
    event_type String,
    amount Decimal(10, 2)
) ENGINE = ReplicatedMergeTree(
    '/clickhouse/tables/{shard}/events',
    '{replica}'
)
ORDER BY (timestamp, user_id)
PARTITION BY toYYYYMM(timestamp);

Benefits:

  • High availability
  • Data replication
  • Automatic failover

Distributed

Distributed Table:

CREATE TABLE events_distributed AS events
ENGINE = Distributed(
    cluster_name,
    default,
    events,
    rand()
);

Benefits:

  • Query distribution
  • Load balancing
  • Horizontal scaling

Data Types

Numeric Types

CREATE TABLE numbers (
    id UInt64,
    count UInt32,
    price Decimal(10, 2),
    ratio Float32
);

String Types

CREATE TABLE strings (
    name String,
    code FixedString(10),
    description String
);

Date/Time Types

CREATE TABLE dates (
    date Date,
    datetime DateTime,
    datetime64 DateTime64(3)
);

Array Types

CREATE TABLE arrays (
    tags Array(String),
    numbers Array(Int32)
);

Queries

Basic Queries

Select:

SELECT * FROM events
WHERE timestamp >= '2024-01-01'
LIMIT 100;

Aggregation:

SELECT 
    event_type,
    count() AS events,
    sum(amount) AS total_amount
FROM events
WHERE timestamp >= '2024-01-01'
GROUP BY event_type
ORDER BY total_amount DESC;

Time-Based Queries

Group by Time:

SELECT 
    toStartOfHour(timestamp) AS hour,
    count() AS events
FROM events
WHERE timestamp >= '2024-01-01'
GROUP BY hour
ORDER BY hour;

Time Windows:

SELECT 
    toStartOfInterval(timestamp, INTERVAL 5 MINUTE) AS window,
    count() AS events
FROM events
GROUP BY window
ORDER BY window;

Window Functions

Row Number:

SELECT 
    user_id,
    timestamp,
    amount,
    row_number() OVER (PARTITION BY user_id ORDER BY timestamp) AS rn
FROM events;

Rank:

SELECT 
    user_id,
    amount,
    rank() OVER (ORDER BY amount DESC) AS rank
FROM events;

Materialized Views

Create Materialized View

CREATE MATERIALIZED VIEW events_hourly
ENGINE = SummingMergeTree()
ORDER BY (hour, event_type)
AS SELECT
    toStartOfHour(timestamp) AS hour,
    event_type,
    count() AS events,
    sum(amount) AS total_amount
FROM events
GROUP BY hour, event_type;

Benefits:

  • Pre-computed aggregations
  • Faster queries
  • Reduced storage

AggregatingMergeTree

CREATE MATERIALIZED VIEW events_daily
ENGINE = AggregatingMergeTree()
ORDER BY (date, event_type)
AS SELECT
    toDate(timestamp) AS date,
    event_type,
    countState() AS events,
    sumState(amount) AS total_amount
FROM events
GROUP BY date, event_type;

Partitioning

Date Partitioning

CREATE TABLE events (
    id UInt64,
    timestamp DateTime,
    user_id UInt32
) ENGINE = MergeTree()
ORDER BY (timestamp, user_id)
PARTITION BY toYYYYMM(timestamp);

Custom Partitioning

CREATE TABLE events (
    id UInt64,
    timestamp DateTime,
    user_id UInt32,
    country String
) ENGINE = MergeTree()
ORDER BY (timestamp, user_id)
PARTITION BY (country, toYYYYMM(timestamp));

Distributed Queries

Cluster Configuration

config.xml:

<clickhouse>
    <remote_servers>
        <cluster_name>
            <shard>
                <replica>
                    <host>server1</host>
                    <port>9000</port>
                </replica>
            </shard>
            <shard>
                <replica>
                    <host>server2</host>
                    <port>9000</port>
                </replica>
            </shard>
        </cluster_name>
    </remote_servers>
</clickhouse>

Distributed Table

CREATE TABLE events_distributed AS events
ENGINE = Distributed(
    cluster_name,
    default,
    events,
    user_id  -- Sharding key
);

Performance Characteristics

Maximum Read & Write Throughput

Single Node:

  • Max Write Throughput:
    • Simple inserts: 10K-50K inserts/sec
    • Batch inserts: 100K-1M rows/sec
    • With MergeTree engine: 500K-2M rows/sec (optimized for analytics)
  • Max Read Throughput:
    • Simple queries (indexed): 1K-10K queries/sec
    • Complex queries (aggregations): 100-1K queries/sec
    • With materialized views: 5K-25K queries/sec (pre-computed)

Distributed Cluster:

  • Max Write Throughput: 100K-1M rows/sec per node (linear scaling)
  • Max Read Throughput: 1K-10K queries/sec per node (linear scaling)
  • Example: 10-node cluster can handle 1M-10M rows/sec and 10K-100K queries/sec total

Factors Affecting Throughput:

  • Table engine (MergeTree optimized for writes)
  • Compression (compressed data = faster queries)
  • Materialized views (pre-computed = much faster queries)
  • Partitioning strategy
  • Primary key and index design
  • Hardware (CPU, RAM, disk I/O, SSD recommended)
  • Query complexity (simple queries = higher throughput)
  • Data volume (larger datasets = slower queries)

Optimized Configuration:

  • Max Write Throughput: 2M-5M rows/sec per node (with optimized MergeTree and batch inserts)
  • Max Read Throughput: 10K-50K queries/sec per node (with materialized views and proper indexing)

Performance Optimization

Indexing

Primary Key:

CREATE TABLE events (
    id UInt64,
    timestamp DateTime,
    user_id UInt32
) ENGINE = MergeTree()
ORDER BY (timestamp, user_id);  -- Primary key

Secondary Index:

ALTER TABLE events ADD INDEX idx_user_id user_id TYPE minmax GRANULARITY 4;

Compression

Codec:

CREATE TABLE events (
    id UInt64,
    timestamp DateTime,
    user_id UInt32 Codec(ZSTD(3)),
    event_type String Codec(ZSTD(1))
) ENGINE = MergeTree()
ORDER BY (timestamp, user_id);

Query Optimization

Use WHERE on Primary Key:

-- Good: Uses primary key
SELECT * FROM events
WHERE timestamp >= '2024-01-01' AND timestamp < '2024-01-02';

-- Less efficient: No primary key usage
SELECT * FROM events
WHERE user_id = 12345;

Limit Results:

SELECT * FROM events
WHERE timestamp >= '2024-01-01'
LIMIT 1000;

Best Practices

1. Schema Design

  • Use appropriate data types
  • Design primary key for queries
  • Partition by time or key
  • Keep columns narrow

2. Partitioning

  • Partition by time for time-series
  • Avoid too many partitions
  • Use appropriate partition size
  • Plan for data growth

3. Materialized Views

  • Pre-compute common aggregations
  • Use appropriate engine
  • Monitor view size
  • Refresh strategy

4. Performance

  • Use primary key in WHERE
  • Limit result sets
  • Use appropriate compression
  • Monitor query performance

What Interviewers Look For

Analytics Database Understanding

  1. ClickHouse Concepts
    • Understanding of columnar storage
    • Table engines
    • Materialized views
    • Red Flags: No ClickHouse understanding, wrong model, no views
  2. OLAP Patterns
    • Aggregation strategies
    • Time-based queries
    • Partitioning strategies
    • Red Flags: Poor aggregations, no time-series, poor partitioning
  3. Performance
    • Query optimization
    • Indexing strategies
    • Compression
    • Red Flags: No optimization, poor indexes, no compression

Problem-Solving Approach

  1. Schema Design
    • Primary key design
    • Partitioning strategy
    • Data type selection
    • Red Flags: Poor keys, no partitioning, wrong types
  2. Query Optimization
    • Primary key usage
    • Materialized views
    • Compression
    • Red Flags: No key usage, no views, no compression

System Design Skills

  1. Analytics Architecture
    • ClickHouse cluster design
    • Distributed queries
    • Materialized views
    • Red Flags: No architecture, poor distribution, no views
  2. Scalability
    • Horizontal scaling
    • Sharding strategy
    • Performance tuning
    • Red Flags: No scaling, poor sharding, no tuning

Communication Skills

  1. Clear Explanation
    • Explains ClickHouse concepts
    • Discusses trade-offs
    • Justifies design decisions
    • Red Flags: Unclear explanations, no justification, confusing

Meta-Specific Focus

  1. Analytics Expertise
    • Understanding of OLAP systems
    • ClickHouse mastery
    • Performance optimization
    • Key: Demonstrate analytics expertise
  2. System Design Skills
    • Can design analytics systems
    • Understands OLAP challenges
    • Makes informed trade-offs
    • Key: Show practical analytics design skills

Summary

ClickHouse Key Points:

  • Column-Oriented: Optimized for analytical queries
  • High Performance: Sub-second queries on large datasets
  • Compression: Efficient data storage
  • Scalability: Horizontal scaling with clustering
  • Materialized Views: Pre-computed aggregations

Common Use Cases:

  • Analytics dashboards
  • Business intelligence
  • Time-series analytics
  • Data warehousing
  • Real-time analytics
  • Log analysis

Best Practices:

  • Design primary key for queries
  • Partition by time or key
  • Use materialized views for aggregations
  • Optimize compression
  • Use distributed tables for scaling
  • Monitor query performance
  • Plan for data growth

ClickHouse is a powerful analytics database optimized for fast OLAP queries on large datasets.