ClickHouse 1 — Tổng quan & định vị OLAP thời gian thực

13 thg 7, 2026 2 lượt xem
#sql
#olap
#clickhouse
#column-store
#real-time-analytics

ClickHouse 1 — Tổng quan & định vị OLAP thời gian thực

Hình dung một dashboard giám sát giao dịch của ngân hàng: 400 triệu dòng log giao dịch mỗi ngày, và cán bộ vận hành cần biết ngay "trong 5 phút vừa rồi, tỷ lệ giao dịch lỗi theo từng kênh (ATM, mobile, POS) là bao nhiêu". Nếu bạn ném câu hỏi đó vào PostgreSQL đang chứa cả năm dữ liệu, nó phải quét hàng tỷ dòng, đọc mọi cột dù chỉ cần vài cột, và bạn ngồi chờ hàng phút. ClickHouse sinh ra chính xác cho tình huống này: trả lời truy vấn phân tích quét tỷ dòng trong mili-giây tới vài giây, ngay cả khi dữ liệu vẫn đang được nạp vào liên tục.

Bài này là nền móng của cả series. Mục tiêu không phải dạy bạn viết truy vấn nhanh ngay, mà dựng một mô hình tinh thần đúng: ClickHouse là loại database nào, giải nó cho bài toán gì, và quan trọng không kém — nó không giải bài toán gì. Có mô hình đó rồi, các bài sau — kiến trúc & lưu trữ, họ engine MergeTree, ingest & schema, truy vấn & materialized view, triển khai phân tán, hiệu năng & vận hành, use case ngân hàng — sẽ ăn khớp thay vì rời rạc.

Lưu ý về sandbox: SQL sandbox của Knowledge Base chạy trên PostgreSQL, KHÔNG phải ClickHouse. ClickHouse dùng phương ngữ SQL riêng (kiểu dữ liệu UInt64, hàm như uniqExact, cú pháp ENGINE = MergeTree...), nên mọi khối code ClickHouse trong bài này không chạy được ở đây — chúng chỉ mang tính minh hoạ và được ghi rõ "(minh hoạ — ClickHouse)".

ClickHouse là gì

ClickHouse là hệ quản trị cơ sở dữ liệu cột (columnar DBMS) hướng phân tích, mã nguồn mở, do Yandex phát triển ban đầu (2016) để phục vụ hệ thống web analytics quy mô hàng nghìn tỷ dòng, sau đó tách thành công ty độc lập ClickHouse, Inc. Ghép các tính từ đó lại sẽ ra bản chất:

  • Phân tích (analytical / OLAP): ClickHouse sinh ra để đọc và tổng hợp khối lượng dữ liệu khổng lồ — đếm, tính tổng, gom nhóm, tính phân vị, uniq (số phần tử phân biệt) — chứ không phải phục vụ hàng chục nghìn giao dịch nhỏ cập nhật từng dòng mỗi giây như một database OLTP.
  • Cột (columnar): dữ liệu lưu theo cột thay vì theo dòng. Khi bạn tính SUM(amount) trên bảng 200 cột, ClickHouse chỉ đọc đúng cột amount, bỏ qua 199 cột còn lại. Đây là chìa khoá của tốc độ.
  • Thời gian thực (real-time): dữ liệu vừa nạp vào có thể truy vấn gần như tức thì; ClickHouse thiết kế để vừa nạp hàng triệu dòng/giây vừa phục vụ truy vấn với độ trễ thấp — điểm khác biệt lớn so với các data warehouse dạng batch.
  • Mã nguồn mở & self-hosted: phát hành theo giấy phép Apache 2.0, chạy được trên máy chủ tự quản lý (on-premise) hoặc cloud, không khoá bạn vào một nhà cung cấp. (Có cả bản quản lý ClickHouse Cloud, nhưng lõi vẫn open-source.)

Điểm cần khắc cốt: ClickHouse không phải bản thay thế cho PostgreSQL hay MySQL. Nó là một loại công cụ khác, giải một lớp bài toán khác. Muốn hiểu vì sao, phải bắt đầu từ sự khác biệt OLTP vs OLAP.

OLTP vs OLAP: hai thế giới workload

