ClickHouse 26.x — Columnar Database cho Real-Time Analytics tỷ dòng dữ liệu mỗi giây

Posted on: 4/27/2026 5:11:59 AM

Khi hệ thống cần phân tích hàng tỷ dòng dữ liệu trong vài giây, những database truyền thống như MySQL hay PostgreSQL bắt đầu "nghẹt thở". Đây chính là sân chơi của ClickHouse — columnar database mã nguồn mở chuyên xử lý real-time analytics với tốc độ nhanh gấp hàng trăm lần RDBMS thông thường. Với phiên bản 26.x (2026), ClickHouse tiếp tục nâng tầm với full-text search GA, materialized CTEs, WebAssembly UDFs, và embedded observability stack. Bài viết này đi sâu vào kiến trúc, cách ClickHouse đạt hiệu năng đáng kinh ngạc, và hướng dẫn thiết kế hệ thống analytics production-ready.

1. Tại sao ClickHouse nhanh đến vậy?

ClickHouse được thiết kế từ đầu cho OLAP (Online Analytical Processing) — tối ưu cho các truy vấn đọc trên tập dữ liệu lớn thay vì giao dịch OLTP nhỏ lẻ. Ba trụ cột kiến trúc giúp ClickHouse vượt trội:

96T events quét trong <2 giây (Cloudflare)
600K rows/sec ingestion (Goldsky blockchain)
3.2x nhanh hơn RIGHT/FULL JOIN (v26.2)
96% granules giảm quét với full-text index

1.1. Columnar Storage — Đọc ít, trả nhanh

Khác với row-oriented database (MySQL, PostgreSQL) lưu từng hàng liền nhau trên disk, ClickHouse lưu từng cột riêng biệt. Khi truy vấn SELECT avg(price) FROM orders WHERE date > '2026-01-01', ClickHouse chỉ đọc 2 cột pricedate thay vì toàn bộ hàng — giảm I/O lên đến 100x trên bảng rộng.

