AI tools such as Claude Code, Codex or the assistant built into ClickHouse Cloud can handle most of the work. Still, understand these before going to production: they are easy to decide on day one and costly to change once the data is large.
Disks on each server (NVMe or SSD): usually faster I/O than object storage, most of all when data is not cached. Every replica holds a full copy; older data can move to HDD or S3.
Scaling
Replicas with ReplicatedMergeTree and ClickHouse Keeper (3 nodes). Shards with Distributed tables, which spread data and disk I/O across servers.
You run
Self-hosted: everything, from upgrades and backups to monitoring. Managed: the vendor does, in its cloud or yours.
Pricing
Follows the servers and disks underneath, yours or the vendor's. Storage usually costs more: it is SSD, and every replica holds a full copy.
Fits
I/O-heavy queries, steady heavy load, data residency, control over versions and hardware.
Few, large inserts, each within one partition. With PARTITION BY toDate(timestamp), each day's rows share one partition, and background merges keep combining the parts inside it. The official guide, Selecting an insert strategy ↗, explains how to insert; the animation shows where the rows land and how merges keep up.
A slice of a table's rows kept on its own servers. Sharding spreads disk I/O across servers and can speed up reads a lot, but a single server works fine without it. ClickHouse Cloud has no shards.
Partition
A logical group of parts that share the same partition-key value. Parts from different partitions are never merged.
Part
A self-contained, immutable unit of sorted columnar data. Inserts create new parts; background merges replace smaller parts with larger ones within the same partition. Small parts can use Compact format, while larger parts typically use Wide format.
Granule
A consecutive range of rows within a part, ordered by the sorting key. It is the smallest unit ClickHouse reads—usually 8,192 rows by default, though the size can vary.
Indexes
The sparse primary index records sorting-key values at granule boundaries. Data-skipping indexes store metadata for one or more granules. Together they rule out granules that cannot match a query before reading column data.
Partition → part → granule
Illustrative example
04 · Transformations
How do I join big tables without running out of memory?#
ClickHouse has a rich set of functions for all kinds of transformations. However, performance problems, above all MEMORY_LIMIT_EXCEEDED errors, happen often when pipelines are not optimized.
Hash join
ClickHouse's default join. It loads the right-hand table into memory as a hash table and streams the left one through it.
One tenant per run
Every table is filtered with WHERE tenant_id = t before the join, so only that tenant reaches memory. Each run appends to the same destination table.
tenant_id first
With ORDER BY (tenant_id, …), a tenant is one range in each table, so a run reads only its own granules: together, the runs read each table once.