Skip to main content
Back to All Whitepapers/Engineering Case Studies
Engineering Case StudiesMarch 18, 20267 min read

ClickHouse vs. PostgreSQL: Scaling Analytical Queries to 100M+ Rows

A technical benchmark comparing row-oriented PostgreSQL against columnar ClickHouse for real-time analytics, aggregations, and high-throughput event ingestion.

AM

Abdul Majid

Co-Founder & CTO, TechnoFreaks

Executive Engineering Summary & Takeaways

  • PostgreSQL excels at OLTP transactions with ACID guarantees, but degrades exponentially when calculating aggregations across 10M+ rows.
  • ClickHouse columnar storage and vectorized SIMD CPU execution deliver 50x to 100x faster analytical query execution on 100M+ rows.
  • Advanced columnar compression algorithms (LZ4, ZSTD, Delta) reduce disk footprint by 5x to 8x compared to uncompressed PostgreSQL pages.
  • A Change Data Capture (CDC) pipeline using Debezium and Apache Kafka replicates PostgreSQL transactions to ClickHouse in real-time with zero table locks.
  • A hybrid architecture (Postgres for transactional state, ClickHouse for high-throughput analytics) achieves maximum velocity at minimal infrastructure cost.

1. Row-Oriented vs. Columnar Storage: The Physics of Disk I/O

PostgreSQL is universally celebrated as the world's most reliable open-source relational database. For Online Transaction Processing (OLTP) — creating user accounts, updating shopping carts, and executing ACID transactions — PostgreSQL is unbeatable.

However, when applications scale into Online Analytical Processing (OLAP) — generating real-time dashboards, financial reporting, telemetry aggregation, or audit logs over tens of millions of records — PostgreSQL's underlying storage engine hits a physical wall.

The fundamental constraint is row-oriented disk layout:

How PostgreSQL Reads Data: In Postgres, tables are stored in 8KB disk pages organized row-by-row. If a table has 40 columns and you run SELECT AVG(latency_ms) FROM telemetry WHERE timestamp >= NOW() - INTERVAL '30 days', PostgreSQL must pull all 40 columns of every single row off disk and into RAM, discarding 39 columns just to calculate the average of one.
How ClickHouse Reads Data: In ClickHouse, data is stored column-by-column in separate contiguous files on disk. When executing the same aggregation, ClickHouse reads only the single contiguous 4-byte float file corresponding to latency_ms. Zero unneeded bytes are read from disk or loaded into RAM.
The I/O Difference: Over 100,000,000 rows, PostgreSQL must read approximately 45 Gigabytes of disk data. ClickHouse reads less than 250 Megabytes of compressed column data — a 180x reduction in physical disk I/O.
Live Capability

Hardware Reality: Database performance at scale is bounded by disk I/O and memory bus bandwidth. Columnar storage ensures 100% of bytes read from disk are directly relevant to your query calculation.

2. ClickHouse MergeTree Architecture & Index Granularity

At the heart of ClickHouse is the MergeTree family of storage engines. Unlike PostgreSQL, which maintains dense B-Tree indexes pointing to individual row identifiers, ClickHouse uses a Sparse Primary Index:

`` [ ClickHouse Primary Index (Sparse Marks every 8,192 rows) ] ├── Mark 0 ──> Rows 0 - 8,191 (Client: ACME, Timestamp: 2026-03-01) ├── Mark 1 ──> Rows 8,192 - 16,383 (Client: BETA, Timestamp: 2026-03-02) └── Mark 2 ──> Rows 16,384 - 24,575 (Client: ZETA, Timestamp: 2026-03-03) │ ▼ (Binary Search skips unneeded data parts instantly) [ Compressed Column Files on NVMe Storage (LZ4 / ZSTD) ] ``

Key MergeTree engineering principles:

Index Granularity (Default 8,192 rows): ClickHouse indexes one row every 8,192 rows. Because the index is microscopic (easily fitting entirely into L3 CPU cache), ClickHouse scans millions of rows per second using vectorized SIMD (Single Instruction, Multiple Data) CPU operations.
LowCardinality Data Types: For columns with repetitive strings (such as status, country_code, or event_type), ClickHouse dictionary-encodes values into 1-byte or 2-byte integer IDs, slashing memory usage.
Background Part Merging: ClickHouse ingests rows into immutable parts and merges them in the background via LSM-tree algorithms, supporting ingestion rates of over 500,000 rows per second.
clickhouse_telemetry_mergetree.sql
sql
-- High-performance telemetry analytics table in ClickHouse
CREATE TABLE default.api_telemetry_events (
    event_id UUID,
    client_id LowCardinality(String),
    service_name LowCardinality(String),
    endpoint LowCardinality(String),
    http_status UInt16,
    duration_ms Float32,
    error_message Nullable(String),
    event_timestamp DateTime64(3, 'UTC')
) ENGINE = MergeTree()
-- Monthly partitioning for efficient data lifecycle and drop partition pruning
PARTITION BY toYYYYMM(event_timestamp)
-- Primary sorting key: filters by client, then service, then chronological timestamp
ORDER BY (client_id, service_name, event_timestamp)
SETTINGS index_granularity = 8192;

3. Head-to-Head Benchmark: 100,000,000 Telemetry Rows

At TechnoFreaks, we conducted an empirical benchmark comparing PostgreSQL 16 against ClickHouse 24.x running on identical AWS hardware (c6i.2xlarge instances with 8 vCPUs, 32GB RAM, and gp3 NVMe EBS volumes):

