Open-Source Wikis

/

ClickHouse

/

ClickHouse

/

Architecture

clickhouse/clickhouse

Architecture

ClickHouse is a columnar OLAP database written in C++23. A single binary, dispatched by programs/main.cpp, hosts the server, the clients, the embedded engine (clickhouse-local), and the Raft coordination service (clickhouse-keeper). All of these share the same engine code under src/.

This page sketches the request lifecycle and the major subsystems. Each subsystem has its own wiki page under systems/.

Birds-eye view

graph TD
    Client[Clients<br/>HTTP / Native TCP / MySQL / Postgres / gRPC] --> Server[programs/server<br/>src/Server]
    Server --> Parsers[src/Parsers<br/>SQL → AST]
    Parsers --> Analyzer[src/Analyzer<br/>QueryTree, name resolution, types]
    Analyzer --> Planner[src/Planner<br/>logical → physical plan]
    Planner --> Pipeline[src/QueryPipeline + src/Processors<br/>operator graph]
    Pipeline --> Storages[src/Storages<br/>MergeTree, Distributed, ObjectStorage, Kafka, ...]
    Storages --> Disks[src/Disks + src/IO<br/>local FS, S3, Azure, HDFS, web]
    Pipeline --> Functions[src/Functions + src/AggregateFunctions]
    Server -.metadata.-> Coord[src/Coordination<br/>clickhouse-keeper / ZooKeeper]
    Storages -.metadata.-> Coord

The request lifecycle

A SELECT arrives at the server through one of the protocol handlers (HTTPHandler, TCPHandler, MySQL/Postgres/gRPC/ArrowFlight in src/Server/). Each handler creates a per-query Context (src/Interpreters/Context.h) that carries the user, settings, access rights, and ClientInfo.

  1. Parse. src/Parsers/ turns the SQL text into an AST (ASTSelectQuery, ASTInsertQuery, …). parseQuery.cpp is the entry point.
  2. Analyze. With enable_analyzer=1 (the default since v24.x), the parser AST is converted into a QueryTree by src/Analyzer/QueryTreeBuilder.cpp. The analyzer resolves names, expands *, infers types, normalizes joins, and fixes pass-types. The legacy path goes through src/Interpreters/InterpreterSelectQuery.cpp and ExpressionAnalyzer.cpp instead.
  3. Plan. src/Planner/Planner.cpp walks the QueryTree and emits a QueryPlan (src/Processors/QueryPlan/) — a tree of logical steps (ReadFromMergeTree, FilterStep, AggregatingStep, JoinStep, LimitStep, …).
  4. Optimize. QueryPlanOptimizations apply rule-based passes: predicate pushdown, projection use, filter merging, ORDER BY removal, IN-set lifting, distributed-aware reordering.
  5. Build the pipeline. Each step builds processors (src/Processors/). A processor is a node in a pull-based dataflow graph with input and output ports (Port.h, IProcessor.h). The graph is executed by src/Processors/Executors/PipelineExecutor.cpp over a thread pool.
  6. Read. Source processors (MergeTreeSource, RemoteSource, KafkaSource, …) emit Chunks of column data. Chunk wraps a Columns array plus row count.
  7. Transform. Filters, aggregations, joins, sorting, window functions, and projections are implemented as ITransform subclasses. Heavy state (hash tables, sort buffers, join indexes) lives in src/Interpreters/.
  8. Sink. For SELECT the final Chunk stream is serialized into the requested Format (src/Formats/) and written back to the client. For INSERT the sink writes a new part into the storage engine.

Storage layer

graph LR
    SQL[INSERT/SELECT] --> Storage[IStorage<br/>src/Storages]
    Storage --> MT[StorageMergeTree<br/>StorageReplicatedMergeTree]
    Storage --> Dist[StorageDistributed]
    Storage --> Obj[ObjectStorage<br/>S3/Azure/HDFS]
    Storage --> Stream[Kafka / NATS / RabbitMQ / FileLog]
    Storage --> Memory[Memory / Buffer / Set / Join]
    Storage --> External[MySQL / Postgres / Mongo / Redis / SQLite]
    MT --> Parts[Data Parts<br/>granules + marks + columns]
    Parts --> Disk[IDisk<br/>local / s3 / azure / web / cache]
    MT -.coordination.-> Keeper[clickhouse-keeper / ZooKeeper]