graph LR
    subgraph ROW["Row-Oriented (MySQL)"]
        R1["Row 1: id|name|price|date"]
        R2["Row 2: id|name|price|date"]
        R3["Row 3: id|name|price|date"]
    end
    subgraph COL["Column-Oriented (ClickHouse)"]
        C1["Column: id → 1,2,3,..."]
        C2["Column: name → A,B,C,..."]
        C3["Column: price → 10,20,30,..."]
        C4["Column: date → 01,02,03,..."]
    end
    Q["SELECT avg(price)
WHERE date > X"] -->|"Đọc toàn bộ rows"| ROW Q -->|"Chỉ đọc 2 cột"| COL style COL fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50 style ROW fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style Q fill:#e94560,stroke:#fff,color:#fff style C3 fill:#e8f5e9,stroke:#4CAF50,color:#2c3e50 style C4 fill:#e8f5e9,stroke:#4CAF50,color:#2c3e50

Hình 1: Row-oriented vs Column-oriented — ClickHouse chỉ đọc các cột cần thiết

1.2. Nén dữ liệu cực hiệu quả

Vì dữ liệu cùng cột có kiểu giống nhau (toàn số, toàn string, toàn date), tỷ lệ nén rất cao. ClickHouse hỗ trợ nhiều codec: LZ4 (mặc định, nhanh), ZSTD (tỷ lệ nén cao hơn), Delta + ZSTD cho time series, và codec mới ALP (v26.3) cho floating-point data. Tỷ lệ nén thực tế thường đạt 5-15x, nghĩa là 1TB dữ liệu gốc chỉ chiếm 70-200GB trên disk.

-- Chọn codec nén phù hợp cho từng cột
CREATE TABLE events (
    event_time DateTime CODEC(Delta, ZSTD(3)),    -- time series: Delta encoding + ZSTD
    user_id    UInt64   CODEC(T64, LZ4),           -- integer: T64 transform + LZ4
    price      Float64  CODEC(ALP),                -- floating-point: ALP codec (v26.3)
    url        String   CODEC(ZSTD(5)),            -- text: ZSTD mức nén cao
    status     LowCardinality(String)              -- dictionary encoding tự động
) ENGINE = MergeTree()
ORDER BY (event_time, user_id);

1.3. Vectorized Query Execution

ClickHouse xử lý dữ liệu theo block (mặc định 65.536 rows/block) thay vì từng row. Mỗi block được xử lý bằng SIMD instructions (SSE4.2, AVX2, AVX-512), tận dụng tối đa CPU pipeline và cache. Kết quả: throughput truy vấn cao gấp 10-100x so với row-by-row processing.

Sparse Primary Index — không như B-Tree

ClickHouse không dùng B-Tree index như RDBMS truyền thống. Thay vào đó, nó dùng sparse index — chỉ lưu giá trị đầu tiên của mỗi granule (mặc định 8.192 rows). Index cực nhỏ (thường vài MB cho bảng hàng tỷ dòng), nên luôn nằm gọn trong RAM. Khi query, ClickHouse skip toàn bộ granules không khớp điều kiện WHERE — gọi là data skipping.

2. ClickHouse 26.x — Những cải tiến đáng chú ý

Tháng 2/2026 — ClickHouse 26.2
Full-text search GA: Index mới giảm 96% granules cần quét so với Bloom filter. QBit data type production-ready. RIGHT/FULL JOIN nhanh gấp 3.2x. Embedded ClickStack cho in-product observability. Time-based block flushing cho streaming data.
Tháng 4/2026 — ClickHouse 26.3 LTS
Materialized CTEs (experimental): tái sử dụng kết quả CTE trong cùng query. WebAssembly UDFs: viết custom function bằng Rust/Go/C++ compile sang Wasm. ALP compression cho floating-point. Asynchronous inserts thành default. JOIN reordering cho ANTI/SEMI/FULL.
Tháng 4/2026 — clickhousectl CLI (Beta)
CLI chính thức hợp nhất quản lý local + cloud. Hỗ trợ scaffolding project, quản lý phiên bản, và tích hợp AI agent — thiết kế sẵn cho kỷ nguyên agentic.

2.1. Full-Text Search — Cuộc cách mạng tìm kiếm trong ClickHouse

Trước v26.2, tìm kiếm text trong ClickHouse chủ yếu dùng LIKE hoặc Bloom filter index — chậm và không chính xác. Từ v26.2, ClickHouse có full-text index thực sự, dùng front-coding compressionadaptive posting lists tối ưu cho object storage.

-- Tạo full-text index trên cột log message
CREATE TABLE logs (
    timestamp DateTime64(3),
    service   LowCardinality(String),
    level     Enum8('DEBUG'=0, 'INFO'=1, 'WARN'=2, 'ERROR'=3),
    message   String,
    INDEX idx_msg message TYPE full_text GRANULARITY 1
) ENGINE = MergeTree()
ORDER BY (service, timestamp);

-- Truy vấn full-text nhanh gấp nhiều lần so với LIKE
SELECT timestamp, service, message
FROM logs
WHERE hasToken(message, 'timeout') AND level >= 'ERROR'
ORDER BY timestamp DESC
LIMIT 100;

-- So sánh: Bloom filter quét ~2.400 granules, full-text index chỉ quét ~96 granules
-- trên cùng dataset → nhanh hơn ~25x

Full-text search thay thế Elasticsearch?

Với full-text search GA, ClickHouse bắt đầu lấn sân sang lãnh địa của Elasticsearch trong use case log analytics. Ưu điểm: không cần cluster riêng cho search, dữ liệu nén tốt hơn (5-10x so với Elasticsearch), SQL syntax quen thuộc. Hạn chế: chưa hỗ trợ fuzzy search, relevance scoring phức tạp, hay synonym expansion — các tính năng Elasticsearch đã mature. Kết luận: phù hợp cho log search và structured analytics, chưa thay thế được Elasticsearch cho full-blown search engine.

2.2. Materialized CTEs — Tránh tính toán lặp

Trước v26.3, CTE trong ClickHouse chỉ là syntax sugar — mỗi lần reference đều re-execute. Materialized CTEs lưu kết quả tạm thời và tái sử dụng, đặc biệt hữu ích cho query phức tạp với nhiều sub-aggregation.

-- Materialized CTE: tính một lần, dùng nhiều lần
WITH daily_stats AS MATERIALIZED (
    SELECT
        toDate(event_time) AS day,
        service,
        count() AS total_events,
        countIf(level = 'ERROR') AS error_count
    FROM logs
    WHERE event_time >= now() - INTERVAL 30 DAY
    GROUP BY day, service
)
SELECT
    s1.service,
    avg(s1.total_events) AS avg_daily_events,
    avg(s1.error_count) AS avg_daily_errors,
    -- So sánh với tuần trước
    (SELECT avg(total_events) FROM daily_stats s2
     WHERE s2.service = s1.service
       AND s2.day >= now() - INTERVAL 7 DAY) AS avg_last_7d
FROM daily_stats s1
GROUP BY s1.service;

2.3. WebAssembly UDFs — Custom Functions không giới hạn

ClickHouse 26.3 cho phép viết User-Defined Functions bằng bất kỳ ngôn ngữ nào compile sang WebAssembly — Rust, Go, C++, AssemblyScript. UDF chạy trong an toàn, không ảnh hưởng đến server stability.

// Rust → WebAssembly UDF: tính Haversine distance
// Compile: cargo build --target wasm32-wasi --release
#[no_mangle]
pub extern "C" fn haversine(lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64 {
    let r = 6371.0; // Earth radius in km
    let dlat = (lat2 - lat1).to_radians();
    let dlon = (lon2 - lon1).to_radians();
    let a = (dlat / 2.0).sin().powi(2)
        + lat1.to_radians().cos() * lat2.to_radians().cos()
        * (dlon / 2.0).sin().powi(2);
    r * 2.0 * a.sqrt().atan2((1.0 - a).sqrt())
}
-- Đăng ký Wasm UDF trong ClickHouse
CREATE FUNCTION haversine_km AS 'haversine'
USING '/path/to/haversine.wasm'
ARGUMENTS (lat1 Float64, lon1 Float64, lat2 Float64, lon2 Float64)
RETURNS Float64;

-- Sử dụng: tìm tất cả cửa hàng trong bán kính 5km
SELECT name, haversine_km(10.7769, 106.7009, lat, lon) AS distance_km
FROM stores
WHERE distance_km <= 5
ORDER BY distance_km;

3. Thiết kế hệ thống Analytics với ClickHouse

Dưới đây là kiến trúc reference cho hệ thống analytics production xử lý hàng triệu events/giây:

graph TB
    SOURCE["Data Sources
(Apps, IoT, Logs)"] --> KAFKA["Apache Kafka
/ Redpanda"] KAFKA --> CH_KAFKA["Kafka Engine Table
(ClickHouse)"] CH_KAFKA --> MV["Materialized View
(Transform + Aggregate)"] MV --> TARGET["MergeTree Table
(Final Storage)"] TARGET --> QUERY["Query Layer"] QUERY --> GRAFANA["Grafana
Dashboard"] QUERY --> API["Analytics API
(.NET / Node.js)"] QUERY --> AGENT["AI Agent
(MCP Server)"] SOURCE --> BATCH["Batch Ingestion
(Airflow / dbt)"] BATCH --> TARGET style TARGET fill:#e94560,stroke:#fff,color:#fff style KAFKA fill:#2c3e50,stroke:#e94560,color:#fff style MV fill:#16213e,stroke:#e94560,color:#fff style GRAFANA fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style API fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style AGENT fill:#f8f9fa,stroke:#e94560,color:#2c3e50

Hình 2: Kiến trúc analytics pipeline — streaming qua Kafka + batch, lưu trữ MergeTree, phục vụ dashboard + API + AI agent

3.1. MergeTree Engine — Trái tim của ClickHouse

MergeTree là table engine quan trọng nhất. Dữ liệu insert được ghi vào parts riêng biệt (immutable), sau đó ClickHouse merge các parts nhỏ thành parts lớn hơn ở background — tương tự LSM-tree. Thiết kế này cho phép insert cực nhanh (append-only) và query hiệu quả (parts lớn, sorted).

-- Thiết kế bảng analytics events tối ưu
CREATE TABLE analytics_events (
    -- Partition key: theo tháng, giúp TTL và query pruning
    event_date    Date,
    event_time    DateTime64(3),

    -- Primary key columns: thứ tự quan trọng!
    -- Đặt cột filter phổ biến nhất lên đầu
    tenant_id     UInt32,
    event_type    LowCardinality(String),
    user_id       UInt64,

    -- Payload
    properties    String CODEC(ZSTD(3)),
    revenue       Decimal64(2) CODEC(Delta, ZSTD),
    country       LowCardinality(String),
    device        LowCardinality(String),

    -- Full-text search trên description
    description   String,
    INDEX idx_desc description TYPE full_text GRANULARITY 1
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_date)
ORDER BY (tenant_id, event_type, event_time, user_id)
TTL event_date + INTERVAL 90 DAY DELETE
SETTINGS
    index_granularity = 8192,
    min_bytes_for_wide_part = 10485760;

Nguyên tắc chọn ORDER BY

ORDER BY trong ClickHouse không chỉ sắp xếp — nó quyết định cấu trúc primary index và hiệu quả data skipping. Quy tắc: đặt cột có cardinality thấp nhất lên đầu (tenant_id, event_type), rồi mới đến cột cardinality cao (user_id). Cột time nên ở giữa nếu bạn thường query theo time range kết hợp filter khác. Sai ORDER BY có thể khiến query chậm gấp 100x trên cùng dataset.

3.2. Materialized Views — Tính toán trước, truy vấn tức thì

Materialized Views trong ClickHouse hoạt động như trigger: mỗi khi dữ liệu insert vào bảng nguồn, MV tự động transform và ghi vào bảng đích. Đây là cơ chế cốt lõi cho real-time aggregation.

-- Bảng đích cho aggregated metrics
CREATE TABLE hourly_metrics (
    hour         DateTime,
    tenant_id    UInt32,
    event_type   LowCardinality(String),
    event_count  AggregateFunction(count, UInt64),
    unique_users AggregateFunction(uniq, UInt64),
    total_revenue AggregateFunction(sum, Decimal64(2))
)
ENGINE = AggregatingMergeTree()
ORDER BY (tenant_id, event_type, hour);

-- Materialized View: auto-aggregate khi insert
CREATE MATERIALIZED VIEW mv_hourly_metrics
TO hourly_metrics AS
SELECT
    toStartOfHour(event_time) AS hour,
    tenant_id,
    event_type,
    countState() AS event_count,
    uniqState(user_id) AS unique_users,
    sumState(revenue) AS total_revenue
FROM analytics_events
GROUP BY hour, tenant_id, event_type;

-- Query: merge kết quả aggregate (sub-millisecond trên pre-aggregated data)
SELECT
    hour,
    countMerge(event_count) AS events,
    uniqMerge(unique_users) AS users,
    sumMerge(total_revenue) AS revenue
FROM hourly_metrics
WHERE tenant_id = 42
  AND hour >= now() - INTERVAL 24 HOUR
GROUP BY hour
ORDER BY hour;

3.3. Streaming Ingestion từ Kafka

ClickHouse có Kafka table engine built-in, cho phép consume trực tiếp từ Kafka topic mà không cần connector bên ngoài:

-- Kafka source table
CREATE TABLE kafka_events (
    event_time DateTime64(3),
    tenant_id  UInt32,
    event_type String,
    user_id    UInt64,
    properties String,
    revenue    Decimal64(2)
)
ENGINE = Kafka()
SETTINGS
    kafka_broker_list = 'kafka-1:9092,kafka-2:9092,kafka-3:9092',
    kafka_topic_list = 'analytics-events',
    kafka_group_name = 'clickhouse-consumer',
    kafka_format = 'JSONEachRow',
    kafka_num_consumers = 4,
    -- v26.2: time-based flushing thay vì chờ đủ rows
    kafka_flush_interval_ms = 5000;

-- Pipe từ Kafka → MergeTree qua Materialized View
CREATE MATERIALIZED VIEW mv_kafka_to_events
TO analytics_events AS
SELECT
    event_time,
    toDate(event_time) AS event_date,
    tenant_id,
    event_type,
    user_id,
    properties,
    revenue,
    JSONExtractString(properties, 'country') AS country,
    JSONExtractString(properties, 'device') AS device,
    '' AS description
FROM kafka_events;

4. ClickHouse so với các giải pháp khác

Tiêu chíClickHouseElasticsearchApache DruidBigQuery
Mô hìnhColumnar OLAPInverted IndexColumnar OLAPServerless OLAP
Nén dữ liệu5-15x (rất tốt)1.5-3x3-8xManaged
Query latencySub-secondSub-secondSub-secondSeconds
IngestionTriệu rows/secTrăm nghìn docs/secTriệu rows/secStreaming (limited)
SQL supportFull SQL + extensionsCó (limited)Có (Druid SQL)Full SQL
Full-text searchGA (v26.2)Native, matureKhôngLimited
JOIN performanceTốt (v26.2+)Rất hạn chếHạn chếTốt
Chi phí vận hànhThấp (self-host) / Medium (Cloud)Cao (RAM-hungry)Cao (complex ops)Pay-per-query
Phù hợp nhấtAnalytics, observability, time seriesSearch, loggingReal-time dashboardsAd-hoc analytics

5. Case Study thực tế 2026

5.1. Cloudflare — 1.61 Quadrillion Events/Ngày

Cloudflare sử dụng ClickHouse để xử lý 1.61 quadrillion events mỗi ngày (1.61 × 10¹⁵) trên hơn 300 data center toàn cầu. Một single query có thể quét 96 nghìn tỷ events trong dưới 2 giây. Kiến trúc: mỗi data center có local ClickHouse cluster, query được fan-out/merge qua distributed layer.

5.2. Goldsky — Blockchain Analytics 12x nhanh hơn

Goldsky (blockchain data infrastructure) chuyển từ pipeline Kafka/Avro sang đọc trực tiếp ClickHouse với Apache Arrow format, đạt 12x speedup cho historical backfill — từ 50K lên 600K rows/sec. Bí quyết: tận dụng columnar-to-columnar transfer (ClickHouse → Arrow) không cần serialize/deserialize từng row.

5.3. Hookdeck — Search latency từ 30s xuống 400ms

Hookdeck (webhook infrastructure) cần tìm kiếm trong hàng tỷ webhook payload. Trước ClickHouse: truy vấn full-text mất 30+ giây. Sau khi chuyển sang ClickHouse với kỹ thuật hash payload values vào typed bucket columns + iterative time-window scanning: latency giảm xuống dưới 400ms — nhanh hơn 60x.

6. Best Practices cho Production

6.1. Schema Design

Những sai lầm phổ biến

1. Dùng UUID làm primary key: UUID phân tán ngẫu nhiên → data skipping gần như vô dụng. Thay bằng composite key với cột cardinality thấp đứng trước.
2. Quá nhiều cột nhỏ: ClickHouse mở file descriptor cho mỗi cột × mỗi part. Bảng 500+ cột với nhiều parts gây overhead lớn. Gộp các trường liên quan vào Nested hoặc JSON.
3. INSERT từng row: Mỗi INSERT tạo một part mới → merge pressure khổng lồ. Luôn batch (tối thiểu 1.000 rows/batch, lý tưởng 10K-100K).
4. Thiếu TTL: Dữ liệu analytics tăng nhanh. Luôn set TTL theo retention policy để tự động xóa dữ liệu cũ.

6.2. Performance Tuning

-- Kiểm tra compression ratio
SELECT
    table,
    formatReadableSize(sum(data_compressed_bytes)) AS compressed,
    formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed,
    round(sum(data_uncompressed_bytes) / sum(data_compressed_bytes), 2) AS ratio
FROM system.columns
WHERE database = 'analytics'
GROUP BY table
ORDER BY sum(data_uncompressed_bytes) DESC;

-- Kiểm tra query đang quét bao nhiêu granules
SELECT
    query,
    read_rows,
    read_bytes,
    result_rows,
    formatReadableSize(memory_usage) AS peak_memory,
    query_duration_ms
FROM system.query_log
WHERE type = 'QueryFinish'
  AND query_duration_ms > 1000
ORDER BY query_duration_ms DESC
LIMIT 10;

-- Async insert (mặc định từ v26.3)
-- ClickHouse tự gom batch insert nhỏ thành batch lớn
SET async_insert = 1;
SET wait_for_async_insert = 0;  -- không chờ, fire-and-forget

6.3. clickhousectl — CLI mới cho thời đại Agent

ClickHouse vừa ra mắt clickhousectl (beta, 04/2026) — CLI chính thức hợp nhất quản lý local installation và cloud deployment. Điểm đặc biệt: thiết kế sẵn cho AI agent integration qua MCP server, cho phép AI agent truy vấn và quản lý ClickHouse cluster thông qua giao thức chuẩn.

# Cài đặt clickhousectl
curl -fsSL https://clickhouse.com/install-ctl | bash

# Quản lý local instance
clickhousectl install 26.3       # Cài phiên bản cụ thể
clickhousectl start              # Khởi động server
clickhousectl status             # Kiểm tra trạng thái

# Kết nối cloud
clickhousectl cloud login
clickhousectl cloud services     # Liệt kê cloud services
clickhousectl cloud query "SELECT count() FROM analytics_events"

# Scaffolding project mới
clickhousectl init my-analytics  # Tạo project template

7. Kết luận

ClickHouse đã chứng minh vị thế là database nhanh nhất cho real-time analytics ở quy mô từ startup đến Cloudflare (1.61 quadrillion events/ngày). Với phiên bản 26.x, ClickHouse không chỉ nhanh hơn mà còn thông minh hơn — full-text search GA loại bỏ nhu cầu Elasticsearch cho log analytics, materialized CTEs đơn giản hoá query phức tạp, WebAssembly UDFs mở rộng khả năng xử lý không giới hạn, và clickhousectl + MCP server chuẩn bị sẵn cho kỷ nguyên agentic AI.

Nếu hệ thống của bạn cần phân tích hàng tỷ dòng dữ liệu với latency dưới giây — cho dù là observability, product analytics, hay IoT time series — ClickHouse xứng đáng nằm trong shortlist đánh giá. Kết hợp với Kafka cho streaming ingestion, Materialized Views cho real-time aggregation, và Grafana cho visualization, bạn có một analytics stack hoàn chỉnh với chi phí vận hành thấp hơn đáng kể so với Elasticsearch hay các managed service đắt đỏ.

Nguồn tham khảo