Hầu hết database "kinh điển" (PostgreSQL, MySQL, Oracle, SQL Server) tối ưu cho OLTP — Online Transaction Processing: xử lý giao dịch trực tuyến. Đặc trưng workload OLTP:

  • Rất nhiều truy vấn nhỏ, mỗi truy vấn chạm ít dòng (đọc/ghi một tài khoản, cập nhật số dư một khách hàng).
  • Ghi/sửa/xoá thường xuyên, đòi hỏi transaction ACID (nguyên tử, nhất quán, cô lập, bền vững) để không sai lệch tiền bạc.
  • Đọc theo khoá chính: "lấy tài khoản có id = 12345" — cần chỉ mục (index) để nhảy thẳng tới dòng.

Ngược lại, OLAP — Online Analytical Processing là xử lý phân tích. Đặc trưng:

  • Ít truy vấn hơn nhưng mỗi truy vấn quét rất nhiều dòng (hàng triệu tới hàng tỷ) rồi tổng hợp lại thành vài trăm dòng kết quả.
  • Ghi chủ yếu dạng append (thêm log mới theo thời gian), hiếm khi sửa/xoá dòng cũ.
  • Đọc theo một vài cột trên rất nhiều dòng: "tổng doanh số theo tỉnh trong 12 tháng qua".

Bảng đối chiếu cho gọn:

Tiêu chíOLTP (Postgres/MySQL)OLAP (ClickHouse)
Đơn vị truy vấn điển hình1 dòng / vài dòngTriệu → tỷ dòng
Số cột truy cập mỗi queryNhiều (cả dòng)Ít (vài cột)
Mẫu ghiInsert/Update/Delete lẻBulk insert (append)
Transaction ACID đầy đủCó, bắt buộcKhông (hoặc rất hạn chế)
Tổ chức lưu trữTheo dòng (row-store)Theo cột (column-store)
Chỉ mục điển hìnhB-tree trên khoáSparse index theo khoá sắp xếp
Ví dụ câu hỏi"Số dư tài khoản X?""Doanh số theo kênh 90 ngày?"

Vì sao Postgres/MySQL không tối ưu cho quét hàng tỷ dòng

Không phải Postgres "kém" — nó được tối ưu cho việc khác. Ba lý do kỹ thuật:

  1. Lưu theo dòng. Postgres lưu cả dòng liền nhau trên đĩa. Muốn tính SUM(amount) trên 1 tỷ dòng, nó phải đọc cả dòng (mọi cột) vào bộ nhớ rồi mới rút ra cột amount — lãng phí I/O gấp nhiều lần lượng dữ liệu thực sự cần.
  2. Nén kém hiệu quả cho analytic. Dữ liệu cùng cột (ví dụ cột currency chỉ có "VND", "USD"...) nếu nằm rải rác trong các dòng thì khó nén; lưu theo cột thì các giá trị giống nhau nằm liền kề, nén cực tốt.
  3. Chi phí MVCC & xử lý từng dòng. Cơ chế MVCC (multi-version concurrency control) của Postgres — vốn tuyệt vời cho transaction đồng thời — tạo overhead khi phải quét khối lượng lớn chỉ để đọc. Postgres cũng xử lý dữ liệu theo mô hình "một dòng một lúc" (row-at-a-time) trong nhiều đường thực thi.

Để cảm nhận, hãy so hai câu hỏi. Câu OLTP điển hình mà Postgres làm rất tốt (câu này chạy được trong sandbox Postgres của chúng ta):

-- ▶ Chạy được
SELECT id, account_no, balance
FROM accounts
WHERE id = 42;

Còn câu OLAP điển hình — gom nhóm trên toàn bảng — chính là loại ClickHouse ăn đứt Postgres khi bảng lên tới tỷ dòng (câu này cũng chạy được vì đúng schema sandbox, nhưng bạn hãy tưởng tượng transactions có 2 tỷ dòng):

-- ▶ Chạy được
SELECT kind, COUNT(*) AS so_giao_dich, SUM(amount) AS tong_tien
FROM transactions
GROUP BY kind
ORDER BY tong_tien DESC;

