ClickHouse, Apache Druid, and StarRocks all answer the same question - which real-time analytics database should we bet on? - and "just use ClickHouse" is a bad answer until someone has asked about your ingestion patterns, query shapes, and operational capacity. All three are columnar, distributed, MPP OLAP systems built for sub-second queries on large datasets, but they reach that goal from very different starting points, and the differences decide which one fits a given workload.
Three philosophies for the same problem
ClickHouse
Born at Yandex for web analytics, ClickHouse is a C++ monolith in the best sense: a single binary handling storage, compute, and coordination. Data lives in its own columnar format using the MergeTree engine family, vectorized execution processes data in column batches, and the query planner is tightly coupled to storage. That coupling is both its greatest strength (extreme scan performance) and its main limit (less flexibility decoupling storage from compute).
Apache Druid
Created at Metamarkets for interactive analytics on event streams, Druid uses a microservices architecture with distinct roles: Coordinator, Overlord, Historical, MiddleManager, Broker, Router. Data is stored as immutable "segments" in deep storage (S3, HDFS, local disk) and cached on Historical nodes. Druid was designed around Kafka streaming from day one, with native real-time and batch indexing. It pre-aggregates at ingestion using "rollup," which can cut storage 10-100× at the cost of losing raw row-level detail.
StarRocks
The newest of the three (originally DorisDB, a fork of Apache Doris), StarRocks pairs a MySQL-compatible frontend (FE) with a C++ backend (BE). It's explicitly a "unified analytics database" aimed at both real-time ingestion and complex ad-hoc queries including multi-table joins, with a cost-based optimizer (CBO), vectorized execution, and columnar storage. Where ClickHouse optimized for scan throughput and Druid for streaming ingestion, StarRocks optimizes for query flexibility.
| Feature | ClickHouse | Apache Druid | StarRocks |
|---|---|---|---|
| Language | C++ | Java | C++ (BE) / Java (FE) |
| Execution model | Vectorized, pipeline | Scatter-gather on segments | Vectorized, pipeline, CBO |
| Storage/compute separation | SharedMergeTree (Cloud) | Deep storage + cache | Shared-data (3.0+) |
| Process roles | 1 (clickhouse-server) | 6+ distinct services | 2 (FE + BE) |
| Native streaming ingestion | Kafka engine (limited) | Native Kafka Supervisor | Routine Load (Kafka) |
| Join support | Limited (hash, no CBO) | Very limited (lookup joins) | Full joins with CBO |
| MySQL protocol | Read-only wire | No | Full compatibility |
Ingestion: batch vs streaming
This is often where the choice gets made for you. Data arriving in Kafka that must be queryable within seconds is a different evaluation than hourly Parquet files from S3.
ClickHouse excels at batch ingestion - loading Parquet, CSV, or INSERT INTO … SELECT from external tables is fast. For streaming it offers a Kafka table engine, but in practice it's fragile: consumer-group rebalancing, exactly-once semantics, and offset management all need careful tuning, and most production deployments use an external consumer that buffers events and batch-inserts every few seconds.
-- ClickHouse: batch insert from S3 Parquet
INSERT INTO events
SELECT * FROM s3('https://my-bucket.s3.amazonaws.com/events/2026-02-01/*.parquet',
'AWS_KEY', 'AWS_SECRET', 'Parquet');
-- ClickHouse: Kafka engine + materialized view
CREATE TABLE events_kafka (event_id String, user_id UInt64, event_type String,
properties String, timestamp DateTime64(3))
ENGINE = Kafka
SETTINGS kafka_broker_list = 'broker1:9092,broker2:9092',
kafka_topic_list = 'user_events',
kafka_group_name = 'clickhouse_consumer',
kafka_format = 'JSONEachRow';
CREATE MATERIALIZED VIEW events_mv TO events AS SELECT * FROM events_kafka;
Druid's Kafka ingestion is production-grade: define a supervisor spec and Druid handles consumer management, segment building, real-time-to-historical handoff, and exactly-once semantics, with data queryable 1-2 seconds after arrival. The trade is verbose JSON specs and a lot of trial and error tuning dimensions, metrics, rollup granularity, and segment size.
StarRocks sits in the middle. Its Routine Load connects directly to Kafka and is more reliable than ClickHouse's Kafka engine; it also supports Stream Load (HTTP push) and batch loads from S3/HDFS. Kafka-to-queryable latency is typically 3-5 seconds - behind Druid, well ahead of ClickHouse's Kafka engine.
-- StarRocks: Routine Load from Kafka
CREATE ROUTINE LOAD events_load ON events
COLUMNS (event_id, user_id, event_type, properties, event_time)
PROPERTIES ("desired_concurrent_number" = "3", "format" = "json",
"jsonpaths" = "[\"$.event_id\",\"$.user_id\",\"$.event_type\",\"$.properties\",\"$.timestamp\"]")
FROM KAFKA ("kafka_broker_list" = "broker1:9092,broker2:9092",
"kafka_topic" = "user_events", "property.group.id" = "starrocks_consumer");
Query performance
A benchmark on identical hardware (8 nodes, 32 vCPU / 128 GB each, NVMe) over 30 days of product-analytics events - 12 billion rows, ~2.4 TB uncompressed - across five representative query shapes:
| Query type | ClickHouse (s) | Druid (s) | StarRocks (s) |
|---|---|---|---|
| Simple count with filter | 0.08 | 0.12 | 0.11 |
| GROUP BY, 10K cardinality | 0.31 | 0.45 | 0.38 |
| Time-series rollup (hourly, 30 days) | 0.22 | 0.09 | 0.26 |
| Top-N, high cardinality (1M+) | 1.40 | 0.72 | 1.15 |
| Multi-table JOIN (events + users + products) | 4.80 | not supported | 1.90 |
| Approximate distinct (HyperLogLog) | 0.05 | 0.04 | 0.07 |
| Funnel query (3-step, 7-day window) | 3.20* | not supported | 2.50 |
* ClickHouse uses the specialized windowFunnel(); Druid lacks native funnel support; StarRocks uses CBO-optimized window functions.
The pattern is consistent with each engine's design. ClickHouse wins simple scans and aggregations on its vectorized engine and tight storage coupling. Druid wins time-series rollups and Top-N because those are exactly the shapes its segment index and bitmaps were built for. StarRocks wins joins and complex analytical queries because its CBO chooses between hash, broadcast, and shuffle join strategies.
The same query - daily active users by platform over 7 days - shows the dialect differences:
-- ClickHouse (own function set)
SELECT toDate(event_time) AS day, platform, uniqExact(user_id) AS dau
FROM events
WHERE event_time >= now() - INTERVAL 7 DAY AND event_type = 'session_start'
GROUP BY day, platform ORDER BY day, platform;
-- StarRocks (standard MySQL syntax)
SELECT DATE(event_time) AS day, platform, COUNT(DISTINCT user_id) AS dau
FROM events
WHERE event_time >= NOW() - INTERVAL 7 DAY AND event_type = 'session_start'
GROUP BY day, platform ORDER BY day, platform;
Druid's SQL layer handles simple queries like this but breaks down on subqueries, CTEs, or complex joins, where you drop into its native JSON query API.
Materialized views and pre-aggregation
ClickHouse materialized views are insert-time triggers - powerful for transforming, aggregating, and routing data as it arrives. The AggregatingMergeTree engine with the -State/-Merge combinator pattern maintains incrementally-updated aggregates and serves dashboards in single-digit milliseconds. It takes time to internalize but is unique to ClickHouse.
CREATE MATERIALIZED VIEW daily_stats_mv
ENGINE = AggregatingMergeTree() ORDER BY (day, platform) AS
SELECT toDate(event_time) AS day, platform,
uniqState(user_id) AS users_state, countState() AS events_state
FROM events GROUP BY day, platform;
-- query with the -Merge combinators
SELECT day, platform, uniqMerge(users_state) AS unique_users, countMerge(events_state) AS total_events
FROM daily_stats_mv GROUP BY day, platform;
Druid's equivalent is rollup at ingestion time - more rigid (dimensions decided upfront) but simpler to operate, with no separate view to manage. StarRocks supports both synchronous MVs (insert-time, like ClickHouse) and asynchronous MVs that can join multiple tables - something neither ClickHouse nor Druid does natively:
CREATE MATERIALIZED VIEW user_event_summary
REFRESH ASYNC EVERY (INTERVAL 5 MINUTE) AS
SELECT u.country, u.signup_source, DATE(e.event_time) AS day,
COUNT(DISTINCT e.user_id) AS unique_users, COUNT(*) AS total_events
FROM events e JOIN users u ON e.user_id = u.user_id
WHERE e.event_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY u.country, u.signup_source, DATE(e.event_time);
Approximate query processing
At billions of rows, exact counts are expensive, and all three support approximate algorithms with differing depth. ClickHouse offers the broadest set - HyperLogLog (uniq), t-digest quantiles, and SAMPLE. Druid's DataSketches integration makes its Theta-sketch support best-in-class for approximate set operations (intersection/union on distinct counts). StarRocks has approx_count_distinct and percentile approximation - functional but less comprehensive. A workload that leans on distinct-count set operations, like intersecting two user segments, points clearly to Druid.
Operational complexity
ClickHouse is deceptively simple to start - one binary, one config file - but scaling brings complexity: Keeper (or ZooKeeper) for replication coordination, manual shard rebalancing, merge pressure under heavy writes, dozens of system tables to monitor, and asynchronous mutations that can pile up. A practical rule: with fewer than two engineers able to dedicate time to operations, use ClickHouse Cloud rather than self-hosting.
Druid is the most operationally complex - six to eight process types, each with its own JVM tuning and failure modes, plus ZooKeeper, a metadata store, and deep storage. Segment management, compaction, coordinator balancing, and MiddleManager capacity planning are ongoing concerns, and operations runbooks routinely run dozens of pages. The payoff is stability once tuned: Historical nodes are stateless caches that can be killed and restarted freely.
StarRocks is the simplest - two process types, no ZooKeeper dependency, built-in replication and rebalancing, online schema changes, and a MySQL protocol that every monitoring tool and BI connector already speaks. The cost is a smaller, less battle-tested community; ClickHouse answers are a Stack Overflow search away, while StarRocks may send you into Chinese-language GitHub issues.
Cloud offerings
| Aspect | ClickHouse Cloud | Imply Polaris (Druid) | CelerData (StarRocks) |
|---|---|---|---|
| Cloud platforms | AWS, GCP, Azure | AWS, GCP | AWS, GCP |
| Storage/compute separation | SharedMergeTree | Deep storage + elastic query | Shared-data mode |
| Auto-scaling | Vertical + horizontal | Query tier | Compute nodes |
| Maturity | GA 2022, very mature | GA 2023, solid | GA 2024, growing |
ClickHouse Cloud is the most mature and feature-complete, handling SharedMergeTree transparently with auto-scaling and built-in observability. Imply Polaris removes most of Druid's operational pain behind a SQL-first interface. CelerData is newest but starts from StarRocks already being simpler to run.
SQL compatibility
Because analysts and data scientists write SQL, compatibility drives adoption speed. StarRocks speaks full MySQL protocol and syntax - existing queries, BI tools (Tableau, Looker, Metabase, Superset), and JDBC/ODBC connectors work out of the box, with CTEs, window functions, and correlated subqueries under the CBO. ClickHouse uses its own dialect with a MySQL read wire; most standard SQL works, but edge cases appear (JOIN semantics default to hash with the right table in memory, a non-standard type system). Druid's SQL layer handles simple SELECT/GROUP BY/ORDER BY but may fail or mislead on CTEs, nested subqueries, non-equi joins, or complex HAVING - leaving the native query language for anything advanced.
Picking the right engine
Choose ClickHouse for log analytics and observability (its scan speed on wide append-only tables is unmatched - Cloudflare, Uber, and many observability companies built on it), for batch or micro-batch ingestion, for the broadest ecosystem (Grafana, dbt, Airbyte, Vector, Fluent Bit), and when queries are mostly single-table scans and aggregations rather than complex joins.
Choose Druid when you need sub-second Kafka-to-query latency at high throughput, when query patterns are time-series + Top-N + rollups, when dimensions can be pre-defined at ingestion for rollup storage savings, and for ad-tech or real-time bidding, where streaming ingestion, pre-aggregation, and approximate set operations line up almost exactly with the domain.
Choose StarRocks when you need complex joins and ad-hoc analytical queries (funnels, window functions over joined dimension tables), when you want the simplest operations on a small team, when MySQL compatibility matters for existing tooling, and when building a product-analytics or BI backend where query flexibility outweighs raw scan speed.
| Use case | Best pick | Runner-up |
|---|---|---|
| Log / observability analytics | ClickHouse | StarRocks |
| Product analytics (funnels, retention) | StarRocks | ClickHouse |
| Real-time dashboards from Kafka | Druid | StarRocks |
| Ad-tech / bidding analytics | Druid | ClickHouse |
| BI / ad-hoc SQL analytics | StarRocks | ClickHouse |
| IoT time-series | ClickHouse | Druid |
| Multi-tenant SaaS analytics | StarRocks | ClickHouse Cloud |
A common production split runs ClickHouse for high-volume observability (logs, metrics, traces) and StarRocks as the analytical backend for customer-facing product dashboards, with Druid reserved for pure streaming use cases where sub-second freshness is non-negotiable - its lack of joins and SQL limits rule it out for ad-hoc query patterns. Starting fresh, StarRocks is the best general-purpose pick for its balance of query power, SQL compatibility, and operational simplicity, with ClickHouse for append-heavy log and metric workloads.
The space is consolidating fast - ClickHouse is adding better joins and a CBO, StarRocks is improving scan performance, Druid is expanding its SQL layer - and these systems may converge in a couple of years. For now the differences are large enough that the right pick saves months of engineering time. Choose on query shapes and ingestion patterns, not benchmark marketing, and run a proof-of-concept on your actual data and queries first: the two weeks spent there save six months of regret.




Leave a Comment