clickhouse/clickhouse
Distributed queries
A ClickHouse cluster is just a set of independent servers configured to know about each other. There is no shared catalogue and no central coordinator (Keeper handles only metadata, not query execution). Distributed queries are routed by Distributed tables — stateless query planners that fan out to shards.
The cluster model
<remote_servers>
<my_cluster>
<shard>
<replica><host>n1</host><port>9000</port></replica>
<replica><host>n2</host><port>9000</port></replica>
</shard>
<shard>
<replica><host>n3</host><port>9000</port></replica>
<replica><host>n4</host><port>9000</port></replica>
</shard>
</my_cluster>
</remote_servers>A cluster has shards; each shard has one or more replicas. The configuration is loaded by src/Interpreters/Cluster.cpp. Cluster-discovery (src/Interpreters/ClusterDiscovery.cpp) extends this by auto-populating clusters from Keeper.
Distributed tables
StorageDistributed (src/Storages/StorageDistributed.cpp, ~90 KB) is a query router with no data. Created with:
CREATE TABLE events_dist AS events
ENGINE = Distributed(my_cluster, my_db, events, sharding_key);Reads:
- The query is rewritten so that the local part runs against
my_db.eventson each shard. - Sub-queries are issued in parallel to one replica per shard via
ClusterProxy::SelectStreamFactory.cpp. - Partial results are merged on the initiator (or push-down through merge-aggregated steps).
Writes (when the distributed table itself is the target):
- Sync (
distributed_foreground_insert=1) — each block is split by the sharding key and forwarded directly. - Async (default in older versions; toggled by
distributed_foreground_insert) — blocks are written to a localdata/<dist_table>/queue and flushed by a background thread.
ClusterProxy
src/Interpreters/ClusterProxy/ is the layer that rewrites a query for distributed execution and creates RemoteSource processors:
SelectStreamFactory.cpp— creates per-shard streams.executeQuery.cpp— picks replicas, optionally hedges, distributes mark ranges (parallel replicas).
Replica selection
load_balancing setting:
random(default).nearest_hostname— by hostname distance.in_order— replica list order.first_or_random— try first; fall back to random.round_robin— rotates per query.
HedgedConnectionsFactory.cpp (in src/Client/) implements hedged requests: try one replica, after hedged_connection_timeout_ms start a second, take whichever responds first.
Parallel replicas
A different fan-out: a single SELECT against a ReplicatedMergeTree is split across the replicas of one shard. The leader splits mark ranges; followers pull. Toggled by allow_experimental_parallel_reading_from_replicas / max_parallel_replicas. See Replication for the read path.
DDL across the cluster
SOMETHING ON CLUSTER cluster_name runs the statement on every shard's selected replicas. It is queued in /clickhouse/task_queue/ddl/ in Keeper and consumed by DDLWorker (src/Interpreters/DDLWorker.cpp). Status is in system.distributed_ddl_queue. Replicated databases automate this away.
Insert deduplication
Because inserts can be routed to multiple replicas of a shard, ClickHouse deduplicates by hashing the inserted block (within a window of replicated_deduplication_window blocks). This makes idempotent retries safe.
Sharding key
sharding_key is an arbitrary expression. Its value modulo the shard count picks the destination shard. Using cityHash64(user_id) is typical. With internal_replication=1 the destination shard handles its own replication.
Aggregation strategies
For SELECT count(), sum(x), etc. the planner emits an AggregatingStep per shard, then a MergingAggregatedStep on the initiator. For GROUP BY with high cardinality the planner uses two-level hashing across shards. Aggregator.cpp implements the merge.
For non-additive operations (uniqExact, quantileExact, …) shards send full data; for additive ones (sum, avg, uniq, quantileTDigest, …) shards send partial states and the initiator merges.
Network compression
Inter-server traffic is compressed by default (network_compression_method, defaulting to LZ4). For wide networks ZSTD may be cheaper.
Diagnostics
system.clusters— current topology.system.distributed_ddl_queue— pendingON CLUSTERwork.system.distribution_queue— async-distributed-insert backlog.system.distributed_replicas— replica info.- Add
SETTINGS log_queries=1and usesystem.query_logfiltered bytype='QueryFinish'to see how a query was decomposed.
Settings of note
prefer_localhost_replica— read from the local replica when possible.distributed_aggregation_memory_efficient— streaming two-phase aggregation.optimize_distributed_group_by_sharding_key— push GROUP BY into shards when the key matches the sharding key.parallel_distributed_insert_select— paralleliseINSERT ... SELECTacross shards.
Related pages
- Replication
- Other engines —
StorageDistributedand*Clustervariants. - Server protocols — interserver HTTP and native TCP.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.