Trên vài nghìn dòng, cả hai đều nhanh. Nhưng khi transactions lên tỷ dòng, câu thứ hai trên Postgres phải quét toàn bảng theo dòng; ClickHouse chỉ đọc đúng hai cột kindamount đã nén sẵn, xử lý theo lô vector — chênh lệch có thể là hàng chục tới hàng trăm lần.

Lưu trữ cột & vector execution: vì sao nhanh

Đây là trái tim của ClickHouse. Hai kỹ thuật cộng hưởng: column-store (lưu theo cột) và vectorized execution (thực thi theo vector/lô).

Row-store vs column-store

Với một bảng transactions(id, account_id, amount, kind, created_at), row-store lưu tuần tự từng dòng:

[1, 501, 250000, 'debit', 10:00]  [2, 502, 90000, 'credit', 10:01]  ...

Column-store tách mỗi cột thành một luồng riêng trên đĩa:

id:         [1, 2, 3, ...]
account_id: [501, 502, 503, ...]
amount:     [250000, 90000, 120000, ...]
kind:       ['debit', 'credit', 'debit', ...]
created_at: [10:00, 10:01, 10:02, ...]

Sơ đồ dưới minh hoạ hai cách tổ chức và hệ quả khi chạy một truy vấn chỉ cần cột amount:

Ba lợi ích trực tiếp của column-store cho OLAP:

  • Ít I/O: truy vấn chỉ đọc đúng cột nó cần. Bảng 200 cột, query chạm 3 cột → đọc ~1.5% dữ liệu.
  • Nén mạnh: giá trị cùng cột thường lặp lại hoặc gần nhau (thời gian tăng dần, mã tiền tệ ít giá trị), nên các thuật toán như LZ4, ZSTD, delta-encoding, dictionary-encoding cho tỷ lệ nén rất cao — vừa tiết kiệm đĩa vừa giảm I/O.
  • Cục bộ CPU tốt: dữ liệu cùng kiểu nằm liền nhau, hợp với cache CPU và xử lý theo lô.

Vectorized execution

Thay vì xử lý một dòng một lúc như engine OLTP truyền thống, ClickHouse xử lý dữ liệu theo khối (block) hàng nghìn giá trị cùng lúc. Một khối là một mảng giá trị của cùng một cột. Engine áp dụng phép toán (cộng, so sánh, lọc) lên cả mảng trong một vòng lặp chặt, tận dụng lệnh SIMD (Single Instruction Multiple Data) của CPU hiện đại — một lệnh CPU cộng cùng lúc 8 hay 16 số. Hệ quả: chi phí "phụ trợ" (gọi hàm, kiểm tra kiểu, nhảy nhánh) chia đều trên cả khối thay vì trả cho từng dòng.

Kết hợp lại: ít dữ liệu phải đọc (columnar + nén) × xử lý cực nhanh phần đọc được (vectorized + SIMD) = truy vấn aggregate trên tỷ dòng chạy trong mili-giây tới giây. Đó là toàn bộ "phép màu" của ClickHouse, không có gì huyền bí.

Một câu aggregate minh hoạ bằng phương ngữ ClickHouse (không chạy trong sandbox Postgres):

-- (minh hoạ — ClickHouse)
SELECT
    channel,
    count()                              AS total,
    countIf(status = 'FAILED')           AS failed,
    round(failed / total * 100, 2)       AS fail_pct,
    uniqExact(customer_id)               AS uniq_customers
FROM txn_log
WHERE event_time >= now() - INTERVAL 5 MINUTE
GROUP BY channel
ORDER BY fail_pct DESC;

count(), countIf(), uniqExact() là hàm phương ngữ ClickHouse — không tồn tại trong Postgres, nên tuyệt đối không đánh dấu chạy được.

Định vị: ClickHouse đứng ở đâu

Đừng nghĩ theo kiểu "cái nào tốt hơn" mà nghĩ theo "mỗi cái giải bài toán gì". So ClickHouse với ba nhóm:

So với PostgreSQL (OLTP)

