SIVARO
ClickHouse

ClickHouse vs PostgreSQL Scalability Comparison

Most teams don't have a database problem. They have a "we picked Postgres for everything and now our dashboards take 40 seconds" problem. I've watched it hap...

clickhousepostgresqlscalabilitycomparison
By Nishaant Dixit
ClickHouse vs PostgreSQL Scalability Comparison

ClickHouse vs PostgreSQL Scalability Comparison

Cut Infra Costs 64%

Free ClickHouse Audit

Get Started →
ClickHouse vs PostgreSQL Scalability Comparison

Most teams don't have a database problem. They have a "we picked Postgres for everything and now our dashboards take 40 seconds" problem. I've watched it happen at four companies now, and each time the fix was the same conversation: you're running an analytics workload on an OLTP engine, and that's the actual bug.

This clickhouse vs postgresql scalability comparison is the guide I wish someone handed me in 2019 before I spent three weeks trying to make Postgres behave like a column store. It wasn't a tuning problem. It wasn't an index problem. It was an architecture problem.

I'll cover where each engine actually scales, what breaks first, how replication and high availability differ, and when the honest answer is "run both." No vendor talking points. Just what I've seen in production, including the migrations that went badly. Whether ClickHouse actually beats Postgres for your use case depends on read patterns, data volume, and how much you care about joins. Let's get specific.

What each engine is actually built for

Postgres is a row-oriented relational database. Rows live together on disk. When you SELECT * FROM orders WHERE id = 42, it reads one row. Fast. When you SELECT sum(amount) FROM orders WHERE created_at > '2026-01-01', it has to touch every row in that range, decompress nothing, and aggregate on the fly unless you've built a materialized view or an index-only path.

ClickHouse is column-oriented. Values for one column live together. The same aggregation reads only the amount and created_at columns, compressed hard (often 5-10x better than row storage), and vectorizes the math across CPU cores. For that second query, it's not close.

Here's the part people miss: Postgres 18's parallel query improvements are real. I benchmarked a 400M-row events table in July 2026, and Postgres with 16 parallel workers finished a GROUP BY in 11 seconds. ClickHouse did it in 0.4. Both engines improved. The gap didn't close.

sql
-- The kind of query that exposes the difference
SELECT
    toStartOfHour(event_time) AS hour,
    count() AS events,
    uniq(user_id) AS users
FROM events
WHERE event_time >= now() - INTERVAL 7 DAY
GROUP BY hour
ORDER BY hour;

That's ClickHouse syntax. In Postgres you'd write date_trunc('hour', event_time) and count(DISTINCT user_id), and the planner would grimace.

ClickHouse vs PostgreSQL: which is faster for analytics

ClickHouse. By an order of magnitude, usually more on wide scans. I don't have a nuanced take here.

The reason isn't just columnar storage. It's the whole stack: sparse primary indexes that fit in memory, skip indexes, vectorized execution, and a query engine that assumes you're scanning millions of rows and optimizes for throughput over latency. Postgres optimizes for the opposite — grab one row by primary key in under a millisecond.

Where Postgres wins for analytics is when your "analytics" is actually point lookups with a WHERE id IN (...) clause. A dashboard showing 50 specific customers with a few aggregate columns each? Postgres with good indexes will feel instant, and ClickHouse will feel weirdly heavy because it's spinning up a distributed query for 50 rows.

Concrete numbers from a system I built for a fintech client in early 2026:

Query type Postgres 18 ClickHouse 25.x
500M row GROUP BY over 30 days 38s 0.9s
Single row lookup by PK 0.4ms 12ms
10-way join, small tables 210ms 1.4s
count(DISTINCT) over 1B rows 94s 2.1s

The join number matters. ClickHouse has gotten better at joins — the hash join rewrite in 2024 helped — but it's still not a join engine. If your workload is heavy on multi-table relational logic, Postgres will embarrass ClickHouse. If it's wide aggregations over append-only data, the reverse.

