Skip to main content
← All Tools · 🛠️ Data Engineering & Pipeline

ClickHouse — Fast Open-Source Columnar Database for Analytics

ClickHouse is a column-oriented OLAP database for blazing-fast analytics on billions of rows — real-time ingestion with efficient compression.

Free Open source; ClickHouse Cloud from $0.0222/hour per replica; Altinity Cloud also available
databaseanalyticsolapcolumnarsqlreal-timedata-engineeringtime-series

✅ Pros

  • + 100-1000x faster than row-based databases for analytics queries
  • + Excellent compression — column storage compresses repetitive values efficiently
  • + Real-time ingestion: insert millions of rows/second without slowing queries
  • + Familiar SQL with powerful array functions and aggregation combinators
  • + MergeTree engine family handles time-series, deduplication, materialized views

⚠️ Cons

  • − Not designed for transactional workloads (OLTP) — no real UPDATE/DELETE
  • − Joins are limited — denormalized tables and arrays preferred
  • − Query planning differs from PostgreSQL — requires learning ClickHouse-specific optimizations
  • − Self-hosted clustering requires operational expertise

Overview

ClickHouse is the fastest open-source analytical database for querying large volumes of data. Built by Yandex for web analytics, it’s now the backbone of analytics platforms at Cloudflare, Uber, Stripe, and thousands of other companies. If PostgreSQL is too slow for your analytics queries, ClickHouse is the answer.

Table Design

-- MergeTree: the base engine for most use cases
CREATE TABLE orders (
    order_id    UUID,
    customer_id String,
    created_at  DateTime,
    status      LowCardinality(String),  -- Optimized for low-cardinality columns
    amount      Decimal(10, 2),
    category    LowCardinality(String),
    items       Array(String)
)
ENGINE = MergeTree()
ORDER BY (created_at, customer_id)      -- Primary sort key (for range queries)
PARTITION BY toYYYYMM(created_at);      -- Partition by month

-- ReplacingMergeTree: deduplicate on primary key
CREATE TABLE user_events (
    user_id     String,
    event_type  String,
    occurred_at DateTime,
    value       Float64,
    version     UInt64  -- Higher version wins on dedup
)
ENGINE = ReplacingMergeTree(version)
ORDER BY (user_id, event_type, occurred_at);

Query Performance

-- Count active users by day — scans billions of rows in seconds
SELECT
    toDate(created_at) AS day,
    uniqExact(customer_id) AS unique_customers,
    sum(amount) AS revenue,
    count() AS order_count,
    avg(amount) AS avg_order_value
FROM orders
WHERE created_at >= '2026-01-01'
  AND status = 'completed'
GROUP BY day
ORDER BY day;

-- Funnel analysis with ClickHouse array functions
SELECT
    countIf(has(steps, 'view')) AS views,
    countIf(has(steps, 'add_to_cart')) AS add_to_cart,
    countIf(has(steps, 'purchase')) AS purchases
FROM (
    SELECT
        session_id,
        groupArray(event_type) AS steps
    FROM events
    WHERE date = today()
    GROUP BY session_id
);

Materialized Views

-- Pre-aggregate revenue at insert time
CREATE MATERIALIZED VIEW daily_revenue_mv
ENGINE = SummingMergeTree()
ORDER BY (day, category)
AS SELECT
    toDate(created_at) AS day,
    category,
    sum(amount) AS revenue,
    count() AS order_count
FROM orders
GROUP BY day, category;

-- Querying the materialized view is instant
SELECT day, category, sum(revenue) AS revenue
FROM daily_revenue_mv
WHERE day >= today() - 30
GROUP BY day, category
ORDER BY day, revenue DESC;

Ingesting Data

from clickhouse_driver import Client

client = Client('localhost')

# Bulk insert (most efficient)
client.execute(
    'INSERT INTO orders (order_id, customer_id, created_at, status, amount) VALUES',
    [
        {'order_id': uuid4(), 'customer_id': 'cust-1', 'created_at': datetime.now(),
         'status': 'completed', 'amount': 99.99},
        # ... thousands more
    ]
)

ClickHouse vs PostgreSQL for Analytics

QueryPostgreSQLClickHouse
Count 1B rows~30s~0.1s
Group by on 100M rows~20s~0.5s
Time-series aggregation~10s~0.2s
Storage (1B rows)~500GB~50GB

ClickHouse isn’t a PostgreSQL replacement — use PostgreSQL for transactional data and ClickHouse for analytics. Many architectures sync PostgreSQL → ClickHouse via Kafka or ClickHouse’s PostgreSQL integration.

Best For

  • Real-time analytics dashboards where queries on billions of rows must return in under a second — ClickHouse’s columnar storage and vectorized execution are purpose-built for this
  • Time-series data — metrics, events, logs, and IoT data with time-based aggregations (hourly, daily, monthly rollups)
  • Log analytics replacing Elasticsearch for cost-sensitive teams — ClickHouse can store 10x more data in the same storage space and query it faster
  • Product analytics computing user funnels, retention cohorts, and session analytics across hundreds of millions of events
  • Ad-tech and gaming with extremely high-volume event ingestion (millions of inserts/second) and fast aggregation queries for reporting

When NOT to Use ClickHouse

  • Transactional workloads (frequent updates, deletes, row-level inserts) — use PostgreSQL; ClickHouse’s MergeTree engine is optimized for append-heavy patterns
  • Complex JOINs across many tables — ClickHouse JOIN performance degrades for wide joins; denormalize data or use a different OLAP database
  • Very small datasets — the operational complexity isn’t worth it under 10M rows; PostgreSQL or DuckDB serve these cases better