Đây không phải cạnh tranh mà là bổ sung. Postgres là "sổ cái nguồn sự thật" (system of record): lưu số dư, khách hàng, giao dịch với ACID nghiêm ngặt. ClickHouse là "kính hiển vi phân tích": nhận bản sao dữ liệu (thường qua CDC — Change Data Capture, hoặc stream từ Kafka) và trả lời câu hỏi tổng hợp. Kiến trúc phổ biến: Postgres ghi giao dịch → stream sang ClickHouse → dashboard/phân tích đọc từ ClickHouse, tách hoàn toàn tải phân tích khỏi hệ thống giao dịch để không làm chậm nghiệp vụ. Xem thêm kiến trúc PostgreSQL để hiểu phía OLTP.

So với BigQuery / Snowflake (cloud data warehouse)

BigQuery và Snowflake cũng là OLAP columnar, nhưng thiên về mô hình serverless / cloud-managed, tách lưu trữ khỏi tính toán, tối ưu cho truy vấn batch/ad-hoc trên khối dữ liệu khổng lồ, tính tiền theo dữ liệu quét hoặc theo credit tính toán. Chúng mạnh khi bạn muốn "không quản lý gì cả" và chấp nhận độ trễ vài giây tới chục giây cho báo cáo lớn. Xem tổng quan BigQuery.

ClickHouse định vị khác: độ trễ cực thấp cho từng truy vấn (interactive, dưới giây), throughput ingest thời gian thực rất cao, và self-hosted nếu bạn cần kiểm soát hạ tầng/chi phí/tuân thủ (rất quan trọng với ngân hàng, khi dữ liệu nhạy cảm phải nằm on-premise). Đổi lại, bạn (hoặc team) phải vận hành cụm — không "serverless" như BigQuery/Snowflake.

PostgreSQLClickHouseBigQuery / Snowflake
Loại workloadOLTPOLAP real-timeOLAP batch/warehouse
Lưu trữRow-storeColumn-storeColumn-store
Transaction ACIDĐầy đủRất hạn chếHạn chế
Độ trễ query phân tíchCao trên bảng lớnRất thấp (sub-second)Thấp–trung (giây)
Mô hình vận hànhSelf-managedSelf-hosted / CloudServerless managed
Điểm mạnh nhấtGiao dịch, tính nhất quánDashboard/log/metrics real-timeDW quét petabyte, ít vận hành

Khi nào NÊN và KHÔNG NÊN dùng ClickHouse

NÊN dùng khi:

  • Cần dashboard/analytics độ trễ thấp trên khối dữ liệu lớn, dữ liệu vẫn đang nạp liên tục.
  • Xử lý log, metrics, sự kiện, observability — dữ liệu dạng append theo thời gian, lượng ghi khổng lồ.
  • Truy vấn chủ yếu là aggregate / group-by / filter theo thời gian trên vài cột.
  • Cần self-hosted vì lý do chi phí, kiểm soát, hoặc tuân thủ dữ liệu nhạy cảm.

KHÔNG NÊN dùng khi:

  • Workload là OLTP: nhiều giao dịch nhỏ, đọc/ghi theo khoá chính, số dư phải chính xác tức thời → dùng Postgres/MySQL.
  • Cần update/delete điểm thường xuyên (sửa một dòng cụ thể liên tục). ClickHouse hỗ trợ mutation (ALTER ... UPDATE/DELETE) nhưng đây là thao tác nặng, bất đồng bộ, ghi lại cả phần dữ liệu (part) — không phải update nhẹ như OLTP.
  • Cần transaction đa câu lệnh, ACID đầy đủ, ràng buộc khoá ngoại. ClickHouse không thiết kế cho việc này.
  • Cần JOIN nhiều bảng lớn phức tạp thường xuyên với ràng buộc quan hệ chặt — ClickHouse làm được nhưng thường ưu tiên denormalize (gộp phẳng dữ liệu) hơn là chuẩn hoá quan hệ.

Quy tắc ghi nhớ: nếu câu hỏi bắt đầu bằng "tổng/đếm/tỷ lệ... theo... trong khoảng thời gian..." → ClickHouse. Nếu bắt đầu bằng "cập nhật/lấy đúng dòng của..." → Postgres.

Use case thực tế