IStorage (src/Storages/IStorage.h) is the abstract base for every table engine. MergeTree is the workhorse: it writes rows as immutable, sorted, columnar parts that are later merged in the background. Mutations are themselves merges that rewrite the affected ranges. Replication uses the Keeper-backed log in StorageReplicatedMergeTree.

Object-storage engines (StorageS3, StorageAzure, StorageHDFS, StorageIceberg, StorageDeltaLake, StorageHudi) live under src/Storages/ObjectStorage/. They share a common abstraction (IObjectStorage in src/Disks/ObjectStorages/) so the same code reads MergeTree parts from a bucket as reads *.parquet files from a bucket.

The execution engine

src/Processors/ implements a pull-based, push-back-pressured operator graph. Each processor exposes a status (NeedData, PortFull, Ready, Async, ExpandPipeline, Finished) and a work() method. The PipelineExecutor walks the graph, picks ready processors, and runs them on a thread pool.

Key operator implementations live in src/Processors/Transforms/ and src/Processors/Merges/AggregatingTransform, MergingAggregatedTransform, MergeJoinTransform, SortingTransform, WindowTransform, LimitTransform, etc. The non-trivial algorithms (vectorized aggregation, hash join, merge join, gorilla compression, query JIT) are kept in src/Interpreters/Aggregator.cpp, src/Interpreters/HashJoin/, and src/Interpreters/ExpressionJIT.cpp.

Coordination and replication

Replicated tables and Keeper itself form ClickHouse's only stateful coordination layer. src/Coordination/ is a Raft implementation built on top of NuRaft (vendored in contrib/NuRaft) with a RocksDB-backed log (contrib/rocksdb) and an in-memory KV state machine (KeeperStateMachine.cpp, KeeperStorage.cpp). It speaks the ZooKeeper wire protocol so existing tooling works.

StorageReplicatedMergeTree writes a log of part operations to Keeper. Replicas pull entries, fetch parts from peers via an HTTP service (DataPartsExchange.cpp), and merge in the background. Distributed tables (src/Storages/StorageDistributed.cpp) are stateless query routers; sharding/replication is described in the cluster config (src/Interpreters/Cluster.cpp).

Cross-cutting concerns

  • Type system (src/DataTypes/, src/Columns/, src/Core/): every value passes through a typed columnar container. Block is a vector of named Column pointers plus types. Field is the boxed scalar form used in interpretation.
  • Functions (src/Functions/, src/AggregateFunctions/): each function is a class registered into a global FunctionFactory. Most functions are vectorized over Column and have specialized integer/float/string paths.
  • Settings (src/Core/Settings.cpp, src/Core/ServerSettings.cpp, src/Storages/MergeTree/MergeTreeSettings.cpp): there are 1500+ runtime settings, registered via X-macros and queryable through system.settings.
  • Access control (src/Access/): users, roles, grants, row policies, quotas, settings profiles. Backed by RBAC stored either in XML/users.xml or in IDiskAccessStorage / Keeper.
  • Backups (src/Backups/): BACKUP and RESTORE to local disk, S3, Azure, etc. Splits into entries and reuses the disks layer.

Configuration

Server config is XML or YAML (programs/server/config.xml, users.xml) with includes from config.d/ and users.d/. The schema is documented inline. At runtime the merged config is exposed in system.server_settings and system.settings.

Build system

CMake plus ninja, with clang mandatory. Top-level entry: CMakeLists.txt. Toolchain configuration lives in cmake/ and PreLoad.cmake. External libraries are in contrib/ (git submodules) plus a small Rust workspace in rust/. For build commands see Getting started.

Further reading

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Architecture – ClickHouse wiki | Factory