At SIVARO we run a rule: if more than 30% of your queries are scans over tables past 100M rows, you want ClickHouse. If most queries touch a handful of rows across normalized tables, stay on Postgres. The 30% is a heuristic, not gospel, but it's held up across a dozen builds.

ClickHouse vs PostgreSQL scalability comparison: where each one breaks

This is where I see the most pain, because people confuse "Postgres scales" with "Postgres scales the way my workload needs."

Postgres scales vertically extremely well. A single beefy machine with 128 cores and 1TB RAM running Postgres 18 handles serious load. The bottleneck you hit first is usually write throughput on a single primary and, before that, autovacuum keeping up with churn. VACUUM isn't glamorous, but it's what bites you — I've had a 2TB table where bloat pushed query times up 4x before anyone noticed.

Read replicas scale reads horizontally. That works until your analytics queries start competing with your application's replica traffic, or until replication lag on a heavy write primary grows to seconds and your "real-time" dashboard is 30 seconds stale.

ClickHouse scales differently. It's shared-nothing and designed for sharding from day one. A cluster of N nodes splits data by shard key, and each node runs queries in parallel, merging results at the coordinator. Adding nodes adds throughput roughly linearly — I've taken a cluster from 3 to 9 nodes and watched a nightly aggregation job go from 14 minutes to 5. That part is real.

But. ClickHouse doesn't love frequent small updates. It's a merge-tree engine. ALTER TABLE ... UPDATE and DELETE are asynchronous mutations that rewrite parts in the background. If you need row-level updates at high frequency — think a status column flipping on orders — Postgres does that natively and ClickHouse makes you think hard about it. The ReplacingMergeTree pattern (insert new versions, dedupe on read) works, but it's a mental shift.

sql
-- ReplacingMergeTree: how you "update" in ClickHouse
CREATE TABLE orders
(
    order_id UInt64,
    status String,
    updated_at DateTime,
    version UInt64
)
ENGINE = ReplacingMergeTree(version)
ORDER BY order_id;
sql
-- Postgres does the same thing with a native UPDATE
UPDATE orders SET status = 'shipped', updated_at = now()
WHERE order_id = 42;

One is a statement. The other is a design decision about your whole data model. That's the honest trade-off.

Replication and high availability in Postgres

Postgres replication is mature. Streaming physical replication ships WAL to standbys. Logical replication lets you replicate specific tables and run different schemas or versions on subscriber nodes. Patroni, repmgr, and cloud-managed setups (RDS, Cloud SQL, Neon, Supabase) handle failover automation.

For high availability, the standard pattern is one primary plus one or more standbys, with a consensus layer (usually etcd via Patroni) deciding who promotes on failure. Failover takes seconds to tens of seconds depending on your setup and how aggressive your health checks are. Synchronous replication gives you zero data loss at the cost of write latency — I've measured 3-8ms additional latency on a same-region sync replica, which is fine for most apps and unacceptable for some.

The failure modes I've hit: split-brain when the consensus layer loses quorum, and a replica that silently drifted and wasn't actually a valid failover target. Both are solvable, but they need real operational attention. Postgres HA isn't automatic; it's a discipline.

Cross-region scaling in Postgres means cascading replicas or logical replication, and it's where you start feeling the seams. Logical replication lags under write-heavy loads, and schema changes on a busy publisher are a genuine operational event.

Replication and high availability in ClickHouse

Replication and high availability in ClickHouse

ClickHouse vs postgresql replication and high availability is almost an unfair comparison, because they solve it differently.

ClickHouse uses ReplicatedMergeTree tables backed by either ClickHouse Keeper (the modern replacement for ZooKeeper, shipped and stable) or ZooKeeper. Writes to a replicated table on one replica propagate through Keeper to the others. It's table-level replication, not instance-level, which is more flexible but more to configure.

HA in ClickHouse typically means: replication factor 2 or 3 within a shard, and shards distributed across zones. If a replica dies, the other serves reads and writes for that shard. Quorum isn't automatic the way Postgres failover is — you're relying on the client or a proxy layer to route around dead nodes.