Bối cảnh: NCB muốn một dashboard giám sát giao dịch thời gian thực cho trung tâm vận hành, hiển thị tỷ lệ giao dịch lỗi theo kênh, cập nhật mỗi 10 giây, trên khối lượng ~400 triệu bản ghi log giao dịch/ngày (ATM, Internet Banking, Mobile, POS, chuyển khoản liên ngân hàng).

Vấn đề với hạ tầng cũ: log giao dịch ban đầu đổ vào một replica PostgreSQL. Câu truy vấn "tỷ lệ lỗi theo kênh 5 phút gần nhất" trên bảng tích luỹ hàng tỷ dòng mất 40–90 giây, và mỗi lần dashboard tự refresh lại làm tăng tải cho replica, ảnh hưởng cả các báo cáo khác. Không thể coi là "real-time".

Giải pháp với ClickHouse:

  1. Core banking ghi giao dịch vào PostgreSQL (nguồn sự thật, ACID). Đồng thời log sự kiện được đẩy qua Kafka sang ClickHouse dạng append.
  2. Bảng txn_log trong ClickHouse dùng engine MergeTree, sắp xếp theo (channel, event_time), lưu ~90 ngày gần nhất (dữ liệu cũ hơn hạ tầng khác lo).
  3. Materialized view tổng hợp sẵn số liệu theo cửa sổ 1 phút, để dashboard đọc bảng đã pre-aggregate thay vì quét thô.

Kết quả (minh hoạ điển hình của loại triển khai này): truy vấn dashboard xuống còn dưới 1 giây, ingest ~5.000 dòng/giây ổn định, dung lượng đĩa giảm mạnh nhờ nén (dữ liệu log nén 8–10 lần là phổ biến). Tải phân tích tách hẳn khỏi core banking. Từ đây các bài sau sẽ dựng chi tiết: chọn engine MergeTree, thiết kế schema & ingest, và materialized view.

Các con số ở phần "Kết quả" là minh hoạ theo bậc độ lớn thường gặp của loại use case này, không phải số đo chính thức của một hệ thống NCB cụ thể.

Ghi nhớ

  • ClickHouse là DBMS cột, OLAP, mã nguồn mở, tối ưu cho phân tích tốc độ cao trên tỷ dòng — không thay thế Postgres/MySQL.
  • OLTP (Postgres) = nhiều giao dịch nhỏ, ACID, đọc theo khoá; OLAP (ClickHouse) = ít truy vấn nhưng quét khối lớn, aggregate, ghi dạng append.
  • Postgres chậm cho analytic tỷ dòng vì lưu theo dòng (đọc thừa cột), nén kém, overhead MVCC/row-at-a-time.
  • Tốc độ ClickHouse = column-store (ít I/O + nén cao) × vectorized execution (SIMD, xử lý theo khối).
  • Định vị: bổ sung cho Postgres (OLTP nguồn sự thật); khác BigQuery/Snowflake ở chỗ độ trễ sub-second, real-time ingest, self-hosted.
  • NÊN dùng cho dashboard real-time, log/metrics/observability, aggregate theo thời gian. KHÔNG dùng cho OLTP, update/delete điểm, transaction ACID đa câu lệnh.
  • Trong ngân hàng: giám sát giao dịch, observability, real-time dashboard — tách tải phân tích khỏi core banking.
  • SQL ClickHouse là phương ngữ riêng (ENGINE = MergeTree, UInt64, uniqExact, countIf...), khác Postgres — không đánh dấu chạy được trong sandbox.

Bài viết liên quan

Cách index hoạt động (B-Tree), đọc EXPLAIN, seq scan vs index scan và mẫu tối ưu truy vấn.

13 thg 7, 2026 4

Khoá, ràng buộc, quan hệ và chuẩn hoá 1NF/2NF/3NF — thiết kế lược đồ đúng từ đầu.

13 thg 7, 2026 4

Kết nhiều bảng đúng cách: các loại JOIN, bẫy thường gặp và phép hợp tập.

13 thg 7, 2026 3

Truy vấn lồng, CTE (WITH), CTE đệ quy và hàm cửa sổ — vũ khí cho phân tích nâng cao.

13 thg 7, 2026 3