BigQuery 10 — Clustering (gom cụm)
Mô hình tinh thần: sắp dữ liệu để bỏ qua được nhiều nhất
Ở BigQuery — Phân vùng bảng (Partitioning) ta thấy ý tưởng cốt lõi để giảm bytes quét: đọc càng ít dữ liệu càng tốt. Partition làm điều đó bằng cách chia bảng thành các "ngăn kéo" tách biệt theo một cột (thường là ngày), rồi bỏ qua trọn ngăn không khớp WHERE.
Clustering đi theo cùng triết lý nhưng ở tầng mịn hơn và mềm dẻo hơn. Thay vì chia bảng thành các ngăn kéo cứng, clustering sắp xếp (sort) dữ liệu bên trong storage theo giá trị của tối đa 4 cột. Khi dữ liệu đã được gom lại — các dòng có cùng giá trị cluster nằm cạnh nhau trong cùng những block vật lý — BigQuery đọc metadata của mỗi block để biết block đó chứa khoảng giá trị nào, rồi bỏ qua (prune) những block chắc chắn không chứa giá trị bạn cần.
Hãy hình dung một quyển danh bạ điện thoại: nếu tên khách được xếp theo bảng chữ cái, muốn tìm "Nguyễn Văn A" bạn lật thẳng tới vần N, không đọc từ đầu tới cuối. Clustering làm đúng việc "xếp theo thứ tự" đó cho bảng BigQuery, còn block pruning là hành động "lật thẳng tới đúng chỗ".
Block, sort, và block pruning
BigQuery lưu dữ liệu theo định dạng cột (Capacitor trên Colossus) và chia mỗi bảng thành nhiều block — các khối dữ liệu vật lý. Khi bảng được clustering theo cột customer_id:
- Các dòng được sắp xếp theo
customer_idrồi ghi vào block, nên mỗi block chứa một khoảng giá trịcustomer_idliền nhau (ví dụ block 1: C0001–C0900, block 2: C0901–C1800…). - BigQuery lưu metadata min/max của cột cluster cho từng block.
- Khi query có
WHERE customer_id = 'C1500', engine đọc metadata, thấy chỉ block 2 chứa khoảng bao trùm C1500, nên chỉ đọc block 2 và bỏ qua tất cả block còn lại. Đó là block pruning.
Điểm mấu chốt: pruning chỉ hiệu quả khi dữ liệu đã được sắp. Nếu bảng không clustering, mỗi giá trị customer_id rải đều khắp mọi block, block nào cũng có khoảng min/max trải rộng toàn bộ → không block nào bị loại → phải quét cả bảng. Clustering "gom" các giá trị giống nhau lại để khoảng min/max của mỗi block hẹp và tách biệt, nhờ đó pruning mới cắt được nhiều.
Clustering khác Partitioning thế nào
Cả hai đều giảm bytes quét bằng cách bỏ bớt dữ liệu, nhưng cơ chế và đặc tính khác hẳn:
| Partitioning | Clustering | |
|---|---|---|
| Cơ chế | Chia bảng thành partition tách biệt (theo ngày/số nguyên/ingestion time) | Sắp xếp dữ liệu theo giá trị cột, gom vào block |
| Số cột | 1 cột phân vùng | Tối đa 4 cột (theo thứ tự) |
| Kiểu cột phù hợp | DATE/TIMESTAMP/DATETIME, integer range | Bất kỳ cột filter/aggregate/join thường dùng, kể cả STRING |
| Cardinality | Nên thấp–vừa (số partition có giới hạn ~10.000/bảng) | Nên cao (high-cardinality) — nhiều giá trị khác nhau |
| Ranh giới cắt | Cứng: partition là biên rõ ràng | Mềm: theo khoảng min/max của block, phụ thuộc độ sắp |
| Ước lượng bytes trước khi chạy | Dry-run cho con số chính xác | Dry-run không ước lượng được lợi ích pruning (biết sau khi chạy) |
| Quản lý vòng đời | Có: partition expiration, thao tác theo partition, cost control theo partition | Không có expiration/thao tác riêng theo cluster |
| Quota | Tối đa ~4.000 partition/bảng (giới hạn cứng) | Không thêm quota partition |
Hai khác biệt thực dụng cần nhớ:
- Cardinality ngược nhau. Partition thích cột ít giá trị (mỗi ngày một partition, vài nghìn ngày là cùng). Clustering thích cột nhiều giá trị như
customer_id,account_number,merchant_id— chính vì nhiều giá trị nên sắp lại mới gom được thành các block tách biệt để prune. - Dry-run không đo được lợi ích clustering. Với partition, dry-run báo chính xác bytes sẽ quét. Với clustering, ước lượng bytes trước khi chạy không phản ánh phần block sẽ bị prune — bạn chỉ biết
total_bytes_processedthực sau khi chạy. Đây là lý do khi tối ưu chi phí, partition cho sự chắc chắn còn clustering cho phần giảm thêm "không cam kết trước".
Kết hợp Partition + Cluster — bộ đôi mạnh nhất
Hai kỹ thuật không loại trừ nhau mà bổ sung nhau, và trong thực tế bảng lớn tốt nhất thường dùng cả hai: partition theo cột thời gian (thô, cắt theo ngày) rồi clustering theo cột nghiệp vụ high-cardinality (mịn, cắt trong từng ngày).
Cơ chế chạy hai tầng: BigQuery prune partition trước (bỏ mọi ngày trừ 2026-06-15), rồi prune block sau bên trong partition còn lại (bỏ mọi block trừ block chứa C1500). Một filter chạm cả hai chiều — thời gian và khách hàng — sẽ được cắt liên tiếp hai lần, để lại lượng dữ liệu quét nhỏ nhất có thể. Các kỹ thuật giảm bytes khác (chọn cột, denormalize, materialized view) xem BigQuery — Kỹ thuật tối ưu truy vấn.
Thứ tự cột cluster RẤT quan trọng
Clustering theo nhiều cột không đối xứng: BigQuery sắp dữ liệu theo thứ tự cột bạn khai báo, giống ORDER BY col1, col2, col3 — cột đầu là khóa sắp chính, cột sau chỉ sắp trong phạm vi giá trị cột trước bằng nhau. Hệ quả là pruning chỉ tận dụng được từ TRÁI sang PHẢI, không bỏ cách được.
Giả sử bảng CLUSTER BY region, branch_id, customer_id:
| Filter trong query | Pruning tận dụng được? |
|---|---|
WHERE region = 'North' | Tốt — dùng cột cluster đầu tiên |
WHERE region = 'North' AND branch_id = 42 | Rất tốt — dùng tiền tố region rồi branch_id |
WHERE region = 'North' AND branch_id = 42 AND customer_id = 'C1500' | Tối ưu — dùng cả 3 theo đúng thứ tự |
WHERE branch_id = 42 (thiếu region) | Kém/không prune — nhảy cóc qua cột đầu |
WHERE customer_id = 'C1500' (thiếu 2 cột trước) | Không prune theo cluster |
Nguyên tắc thiết kế: đặt cột được filter thường xuyên nhất (và thường theo bình đẳng =) lên đầu, theo sau là các cột lọc thứ cấp, giảm dần theo tần suất dùng trong WHERE/JOIN/GROUP BY. Đặt sai thứ tự khiến phần lớn query không hưởng lợi dù bảng vẫn "có clustering".
Automatic reclustering — BigQuery tự dọn nền
Khi bạn ghi thêm dữ liệu (INSERT, streaming, load) vào bảng đã clustering, dữ liệu mới ban đầu nằm ở các block chưa được sắp chung với dữ liệu cũ — mức độ "gom cụm" suy giảm dần, block mới có khoảng min/max chồng lấn nhau khiến pruning kém đi.
BigQuery xử lý bằng automatic reclustering: một tiến trình nền tự động sắp xếp lại dữ liệu trong các block để khôi phục độ gom cụm, hoàn toàn tự động và miễn phí — không tính vào bytes billed hay slot của bạn, không cần bảo trì thủ công, không làm gián đoạn query đang chạy. Bạn chỉ khai báo CLUSTER BY một lần lúc tạo bảng; việc giữ cho dữ liệu luôn "được sắp" là do BigQuery lo. Đây là khác biệt lớn so với nhiều hệ khác (như phải chạy OPTIMIZE/compaction thủ công): ở BigQuery clustering gần như "cài đặt xong rồi quên".
Viết SQL: tạo và dùng bảng clustered
-- Tạo bảng vừa PARTITION theo ngày, vừa CLUSTER theo 3 cột high-cardinality.
-- Thứ tự cluster: region → branch_id → customer_id (lọc từ thô đến mịn).
CREATE TABLE `ncb-dwh.core.transactions_clustered`
(
txn_id STRING,
txn_date DATE,
region STRING,
branch_id INT64,
customer_id STRING,
merchant_id STRING,
amount NUMERIC,
risk_flag BOOL
)
PARTITION BY txn_date
CLUSTER BY region, branch_id, customer_id
AS
SELECT txn_id, txn_date, region, branch_id, customer_id, merchant_id, amount, risk_flag
FROM `ncb-dwh.core.transactions`;
-- Query hưởng lợi từ CẢ hai: partition pruning theo txn_date,
-- rồi block pruning theo tiền tố cluster (region, branch_id, customer_id).
SELECT customer_id, SUM(amount) AS total_spent, COUNT(*) AS n_txn
FROM `ncb-dwh.core.transactions_clustered`
WHERE txn_date BETWEEN '2026-06-01' AND '2026-06-30' -- cắt partition
AND region = 'North' -- cột cluster 1
AND branch_id = 42 -- cột cluster 2
GROUP BY customer_id
ORDER BY total_spent DESC
LIMIT 100;
-- Thêm clustering cho bảng đã tồn tại (không cần tạo lại):
ALTER TABLE `ncb-dwh.core.transactions_clustered`
SET OPTIONS (
clustering_fields = ['region', 'branch_id', 'customer_id']
);
-- Lưu ý: ALTER chỉ áp cho dữ liệu ghi MỚI sau lệnh này;
-- muốn sắp lại dữ liệu cũ, chạy một câu ghi lại (ví dụ UPDATE toàn bảng
-- hoặc CREATE OR REPLACE ... AS SELECT) để BigQuery re-cluster phần cũ.
Khi nào clustering thực sự hiệu quả
Clustering không miễn phí về mặt hiệu quả nếu dùng sai chỗ. Nó phát huy khi:
- Cột high-cardinality (nhiều giá trị khác nhau):
customer_id,account_number,merchant_id,card_number. Cột cardinality thấp (nhưis_activechỉ 2 giá trị) gần như vô ích để cluster — không sắp thành các block tách biệt được. - Cột đó thường xuất hiện trong
WHERE(nhất là filter bình đẳng=hoặcIN), trongGROUP BYaggregate, hoặc là khóaJOIN. Clustering giúp cả pruning lẫn giảm shuffle khi aggregate/join theo cột cluster. - Bảng đủ lớn (thường ≥1 GB). Bảng nhỏ vốn chỉ vài block, pruning chẳng cắt được gì đáng kể — chi phí quản lý còn lớn hơn lợi ích.
- Query lọc theo tiền tố cột cluster từ trái sang (đúng thứ tự đã thiết kế).
Clustering kém tác dụng khi: cột cardinality thấp; filter chủ yếu theo range rộng chạm gần hết block; query hầu như SELECT * toàn bảng không filter; hoặc bảng quá nhỏ. Khi nghi ngờ, đo total_bytes_processed (qua INFORMATION_SCHEMA.JOBS) trước/sau clustering trên các query thật thay vì đoán.
Use case thực tế
Đội Fraud của NCB có bảng transactions ~2.4 TB, 2 năm lịch sử. Truy vấn điều tra điển hình: "mọi giao dịch của một khách trong một khoảng ngày". Ban đầu bảng chỉ partition theo txn_date: filter 30 ngày cắt còn ~45 GB, nhưng vì trong mỗi ngày customer_id rải khắp mọi block, tra một khách vẫn phải quét toàn bộ 30 partition đó.
Đội tạo lại bảng PARTITION BY txn_date CLUSTER BY region, branch_id, customer_id. Với query điều tra 1 khách trong 30 ngày ở 1 khu vực:
- Partition pruning cắt 2 năm → 30 ngày: ~45 GB.
- Block pruning theo tiền tố cluster (
region→branch_id→customer_id) cắt tiếp trong từng ngày: mỗi lần chỉ đọc vài block chứa đúng khách → còn ~1.2 GB quét thực (minh hoạ định tính).
Kết quả: cùng câu hỏi nghiệp vụ, bytes quét giảm thêm ~97% so với chỉ-partition, thời gian trả về từ vài chục giây xuống dưới 2 giây, và automatic reclustering giữ hiệu quả này ổn định dù bảng nhận thêm hàng triệu giao dịch streaming mỗi ngày mà không cần bảo trì thủ công.
Ghi nhớ
- Clustering = sắp xếp vật lý dữ liệu theo tối đa 4 cột; nhờ đó BigQuery prune (bỏ qua) các block có khoảng min/max không khớp filter.
- Block pruning chỉ hiệu quả khi dữ liệu đã được sắp gom cụm — cột high-cardinality dùng để filter (
=/IN), aggregate, hoặc join là ứng viên tốt nhất. - Khác partition: partition cắt cứng theo 1 cột low-cardinality (thường ngày); clustering cắt mềm theo tối đa 4 cột, hợp cột nhiều giá trị.
- Thứ tự cột cluster quan trọng: pruning tận dụng từ trái sang phải như
ORDER BY; đặt cột filter thường nhất lên đầu, không bỏ cách được. - Kết hợp partition + cluster là bộ đôi mạnh nhất: prune partition trước, prune block sau — cắt hai tầng.
- Automatic reclustering tự sắp lại dữ liệu nền, miễn phí, không cần bảo trì thủ công.
- Dry-run không ước lượng được lợi ích clustering; đo
total_bytes_processedthực sau khi chạy để đánh giá. - Clustering vô ích với cột cardinality thấp, bảng quá nhỏ, hoặc query không filter theo tiền tố cluster.
Nguồn tham khảo
- Clustered tables: https://cloud.google.com/bigquery/docs/clustered-tables
- Introduction to clustered tables: https://cloud.google.com/bigquery/docs/clustered-tables-intro
- Creating and using clustered tables: https://cloud.google.com/bigquery/docs/creating-clustered-tables
- Partitioned tables: https://cloud.google.com/bigquery/docs/partitioned-tables
- Best practices for performance overview: https://cloud.google.com/bigquery/docs/best-practices-performance-overview
- BigQuery documentation: https://cloud.google.com/bigquery/docs
- "Google BigQuery: The Definitive Guide" — Lakshmanan & Tigani, O'Reilly
Bài viết liên quan
So sánh các định dạng dữ liệu (CSV, JSON, XML, Avro, Parquet, ORC) và lý do lưu theo cột nhanh hơn cho phân tích. Bài đi sâu vào row vs columnar storage, nén (Snappy/gzip/zstd), schema evolution, OLTP vs OLAP, object storage và partitioning để tối ưu chi phí lẫn tốc độ truy vấn.
Data Engineering là ngành xây dựng và vận hành hệ thống biến dữ liệu thô thành dữ liệu sạch, tin cậy, sẵn sàng cho phân tích và AI. Bài giới thiệu vai trò Data Engineer trong vòng đời dữ liệu (nguồn → ingestion → storage → transformation → serving), phân biệt với Analyst/Scientist/ML Engineer, bức tranh hệ sinh thái công cụ và bài toán đưa dữ liệu core banking sang kho phân tích.
Stream processing là gì, khác biệt batch vs stream (bounded/unbounded), micro-batch (Spark) vs true streaming, Apache Flink là gì và định vị so với Spark Structured Streaming và Kafka Streams. Kiến trúc runtime JobManager/TaskManager, các tầng API, triết lý streaming-first và bối cảnh phát hiện gian lận ngân hàng.
Vì sao một máy không đủ và cần xử lý phân tán: từ MapReduce, Hadoop/HDFS đến Apache Spark in-memory. Kiến trúc driver–executor–cluster manager, các mức trừu tượng RDD/DataFrame/Dataset, cơ chế lazy evaluation với DAG, và vì sao shuffle (wide dependency) là phần tốn kém nhất. Kèm PySpark, Spark SQL và các kỹ thuật tối ưu (partition, broadcast join, cache, chống skew) cùng khi nào KHÔNG nên dùng Spark.
Cảm nhận của bạn
Bình luận
Chưa có bình luận. Hãy là người đầu tiên chia sẻ!