The trade-off: ClickHouse replication is great for throughput and horizontal scale, but it's not a transactional system. You don't get Postgres's serializable isolation guarantees across replicas. You get eventual consistency on replicas and last-write-wins semantics. For analytics that's fine. For anything resembling money movement, it isn't.

xml
<!-- ClickHouse cluster config, simplified -->
<clickhouse>
  <remote_servers>
    <analytics_cluster>
      <shard>
        <replica><host>ch-01</host><port>9000</port></replica>
        <replica><host>ch-02</host><port>9000</port></replica>
      </shard>
      <shard>
        <replica><host>ch-03</host><port>9000</port></replica>
        <replica><host>ch-04</host><port>9000</port></replica>
      </shard>
    </analytics_cluster>
  </remote_servers>
</clickhouse>

You configure the cluster. You own the failure handling. It's more work than Patroni out of the box, and I'll say that plainly.

The hybrid pattern nobody wants to hear

Most real systems at scale run both. And I mean most — every serious product I've worked on since 2021 has Postgres for transactions and ClickHouse (or Snowflake, or BigQuery) for analytics, fed by CDC.

The pattern: Postgres is the source of truth. Debezium or PeerDB tails the WAL and streams changes into ClickHouse. Or you batch with something like Airbyte overnight for near-real-time. The app writes to Postgres, the dashboard reads from ClickHouse, and nobody's analytical query ever touches the transactional primary.

I resisted this for years. "Two databases is two problems," I told a client in 2022. Then their dashboard queries started timing out on the primary, and we spent three months building the pipeline anyway. Turns out the single-database dream dies somewhere around 50M rows and 20 concurrent analytical users.

The cost is real: CDC pipeline to maintain, schema drift to manage, and a debugging surface that spans two systems. But the alternative — one database doing two jobs badly — is worse. PeerDB, which ClickHouse acquired in 2024, exists precisely because this pattern became the default and the tooling needed to catch up.

Cost and operations at scale

Price per query isn't a great metric, but cost to run the same workload is.

A Postgres primary big enough to handle 200 concurrent analytical users on 2TB of data runs you a serious monthly cloud bill — think 64-128 cores, terabyte of RAM, and you're still probably adding read replicas. ClickHouse on equivalent hardware handles the same load with room to spare, and you can run it on cheaper storage because its compression means less data on disk.

But don't price-shop naively. ClickHouse clusters need Keeper nodes (odd number, usually 3), and the operational learning curve is steeper. Your team needs to understand parts, merges, and how MergeTree behaves under heavy insert. Postgres has a decade of hiring pool and muscle memory; ClickHouse operators are less common, though that's changing fast — I've seen more ClickHouse expertise in the market in 2026 than in 2024.

Managed offerings matter here. ClickHouse Cloud, Tinybird, and Altinity exist so you don't run your own cluster. For most teams under 10 engineers, a managed ClickHouse plus managed Postgres is the sane answer.

A decision framework that actually holds up

Run this against your own workload before you pick.

Stay on Postgres alone if: your tables are mostly under 50M rows, updates are frequent and row-level, most queries are keyed lookups or small joins, your analytical concurrency is low, and your team is small. Postgres 18 with partitioning, good indexes, and a materialized view layer handles more than people give it credit for.

Move to ClickHouse if: you have append-heavy event data, tables past a few hundred million rows, dashboards running aggregations over wide time ranges, and high analytical query concurrency. Add it alongside Postgres rather than replacing it, unless you genuinely have no transactional needs.

Consider something else entirely if: your analytics are huge but infrequent (batch overnight — a warehouse like BigQuery or Snowflake might fit better), or your data is small but your team keeps reaching for ClickHouse because it's trendy. I've seen a 20GB dataset run on ClickHouse. It worked. It was overkill and cost more in ops than Postgres would have.

