I migrated a 2TB event analytics workload from TimescaleDB to ClickHouse last year. Query times dropped from 8-12 seconds to 200-400 milliseconds. The team celebrated for about a day, then spent the next three weeks dealing with everything ClickHouse makes harder: schema migrations, deduplication, integrating with an application that still needed Postgres for transactional work, and explaining to the on-call rotation why a new system with its own failure modes now sat in the critical path.
That trade-off -- raw speed versus operational simplicity -- is the entire ClickHouse vs TimescaleDB decision in miniature. Neither system is better. They solve different problems, and the right choice depends on which problems you actually have.
The fundamental architectural split
ClickHouse and TimescaleDB are not two implementations of the same idea. They start from completely different premises about what a database should be.
ClickHouse is a columnar OLAP database. Originally built at Yandex for web analytics (counting page views across billions of rows), it stores data in columns rather than rows. When you run SELECT avg(response_time) FROM events WHERE timestamp > now() - interval 1 hour, ClickHouse only reads the response_time and timestamp columns from disk. Everything else -- user_id, event_type, metadata -- stays untouched. This is why it is fast. It is also why point lookups (SELECT * FROM events WHERE id = 'abc123') are comparatively slow. The storage format is optimized for scanning large ranges of a few columns, not fetching all columns for a single row.
TimescaleDB is PostgreSQL with automatic time-based partitioning. It is literally a Postgres extension. Your application connects to it with a standard Postgres driver. It speaks full SQL -- window functions, CTEs, JOINs, subqueries, foreign keys, everything. Under the hood, it splits your time-series tables into "chunks" (partitions by time range) and adds query planner optimizations that make time-range queries fast. But it is still fundamentally row-oriented storage (with optional columnar compression on older chunks).
This means ClickHouse is a separate analytical system that lives alongside your application database. TimescaleDB is your application database, with analytics built in. That distinction matters far more than benchmark numbers.
Storage and compression
ClickHouse's compression is genuinely impressive. Columnar storage means similar values sit next to each other on disk (all your timestamps together, all your status codes together), which compresses beautifully. With LZ4 (default) or ZSTD (better ratio, slightly more CPU), I routinely see 10-15x compression ratios on event data. A 10TB logical dataset might occupy 700GB on disk.
The MergeTree engine family handles this automatically. You define a table with an ORDER BY clause that determines the physical sort order (critical for query performance), and ClickHouse manages the rest -- merging data parts in the background, deduplicating if you use ReplacingMergeTree, summing if you use SummingMergeTree. The engine variants are powerful but add conceptual overhead. You need to understand eventual consistency semantics: a row you just inserted might appear twice until background merges run.
TimescaleDB's compression story has improved significantly. Since Timescale 2.0, you can enable columnar compression on older chunks. A hypertable might keep the last 24 hours in standard row format (fast for inserts and recent queries) and compress everything older into columnar segments. Compression ratios are typically 5-10x -- worse than ClickHouse because Postgres was not designed from the ground up for this, but still meaningful for storage costs.
The operational difference: ClickHouse compression is always on, always columnar. TimescaleDB compression is a policy you configure -- recent data stays row-oriented for fast writes, older data gets compressed for storage efficiency. This two-tier approach is actually clever for mixed workloads where you need both fast recent inserts and efficient historical storage.
Query performance
Here is where ClickHouse pulls away decisively. For pure analytical queries -- aggregations, GROUP BY, filtering over time ranges, counting distinct values -- ClickHouse is typically 10-100x faster than TimescaleDB at scale.
A query like this on a table with 5 billion rows:
SELECT
toStartOfHour(timestamp) AS hour,
country,
count() AS events,
uniqExact(user_id) AS unique_users
FROM events
WHERE timestamp >= now() - INTERVAL 7 DAY
GROUP BY hour, country
ORDER BY hour DESCClickHouse returns this in 1-3 seconds. The equivalent on TimescaleDB with the same hardware and data volume takes 30-90 seconds. The columnar storage format, vectorized query execution, and aggressive use of SIMD instructions give ClickHouse a structural advantage that TimescaleDB cannot close without fundamentally changing what it is.
But. TimescaleDB has continuous aggregates -- pre-computed materializations that refresh incrementally. If you know your query patterns in advance (and for dashboards, you usually do), you can define a continuous aggregate that pre-computes those hourly country-level metrics. Querying the aggregate is fast regardless of the underlying data volume. You trade flexibility for speed: ad-hoc queries over raw data are slow, but known queries over materialized views are fast.
ClickHouse has materialized views too, but they work differently -- they trigger on insert and write to a separate target table. Both approaches require you to anticipate your query patterns. The difference is what happens when someone asks an unexpected question: ClickHouse can still answer it fast from raw data. TimescaleDB cannot.
Ingestion
ClickHouse handles millions of rows per second on a single node. The recommended pattern is batch inserts (1000+ rows per INSERT statement), and it will happily ingest 1-2 million rows/sec sustained. It is designed for high-throughput append workloads. The trade-off: individual row inserts are inefficient, and you should batch on the client side or use a buffer table.
TimescaleDB ingests at typical Postgres speeds -- tens of thousands of rows per second on standard hardware, potentially hundreds of thousands with careful tuning (COPY command, large batch sizes, parallel workers). Fast enough for most applications, but if you are ingesting clickstream data at internet scale, you will hit limits before ClickHouse would.
For context: if your event volume is under 50,000 events per second, TimescaleDB handles it fine. Above that, ClickHouse's ingestion architecture starts to matter.
SQL compatibility
TimescaleDB wins here completely because it is Postgres. Every ORM, every driver, every tool that speaks Postgres works with TimescaleDB. Full SQL standard support. JOINs work exactly as you expect. You can have foreign keys, transactions, UPSERT, UPDATE, DELETE -- the full relational model.
ClickHouse has its own SQL dialect. It is close to standard SQL but has quirks. JOINs work but are historically weaker (improvements in recent versions have narrowed the gap significantly). No true UPDATE or DELETE -- you have ALTER TABLE ... UPDATE and ALTER TABLE ... DELETE which are async mutations that rewrite data parts in the background. No foreign keys. No multi-statement transactions. If you need to update a row, ClickHouse makes you fight for it.
This matters most when your analytics queries need to JOIN against operational data. In TimescaleDB, your events table and your users table live in the same database -- JOIN them freely. In ClickHouse, your users table is probably in a separate Postgres instance. You either duplicate user data into ClickHouse (via a dictionary or materialized table) or do the JOIN at the application layer.
Scaling
ClickHouse scales horizontally via sharding. You define a cluster, distribute data across shards based on a sharding key, and ClickHouse coordinates distributed queries. This works well but adds operational complexity -- you are now running a distributed system with replication, resharding considerations, and cluster-aware configuration. ClickHouse Cloud abstracts this away if you want managed infrastructure.
TimescaleDB scales vertically. Bigger instance, more RAM, faster disks. Timescale Cloud offers a multi-node option, but most deployments run on a single (large) node. This is simpler to operate but has a ceiling. For most workloads under 5-10TB of active data, a single well-provisioned TimescaleDB instance is sufficient. Beyond that, you start hitting Postgres's inherent single-node limitations.
Comparison table
| Dimension | ClickHouse | TimescaleDB |
|---|---|---|
| Architecture | Dedicated columnar OLAP engine | PostgreSQL extension |
| Storage model | Column-oriented, always compressed | Row-oriented + optional columnar compression on older chunks |
| Compression ratio | 10-15x typical | 5-10x typical |
| Analytical query speed (billions of rows) | Sub-second to low seconds | Tens of seconds (raw), fast via continuous aggregates |
| Ingestion rate (single node) | 1-2M rows/sec | 50-200K rows/sec |
| SQL compatibility | Custom dialect, improving JOINs, no real UPDATE/DELETE | Full PostgreSQL SQL |
| Point lookups | Slow (not designed for this) | Fast (standard Postgres) |
| Transactions | No | Full ACID |
| Scaling model | Horizontal sharding | Vertical (single node or multi-node cloud) |
| Managed options | ClickHouse Cloud | Timescale Cloud |
| Mixed OLTP + OLAP | No (analytics only) | Yes (same instance) |
| Operational overhead | Separate system to manage | Extension on existing Postgres |
| Best for | Dedicated analytics at massive scale | Teams already on Postgres needing time-series + analytics |
When to pick ClickHouse
You have billions of rows and need sub-second aggregations. Your team is comfortable operating a separate analytical system (or paying for ClickHouse Cloud). Your query patterns are diverse and ad-hoc -- you cannot pre-compute everything into continuous aggregates. You are ingesting at very high volume (100K+ events/sec). You do not need transactional semantics or frequent updates on your analytical data.
Real example: a product analytics platform processing 500M events/day across thousands of customers, where any customer can run arbitrary time-range queries over their event stream. ClickHouse handles this without breaking a sweat.
When to pick TimescaleDB
You are already running Postgres. You need both transactional and analytical workloads on the same data. Your team knows SQL and does not want to learn ClickHouse's quirks. Your data volume is under 5TB active. You value simplicity over raw speed. You want one database to back up, monitor, and maintain instead of two.
Real example: a SaaS application that stores user events in Postgres, needs to show usage dashboards in the product, and wants to run analytical queries for business reporting -- all without adding a separate system to the architecture.
The third option nobody talks about
For many teams, the real question is not "which database" but "do I need a dedicated analytical database at all?" If your total data volume is under 100GB and your dashboards have 10-20 known queries, Postgres with proper indexes and maybe a few materialized views handles real-time analytics just fine. You do not need ClickHouse or TimescaleDB's time-series features. You need a well-tuned Postgres instance and a dashboard tool that connects directly to it.
Fastero connects directly to both ClickHouse and TimescaleDB (and plain Postgres), so you can build dashboards and set metric alerts without an ETL layer in between. The database choice is yours -- the visualization and alerting layer does not force you into either camp.
The honest bottom line
ClickHouse is the right answer when analytical performance is your primary constraint and you are willing to accept the operational complexity of a separate system. It is not close on raw speed -- 10-100x faster for aggregations at scale is a structural advantage, not a tuning gap.
TimescaleDB is the right answer when you want analytics capabilities without leaving the Postgres ecosystem. The performance is good enough for most workloads (especially with continuous aggregates), and not running a second database has real value in reduced operational burden, simpler backups, fewer failure modes, and a smaller blast radius.
If you are unsure: start with TimescaleDB. You can always add ClickHouse later when (if) you outgrow it. Going the other direction -- migrating from ClickHouse back to Postgres -- is harder because you have probably built around ClickHouse's strengths and worked around its limitations in ways that do not translate back cleanly.
Related reading:
- Best Real-Time Analytics Platforms (2025)
- Apache Druid vs ClickHouse: OLAP Databases Compared
- InfluxDB vs TimescaleDB: Time-Series Databases Compared
- Tinybird vs ClickHouse: Serverless vs Self-Hosted Analytics
- How to Build a Live KPI Dashboard from Postgres
Try Fastero free — connect your data sources and set up real-time monitoring with triggers and alerts — ask questions in plain English, get answers in seconds. No credit card required.

