ClickHouse — Cơ sở dữ liệu phân tích thời gian thực cho hệ thống quy mô lớn
Posted on: 4/25/2026 10:10:31 AM
Table of contents
- 1. Tại sao cần cơ sở dữ liệu dạng cột?
- 2. ClickHouse hoạt động như thế nào?
- 3. Các engine phái sinh từ MergeTree
- 4. Thực hành: Thiết kế schema cho hệ thống log analytics
- 5. Materialized Views — Pre-compute để tăng tốc
- 6. So sánh ClickHouse với các giải pháp phân tích khác
- 7. Kiến trúc triển khai production
- 8. Tối ưu hiệu suất truy vấn
- 9. ClickHouse Cloud và SharedMergeTree
- 10. Khi nào KHÔNG nên dùng ClickHouse?
- Kết luận
Khi hệ thống của bạn bắt đầu xử lý hàng tỷ sự kiện mỗi ngày — từ log truy cập, metric giám sát, đến dữ liệu clickstream — các cơ sở dữ liệu truyền thống bắt đầu "nghẹt thở". PostgreSQL, MySQL hay SQL Server đều được thiết kế cho OLTP (xử lý giao dịch), không phải cho việc scan hàng triệu dòng trong tích tắc. Đó chính là lúc ClickHouse xuất hiện — một cơ sở dữ liệu dạng cột (columnar) mã nguồn mở, được thiết kế từ đầu cho phân tích thời gian thực ở quy mô khổng lồ.
1. Tại sao cần cơ sở dữ liệu dạng cột?
Trong cơ sở dữ liệu dạng dòng (row-oriented) truyền thống, mỗi dòng được lưu liền kề trên disk. Khi bạn chạy truy vấn phân tích kiểu SELECT AVG(price) FROM orders WHERE created_at > '2026-01-01', database phải đọc toàn bộ mỗi dòng — bao gồm cả những cột bạn không cần (customer_name, address, phone...) — rồi mới trích xuất cột price.
Cơ sở dữ liệu dạng cột lật ngược logic này: dữ liệu của mỗi cột được lưu liên tiếp. Truy vấn trên chỉ cần đọc 2 cột (price và created_at), bỏ qua hoàn toàn phần còn lại. Với bảng 50 cột, bạn chỉ đọc 4% dữ liệu.
graph LR
subgraph ROW["Row-Oriented Storage"]
R1["Row 1: id|name|price|date|addr"]
R2["Row 2: id|name|price|date|addr"]
R3["Row 3: id|name|price|date|addr"]
end
subgraph COL["Column-Oriented Storage"]
C1["id: 1, 2, 3, 4, 5..."]
C2["name: A, B, C, D, E..."]
C3["price: 10, 20, 15, 30..."]
C4["date: 01, 02, 03, 04..."]
end
Q["SELECT AVG(price)
WHERE date > X"] --> COL
Q -.->|"Đọc toàn bộ row"| ROW
style ROW fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style COL fill:#e8f5e9,stroke:#4CAF50,color:#2c3e50
style Q fill:#e94560,stroke:#fff,color:#fff
So sánh cách đọc dữ liệu giữa row-store và column-store
2. ClickHouse hoạt động như thế nào?
2.1. Kiến trúc MergeTree Engine
Trái tim của ClickHouse là MergeTree engine family — engine lưu trữ được thiết kế cho việc ghi dữ liệu cực nhanh và truy vấn phân tích hiệu suất cao. Khi bạn INSERT dữ liệu, ClickHouse không chèn từng dòng một vào B-tree index như database truyền thống. Thay vào đó:
- Ghi thành "parts" bất biến (immutable): Mỗi batch INSERT tạo ra một "part" — một thư mục chứa dữ liệu đã sắp xếp theo primary key, mỗi cột là một file riêng.
- Merge nền (background merge): ClickHouse liên tục gộp các part nhỏ thành part lớn hơn trong background, giống cách LSM-tree hoạt động — nhưng tối ưu cho dạng cột.
- Sparse primary index: Thay vì index mọi dòng, ClickHouse chỉ lưu index cho mỗi 8.192 dòng (granule). Một bảng 1 tỷ dòng chỉ cần ~122.000 entry trong index — đủ nhỏ để fit trong RAM.
graph TD
INSERT["INSERT batch"] --> P1["Part 1
(sorted, immutable)"]
INSERT --> P2["Part 2"]
INSERT --> P3["Part 3"]
P1 --> MERGE["Background Merge"]
P2 --> MERGE
P3 --> MERGE
MERGE --> BIG["Merged Part
(larger, optimized)"]
BIG --> QUERY["Query Engine"]
QUERY --> IDX["Sparse Index
(1 entry / 8192 rows)"]
IDX --> SCAN["Column Scan
(chỉ đọc cột cần thiết)"]
style INSERT fill:#e94560,stroke:#fff,color:#fff
style MERGE fill:#2c3e50,stroke:#fff,color:#fff
style BIG fill:#16213e,stroke:#fff,color:#fff
style QUERY fill:#4CAF50,stroke:#fff,color:#fff
style IDX fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style SCAN fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
Luồng dữ liệu trong MergeTree Engine
2.2. Vectorized Query Execution
ClickHouse xử lý dữ liệu theo batch (chunk) thay vì từng dòng một — kỹ thuật được gọi là vectorized execution, lấy cảm hứng từ mô hình MonetDB/X100. Mỗi operator trong query pipeline nhận vào một block gồm hàng nghìn dòng, áp dụng phép tính lên toàn bộ block cùng lúc, tận dụng tối đa SIMD instructions (Single Instruction, Multiple Data) của CPU hiện đại.
Tại sao vectorized execution nhanh hơn?
Trong mô hình Volcano (row-at-a-time) truyền thống, mỗi dòng phải đi qua chuỗi virtual function calls. Với 1 tỷ dòng, đó là 1 tỷ lần gọi hàm ảo. Vectorized execution giảm xuống chỉ còn ~122.000 lần gọi (1 tỷ / 8.192) — giảm 99.99% overhead. Kết hợp với SIMD, CPU có thể xử lý 4-16 giá trị cùng lúc trong một instruction cycle.
2.3. Nén dữ liệu thông minh
Vì dữ liệu cùng cột có kiểu giống nhau và thường có pattern lặp lại, ClickHouse đạt tỷ lệ nén cực cao:
| Codec | Phù hợp với | Tỷ lệ nén |
|---|---|---|
LZ4 | Mặc định, cân bằng tốc độ và nén | 3-5x |
ZSTD | Dữ liệu cold, ưu tiên dung lượng | 5-10x |
Delta + ZSTD | Cột timestamp, giá trị tăng dần | 10-20x |
T64 | Cột integer có range nhỏ | 8-15x |
DoubleDelta | Chuỗi thời gian (time-series) | 15-40x |
Một bảng log 1TB trong PostgreSQL có thể chỉ còn 60-150GB trong ClickHouse — tiết kiệm đáng kể chi phí lưu trữ, đặc biệt trên cloud.
3. Các engine phái sinh từ MergeTree
ClickHouse không chỉ có MergeTree cơ bản — family này bao gồm nhiều biến thể phục vụ các use case khác nhau:
| Engine | Chức năng | Use Case |
|---|---|---|
ReplacingMergeTree | Tự động deduplicate dòng trùng primary key khi merge | CDC (Change Data Capture), upsert pattern |
AggregatingMergeTree | Pre-aggregate dữ liệu trong quá trình merge | Materialized views, metric rollup |
CollapsingMergeTree | Hỗ trợ update/delete logic bằng cách insert dòng "cancel" | Dữ liệu có trạng thái thay đổi |
SummingMergeTree | Tự cộng dồn giá trị cột numeric khi merge | Counter, revenue aggregation |
VersionedCollapsingMergeTree | Như Collapsing nhưng hỗ trợ insert không theo thứ tự | Distributed ingestion không đảm bảo order |
4. Thực hành: Thiết kế schema cho hệ thống log analytics
Giả sử bạn đang xây dựng hệ thống giám sát API với hàng trăm triệu request/ngày. Đây là cách thiết kế schema tối ưu trong ClickHouse:
CREATE TABLE api_logs
(
timestamp DateTime64(3),
date Date DEFAULT toDate(timestamp),
service LowCardinality(String),
endpoint LowCardinality(String),
method Enum8('GET' = 1, 'POST' = 2, 'PUT' = 3, 'DELETE' = 4, 'PATCH' = 5),
status_code UInt16,
response_time_ms UInt32,
request_size UInt32,
response_size UInt32,
user_id UInt64,
trace_id UUID,
error_message Nullable(String),
tags Map(String, String)
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(date)
ORDER BY (service, endpoint, timestamp)
TTL date + INTERVAL 90 DAY DELETE
SETTINGS index_granularity = 8192;
Những nguyên tắc thiết kế quan trọng
- LowCardinality: Dùng cho cột string có ít giá trị unique (service name, endpoint). Tạo dictionary encoding tự động, giảm 5-10x dung lượng và tăng tốc filter.
- Enum8/Enum16: Cho cột có tập giá trị cố định (HTTP method). Chỉ lưu 1-2 byte thay vì chuỗi ký tự.
- ORDER BY: Xếp theo thứ tự từ cardinality thấp → cao. Filter theo service + endpoint sẽ cực nhanh nhờ sparse index.
- PARTITION BY: Phân vùng theo tháng. TTL tự động xóa dữ liệu cũ hơn 90 ngày — không cần cron job.
- Map type: Linh hoạt cho metadata không cố định (tags, labels) mà không cần ALTER TABLE.
4.1. Truy vấn phân tích mẫu
-- Top 10 endpoint chậm nhất trong 24h qua
SELECT
service,
endpoint,
quantile(0.95)(response_time_ms) AS p95_ms,
quantile(0.99)(response_time_ms) AS p99_ms,
count() AS total_requests,
countIf(status_code >= 500) AS errors,
round(countIf(status_code >= 500) * 100.0 / count(), 2) AS error_rate
FROM api_logs
WHERE timestamp >= now() - INTERVAL 24 HOUR
GROUP BY service, endpoint
ORDER BY p95_ms DESC
LIMIT 10;
-- Phân tích traffic pattern theo giờ (heatmap data)
SELECT
toHour(timestamp) AS hour,
toDayOfWeek(timestamp) AS day_of_week,
count() AS requests,
avg(response_time_ms) AS avg_latency
FROM api_logs
WHERE date >= today() - 7
GROUP BY hour, day_of_week
ORDER BY day_of_week, hour;
5. Materialized Views — Pre-compute để tăng tốc
Một trong những tính năng mạnh nhất của ClickHouse là Materialized Views — cho phép pre-aggregate dữ liệu ngay khi nó được insert, không cần ETL pipeline riêng.
-- Tạo bảng đích cho metric tổng hợp mỗi phút
CREATE TABLE api_metrics_1m
(
timestamp DateTime,
service LowCardinality(String),
endpoint LowCardinality(String),
requests AggregateFunction(count, UInt64),
p95_latency AggregateFunction(quantile(0.95), UInt32),
errors AggregateFunction(sum, UInt64)
)
ENGINE = AggregatingMergeTree()
ORDER BY (service, endpoint, timestamp);
-- Materialized view tự động aggregate khi có INSERT
CREATE MATERIALIZED VIEW api_metrics_1m_mv
TO api_metrics_1m AS
SELECT
toStartOfMinute(timestamp) AS timestamp,
service,
endpoint,
countState() AS requests,
quantileState(0.95)(response_time_ms) AS p95_latency,
sumState(if(status_code >= 500, 1, 0)) AS errors
FROM api_logs
GROUP BY timestamp, service, endpoint;
Hiệu quả thực tế
Truy vấn trên bảng gốc api_logs (1 tỷ dòng): ~2-5 giây. Cùng truy vấn trên api_metrics_1m (chỉ chứa dữ liệu đã aggregate): ~10-50ms. Tăng tốc 100-500 lần, không tốn thêm công sức ETL.
6. So sánh ClickHouse với các giải pháp phân tích khác
| Tiêu chí | ClickHouse | PostgreSQL | Elasticsearch |
|---|---|---|---|
| Kiểu lưu trữ | Columnar (OLAP) | Row-oriented (OLTP) | Inverted Index + Doc Store |
| Tốc độ truy vấn phân tích | ⚡ Cực nhanh (~ms trên tỷ dòng) | 🐢 Chậm (phút-giờ trên tỷ dòng) | 🔄 Trung bình (giây-phút) |
| Tốc độ INSERT | Hàng triệu dòng/giây | Hàng nghìn dòng/giây | Hàng chục nghìn docs/giây |
| Nén dữ liệu | Xuất sắc (6-20x) | Trung bình (2-3x) | Kém (1.5-3x) |
| Full-text search | Cơ bản (bloom filter) | Tốt (tsvector) | Xuất sắc (inverted index) |
| UPDATE/DELETE | Hạn chế (async mutation) | Xuất sắc (MVCC) | Tốt (doc-level) |
| Chi phí vận hành | Thấp (ít RAM, ít storage) | Trung bình | Cao (RAM-heavy) |
| SQL support | Rất đầy đủ | Đầy đủ nhất | Hạn chế (DSL-first) |
7. Kiến trúc triển khai production
Một hệ thống analytics production thường kết hợp ClickHouse với các thành phần khác theo mô hình Kappa architecture:
graph LR
APP["Application
Services"] --> KAFKA["Apache Kafka
/ Redpanda"]
KAFKA --> CH["ClickHouse
Cluster"]
CH --> GRAFANA["Grafana
Dashboard"]
CH --> API["Analytics API
(.NET / Node.js)"]
API --> UI["Frontend
(Vue.js)"]
CH --> MV["Materialized
Views"]
MV --> CH
KAFKA --> REDIS["Redis
(real-time cache)"]
REDIS --> API
style APP fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style KAFKA fill:#2c3e50,stroke:#fff,color:#fff
style CH fill:#e94560,stroke:#fff,color:#fff
style GRAFANA fill:#16213e,stroke:#fff,color:#fff
style API fill:#4CAF50,stroke:#fff,color:#fff
style UI fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
style MV fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style REDIS fill:#ff9800,stroke:#fff,color:#fff
Kiến trúc Kappa với ClickHouse làm trung tâm phân tích
Giải thích luồng dữ liệu
- Ingestion: Application services gửi event vào Kafka/Redpanda. Không ghi trực tiếp vào ClickHouse để tránh nhiều INSERT nhỏ (anti-pattern).
- Batch insert: ClickHouse Kafka engine hoặc consumer service đọc batch từ Kafka, insert theo block lớn (10.000-100.000 dòng/batch) — đây là cách tối ưu nhất.
- Pre-aggregation: Materialized Views tự động tổng hợp dữ liệu real-time khi insert, phục vụ dashboard nhanh hơn.
- Query layer: Grafana kết nối trực tiếp (plugin chính thức). API layer phục vụ custom analytics cho frontend.
- Hot cache: Redis cache kết quả truy vấn thường xuyên, giảm tải cho ClickHouse ở concurrent queries cao.
8. Tối ưu hiệu suất truy vấn
8.1. Nguyên tắc vàng: Đọc ít dữ liệu hơn
ClickHouse nhanh nhất khi nó đọc ít dữ liệu nhất. Mọi tối ưu đều xoay quanh nguyên tắc này:
Checklist tối ưu truy vấn
- Filter trước, transform sau: Luôn đặt WHERE clause lọc theo cột trong ORDER BY key. ClickHouse dùng sparse index để skip toàn bộ granule không liên quan.
- Dùng PREWHERE: Cho cột filter nhỏ. ClickHouse đọc cột PREWHERE trước, loại bỏ dòng không cần, rồi mới đọc các cột còn lại — giảm I/O đáng kể.
- Tránh SELECT *: Chỉ chọn cột cần thiết. Mỗi cột thêm vào = thêm I/O.
- Projection: Tạo physical sort order phụ cho truy vấn với pattern filter khác PRIMARY KEY.
- Skip index: Bloom filter index cho cột high-cardinality (trace_id, user_id) không nằm trong ORDER BY.
8.2. Ví dụ PREWHERE và Skip Index
-- Thêm bloom filter index cho trace_id
ALTER TABLE api_logs
ADD INDEX idx_trace_id trace_id TYPE bloom_filter(0.01) GRANULARITY 4;
-- ClickHouse tự động chuyển filter nhẹ sang PREWHERE
-- Nhưng có thể force nếu cần:
SELECT timestamp, endpoint, response_time_ms, error_message
FROM api_logs
PREWHERE service = 'payment-service'
WHERE timestamp >= now() - INTERVAL 1 HOUR
AND status_code >= 500;
9. ClickHouse Cloud và SharedMergeTree
ClickHouse Cloud (dịch vụ managed) giới thiệu SharedMergeTree — engine mới tách biệt hoàn toàn storage và compute:
| Đặc điểm | MergeTree (self-hosted) | SharedMergeTree (Cloud) |
|---|---|---|
| Storage | Local disk hoặc attached volume | Object storage (S3/GCS) |
| Compute scaling | Phải rebalance data khi thêm node | Thêm stateless node, query ngay |
| Merge process | Chạy trên query node, ảnh hưởng latency | Chạy trên node riêng, không ảnh hưởng query |
| Chi phí | Trả cho server 24/7 | Pay-per-query + storage |
| Phù hợp | Workload ổn định, muốn kiểm soát | Workload biến động, muốn tối ưu chi phí |
10. Khi nào KHÔNG nên dùng ClickHouse?
ClickHouse không phải silver bullet
- Ứng dụng OLTP: Cần UPDATE/DELETE thường xuyên, transaction ACID → dùng PostgreSQL, SQL Server.
- Full-text search phức tạp: Cần relevance scoring, fuzzy matching → dùng Elasticsearch/OpenSearch.
- Dữ liệu nhỏ (<100GB): PostgreSQL với index tốt đã đủ nhanh, không cần thêm complexity.
- JOIN nhiều bảng lớn: ClickHouse hỗ trợ JOIN nhưng không tối ưu bằng RDBMS truyền thống cho complex join patterns.
- Point lookups: Truy vấn lấy 1 dòng theo primary key — row-store nhanh hơn.
Kết luận
ClickHouse đã chứng minh vị thế là giải pháp hàng đầu cho phân tích dữ liệu thời gian thực ở quy mô lớn. Với kiến trúc columnar, vectorized execution, và hệ thống nén thông minh, nó cho phép xử lý hàng tỷ dòng dữ liệu trong vài mili-giây — điều mà các database truyền thống cần hàng phút hoặc hàng giờ. Khi kết hợp với Kafka để ingestion, Redis để caching, và Grafana để visualization, ClickHouse trở thành trung tâm của một kiến trúc analytics hiện đại, đáp ứng nhu cầu observability và business intelligence ngày càng phức tạp của các hệ thống phần mềm.
Tham khảo
WebGPU — Kỷ Nguyên Mới Của GPU Computing Trên Trình Duyệt
Structured Logging trong .NET 10: Từ Console.WriteLine đến Hệ thống Log chuyên nghiệp với Serilog
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.