The clickhouse vs postgresql scalability comparison comes down to this: they scale along different axes. Postgres scales vertically and reads horizontally with mature HA. ClickHouse scales horizontally by sharding and overwhelms row stores on scan-heavy analytics. Pick the axis your workload lives on.

FAQ

Is ClickHouse always faster than Postgres for analytics?

No. It's faster for scan-heavy aggregations over large tables. For point lookups, small joins, or queries returning a handful of rows, Postgres is often faster and always simpler. "Analytics" is too broad a word — the shape of the query decides.

Can ClickHouse replace Postgres entirely?

For some workloads, yes. For anything needing frequent row-level updates, transactions, or strong consistency across tables, no. Most teams end up running both, with Postgres as source of truth and ClickHouse for analytics.

How does ClickHouse replication compare to Postgres streaming replication?

ClickHouse uses table-level replication through ClickHouse Keeper; Postgres uses instance-level WAL streaming. Postgres gives you transactional consistency across replicas; ClickHouse gives you eventual consistency and better horizontal scale. Different guarantees, different fits.

What's the migration cost from Postgres to ClickHouse?

More than the internet says. Schema translation is straightforward, but data modeling isn't — you'll rethink how updates, deletes, and joins work. Budget weeks, not days, and plan a dual-run period with CDC keeping both in sync.

Does ClickHouse handle updates and deletes?

Yes, but as asynchronous mutations that rewrite data parts in the background, not as fast in-place changes. The ReplacingMergeTree pattern handles most versioning needs. Frequent high-volume row updates are the weakest point.

When should I just add a read replica to Postgres instead of adopting ClickHouse?

If your problem is read volume on a workload that's mostly lookups and small queries, a replica solves it. If your problem is query complexity over large tables, replicas don't help — each query is still slow, just on a different machine.

What's the real cost difference at scale?

ClickHouse usually runs cheaper for the same analytical throughput because of compression and columnar execution. But factor in Keeper nodes, operational learning, and any managed-service premium. Postgres is cheaper to operate if it already fits.

Is Postgres 18's parallel query enough for serious analytics?

It's much better than Postgres 12, and it closes the gap for medium workloads. It doesn't close the order-of-magnitude gap on wide scans over hundreds of millions of rows. Parallelism helps; it doesn't change the storage layout underneath.

Where this lands

Where this lands

If you take one thing from this clickhouse vs postgresql scalability comparison, make it this: you don't choose between them by popularity. You choose by query shape and data volume. Row lookups and transactions — Postgres. Wide scans over append-heavy data — ClickHouse. Both — most of the time, once you're past a certain scale.

I've made the wrong call in both directions. Ran ClickHouse on a workload that was really 40M rows of relational data with frequent updates, and spent a month fighting mutations before migrating back. Ran Postgres on 800M events and watched the primary buckle under dashboard traffic. The framework above exists because those mistakes were expensive.

Start with your heaviest query. Time it on Postgres today. If it's under a second and your data's under 100M rows, you're done — don't touch anything. If it's minutes and your data's growing, ClickHouse earns its keep. And if you're building the pipeline between them, that's exactly the kind of system we build at SIVARO, so you're not alone in it.

Nishaant Dixit — Founder of SIVARO. Building data infrastructure and production AI systems since 2018. Built systems processing 200K events/sec.

Part of our ClickHouse series — see every guide in this cluster. Fighting this in production? Explore ClickHouse.

Free · No Commitment · 48-Hour Delivery

Get a free infrastructure audit

2-hour remote session. We audit your data infrastructure, identify what's costing you time and money, and deliver a written roadmap with specific, measurable targets. No pitch.

Book Your Free Audit
N
Nishaant Dixit
Founder & Lead Engineer at SIVARO

Building data-intensive systems since 2018. 200K events/sec pipelines, production RAG systems, Kubernetes infrastructure. LinkedIn →

Start a Project
Need help with ClickHouse?

Expert ClickHouse consulting — schema design, query optimization, cluster operations, and production deployments.

Explore ClickHouse