- date
- 2026-10-27
- tags
- [architecture, elasticsearch, clickhouse, case-study]
Elasticsearch for search, ClickHouse for analytics
Why I kept a working search engine and added a second datastore only for the workload that needed it, on a solo-built B2B search platform.
From October 2025 to October 2026 I built a B2B search platform on my own: contact discovery and lead generation over hundreds of millions of searchable records. I designed it end to end, and I also deployed and operated it. That last part shaped every architecture decision, because any component I added was one I’d be running.
This post is about one of those decisions: which datastore does what.
Two workloads that look alike
The product asks two kinds of questions of the same data.
- Interactive search and filtering. A user narrows a large dataset by indexed fields and expects results while they’re still looking at the screen.
- Analytics and reporting. Aggregations, rankings, grouped counts and reporting scans over the same records.
Both are “queries over records”, which makes it tempting to answer both from one place. But they optimise for different things. Search wants fast discovery over indexed fields. Analytics wants to scan and aggregate many rows cheaply.
What was already working
Elasticsearch already handled the core search and filtering workload, and handled it well enough. The dataset was large, but large by itself wasn’t a reason to replace anything.
That matters, because the easy story here would be “Elasticsearch couldn’t cope, so I brought in ClickHouse”. That isn’t what happened, and I’d rather not tell it that way.
The decision
I kept the boundary simple:
Elasticsearch → interactive search / filtering
ClickHouse → analytics / aggregation / ranking / reporting
Elasticsearch stays wherever the existing search and index model is enough. ClickHouse is used only for analytical access patterns that justify an extra pipeline. Its column-oriented storage suits aggregation and ranking, and its cost profile for those workloads was better. I’m keeping that cost point qualitative, since I don’t have a retained benchmark to put a number on it.
What I didn’t do
Keep everything in Elasticsearch. This was a real option. One datastore is simpler to run, and Elasticsearch can do aggregations. I chose the split because analytics was a distinct enough responsibility to deserve its own path, and because it kept reporting concerns from shaping the search design. It wasn’t because Elasticsearch couldn’t do it.
Replace Elasticsearch with ClickHouse. This would have mixed a storage migration into an analytics decision, to solve a search problem I didn’t have.
One generic datastore for everything. Minimising the number of technologies is a good default, but it shouldn’t be the guiding rule when two workloads clearly differ.
What the second datastore costs
A second datastore is not free, and on a solo project the person paying was me:
- data has to be projected into an analytical shape and kept moving;
- schema changes may need coordinating across the search and analytics projections;
- freshness expectations have to be explicit, since the two can lag each other;
- there’s more to monitor, deploy and recover when something fails.
So the rule I took from this isn’t “two databases are better than one”. It’s that a second datastore is justified when the workload boundary is worth its operating cost. Here it was. On a smaller dataset, or with lighter reporting, I’d have stayed with one.
Notes
- The product is anonymised on purpose, so I call it “a B2B search platform” and describe its scale only in round terms.
- This is a reconstructed decision record, written after the fact from my own production ownership. It shows how I reasoned, but it isn’t an independent audit.
I keep the general version of this rule in the garden: A second datastore has to earn its operating cost.
$ grep -rl "[[blog/elasticsearch-for-search-clickhouse-for-analytics]]" ~ # linked from
- blog/reputation-scoring-64-million-wallets.mdReputation scoring over 64 million wallets
When one workload is mostly aggregation and ranking, give it a store built for that, rather than stretching the transactional or search side to cover it. I applied the same thinkin…
- garden/datastore-per-access-pattern.mdA second datastore has to earn its operating cost
Elasticsearch for search, ClickHouse for analytics: kept Elasticsearch for search and added ClickHouse only for analytics.