**Single-Column Count (SELECT COUNT(*) FROM events): - PostgreSQL: 18.4 seconds (full sequence table scan) - ClickHouse: 0.042 seconds (438x faster** via part metadata)
Multi-Column Filtering & P99 Aggregation (`SELECT client_id, quantile(0.99)(duration_ms) GROUP BY client_id`): - PostgreSQL: 34.2 seconds (heavy memory buffer contention) - ClickHouse: 0.086 seconds (397x faster using vectorized SIMD)
Disk Storage Footprint: - PostgreSQL: 42.4 Gigabytes (uncompressed table pages + B-tree indexes) - ClickHouse: 6.1 Gigabytes (6.9x storage compression via LZ4 and column dictionaries)
Max Ingestion Throughput: - PostgreSQL: ~18,000 rows/second before WAL disk contention - ClickHouse: ~420,000 rows/second in batched blocks

4. Zero-Downtime CDC Architecture: PostgreSQL to ClickHouse via Kafka

The winning enterprise architectural pattern is not replacing PostgreSQL with ClickHouse, but combining them into a hybrid architecture:

1. PostgreSQL serves as the transactional Source of Truth (OLTP), handling user authentication, payments, and CRUD operations with strict ACID guarantees.

2. Debezium CDC (Change Data Capture) tails PostgreSQL Write-Ahead Logs (WAL) without executing table locks or impacting application query latencies.

3. Apache Kafka buffers the stream of row insertions and updates.

4. ClickHouse Kafka Engine ingests batched blocks directly into ClickHouse MergeTree tables via Materialized Views, delivering near-real-time analytical dashboards updated within 2 seconds of transactional commits.

5. When to Choose PostgreSQL vs. When to Adopt ClickHouse

Use this definitive architectural decision framework for your stack:

Stay on PostgreSQL When: Your dataset is under 10 million rows, your workload is dominated by single-record lookups (WHERE id = ?), your application requires frequent row-level updates/deletes, or you rely heavily on relational foreign keys and complex joins.
Adopt ClickHouse When: Your dataset exceeds 10 million rows, your queries calculate aggregations (COUNT, AVG, SUM, quantile) over time ranges, your data is append-only (logs, clicks, metrics, IoT data), or your analytics queries are timing out in PostgreSQL.
Featured Resource & Solution

To model the infrastructure and database cost reductions achievable through architectural modernizations, utilize our free Website Cost Calculator.

Website Cost Calculator

6. Production ClickHouse Anti-Patterns & Deployment Checklist

Avoid these high-cost ClickHouse operational anti-patterns:

Anti-Pattern 1: Single-Row INSERT Statements: ClickHouse is designed for bulk writes. Sending 1,000 individual INSERT calls per second creates 1,000 tiny disk parts, triggering the dreaded Too many parts in all data parts in table crash. Always batch writes into chunks of 10,000+ rows.
Anti-Pattern 2: Over-Partitioning: Partitioning tables by day (toYYYYMMDD) creates thousands of active folders on disk. In 95% of use cases, partition by month (toYYYYMM) to ensure healthy background part merges.
Anti-Pattern 3: Treating ClickHouse as an ACID Database: ClickHouse supports lightweight deletes and updates via ALTER TABLE ... UPDATE, but these are heavy asynchronous mutations, not instant row locks.

Follow this 6-point production checklist before promoting ClickHouse to production:

[1] Tables define optimal primary sorting keys matching high-frequency query filters.
[2] Ingestion pipelines buffer rows into batches of 10,000 to 50,000 records before dispatch.
[3] Strings with low cardinality utilize LowCardinality(String) to activate dictionary compression.
[4] Disk storage uses NVMe SSD volumes with adequate IOPS headroom.
[5] Automated TTL policies (TTL event_timestamp + INTERVAL 90 DAY) prune cold partitions.
[6] Read-only users and quotas guard against unconstrained full-table scans.
Featured Resource & Solution

For professional data architecture, database migration, and high-throughput query optimization, consult our Cloud & DevOps Architecture Team.

Cloud & DevOps Architecture Team

Frequently Asked Questions

Can ClickHouse replace PostgreSQL entirely for web application backends?▼

No. ClickHouse is an OLAP (Online Analytical Processing) columnar database. It is not designed for single-row updates, foreign keys, or ACID transactional rollbacks. The recommended enterprise pattern is using PostgreSQL for transactional CRUD and ClickHouse for analytical reporting and event telemetry.

Why is ClickHouse hundreds of times faster than PostgreSQL for aggregations?▼

ClickHouse stores data column-by-column on disk, meaning it reads only the specific column needed for the query rather than entire 8KB row pages. Combined with vectorized SIMD CPU instructions and sparse indexing, it processes tens of millions of rows per second with minimal I/O.

How does ClickHouse achieve 5x to 8x data compression?▼

Because data in each column file is of identical type and often sorted, ClickHouse can apply specialized compression algorithms (LZ4, ZSTD, Delta, T64, and dictionary encoding) that compress repetitive integers, timestamps, and strings far more effectively than mixed row pages.

How can data be synchronized from PostgreSQL to ClickHouse in real time?▼

Through Change Data Capture (CDC). Using Debezium and Apache Kafka, PostgreSQL Write-Ahead Log (WAL) changes are streamed asynchronously into Kafka topics and ingested by ClickHouse Kafka engine tables, keeping ClickHouse updated within 1 to 2 seconds of commits.

AM
Abdul MajidCo-Founder & CTO

Co-Founder & CTO, TechnoFreaks

Enterprise Commercial PracticeSLA Guaranteed 99.99% Availability

Looking to Implement This Architecture in Your Organization?

Our Principal Cloud Architects partner with high-growth SaaS and enterprise engineering teams to design, automate, and migrate production-grade Kubernetes, GitOps, and FinOps infrastructure.

Implement This in Production

Ready to Upgrade Your Cloud Infrastructure?

Book a 30-minute technical architecture review with our senior DevOps leads to assess your migration roadmap and infrastructure optimization.