0

Google BigQuery - Bài học 26: Table Partitioning (Phân vùng bảng) – Chiến lược chia bảng theo Thời gian và Số nguyên

Chào anh em! Chúng ta đã chính thức bước sang Phần 4: Thiết kế Bảng, Tối ưu Hiệu năng & Cắt giảm Chi phí – phần quan trọng nhất đối với bất kỳ Data Engineer hay Backend Lead nào muốn làm chủ hoàn toàn bài toán tốc độ và tiền bạc trên Google BigQuery.

Ở các bài học trước, chúng ta đã biết cách viết SQL, lập trình Procedural, gọi API và sao lưu dữ liệu. Nhưng hãy thử tưởng tượng kịch bản sau: Công ty bạn tích lũy dữ liệu đơn hàng trong suốt 5 năm qua, tổng cộng lên tới 10 Tỷ dòng (nặng khoảng 2 Terabyte). Sếp yêu cầu bạn chạy một báo cáo đơn giản: "Hãy đếm số lượng đơn hàng được tạo trong ngày hôm qua."

Bạn viết câu lệnh:

SQL

SELECT COUNT(*) 
FROM `my-project.core_dwh.fact_orders`
WHERE DATE(created_at) = '2026-10-17';

Bạn bấm Run, và điều gì xảy ra?

  • Bảng này không có phân vùng. Dremel buộc phải huy động Slots thò tay xuống Colossus đọc toàn bộ 2 TB dữ liệu của cả 5 năm qua lên để lọc từng dòng xem dòng nào thuộc về ngày hôm qua.

  • Câu lệnh chạy mất 30 giây và ngốn của công ty gần $13 cho một thao tác cực kỳ lỏm ngỏm. Nếu mỗi ngày có 100 nhân viên chạy các câu lệnh tương tự mà quên mất tối ưu, hóa đơn cuối tháng sẽ khiến bạn "khóc thét".

Làm thế nào để bắt BigQuery chỉ đọc đúng dung lượng của đúng ngày hôm qua (chỉ vài chục Megabyte) thay vì quét nguyên cả đống rác 2 TB? Câu trả lời chính là Table Partitioning (Phân vùng bảng)! Hãy cùng mổ xẻ ngay nhé!

1. Table Partitioning là gì? Tại sao nó là "vũ khí tối thượng" chống cháy túi?

Table Partitioning là kỹ thuật chia nhỏ một bảng logic lớn thành nhiều "phân vùng" (partitions) vật lý độc lập trên ổ đĩa dựa trên giá trị của một cột cụ thể (thường là cột thời gian hoặc số nguyên).

Trực quan hóa sự khác biệt:

  • Bảng thông thường (Non-partitioned Table): Giống như một cuốn sách dày 10.000 trang nhưng viết trộn lẫn tất cả các năm từ 2021 đến 2026 vào chung một chương. Muốn tìm dữ liệu ngày hôm qua, bạn phải đọc từ trang đầu đến trang cuối.

  • Bảng phân vùng (Partitioned Table): Giống như tủ tài liệu được chia thành hàng ngàn ngăn kéo, mỗi ngăn kéo đại diện cho một ngày cụ thể (ví dụ: ngăn kéo 2026-10-17, ngăn kéo 2026-10-18). Khi bạn gõ điều kiện tìm kiếm theo ngày, BigQuery chỉ mở đúng ngăn kéo ngày hôm đó và hoàn toàn phớt lờ các ngăn kéo còn lại!

Lợi ích trực diện:

  1. Tiết kiệm chi phí khủng khiếp (Partition Pruning): BigQuery chỉ tính tiền dựa trên dung lượng của các partition thực sự bị quét. Quét 50 MB thay vì 2 TB nghĩa là chi phí giảm đi hàng chục ngàn lần!

  2. Tăng tốc độ truy vấn: Lượng dữ liệu phải đọc giảm đi thì thời gian phản hồi (Latency) thu gọn lại từ phút xuống mili-giây.

  3. Quản lý dữ liệu dễ dàng: Bạn có thể dễ dàng xóa hoặc thay thế dữ liệu của một ngày cũ kỹ mà không làm ảnh hưởng đến các phân vùng khác.

2. Các loại Table Partitioning được hỗ trợ trong BigQuery

GoogleSQL hỗ trợ 2 hình thức phân vùng bảng chính:

2.1. Phân vùng theo Thời gian (Time-unit Partitioning) – Phổ biến nhất 99% dự án

Dữ liệu được chia nhỏ theo khoảng thời gian dựa trên một cột kiểu DATE, TIMESTAMP, hoặc DATETIME. Bạn có thể chọn chia nhỏ theo: Theo ngày (Daily - Mặc định), theo giờ (Hourly), theo tháng (Monthly), hoặc theo năm (Yearly).

  • Ứng dụng hoàn hảo: Bảng log hệ thống, bảng sự kiện người dùng (user_events), bảng đơn hàng (fact_orders), bảng giao dịch tài chính.

2.2. Phân vùng theo Khoảng số nguyên (Integer Range Partitioning)

Dữ liệu được chia nhỏ dựa trên các khoảng giá trị của một cột kiểu số nguyên (INT64). Bạn phải định nghĩa rõ điểm bắt đầu (Start), điểm kết thúc (End), và khoảng bước nhảy (Interval).

  • Ứng dụng thực tế: Phân vùng theo khoảng ID khách hàng lớn (ví dụ chia theo từng cụm 1 triệu user_id) hoặc mã vùng bưu điện.

3. Thực chiến: Tạo bảng phân vùng theo ngày và cú pháp DDL

Hãy xem cách chúng ta định nghĩa một bảng phân vùng theo thời gian chuẩn mực trong BigQuery:

SQL

CREATE TABLE `my-project.core_dwh.fact_orders_partitioned` (
    order_id INT64,
    customer_id INT64,
    status STRING,
    total_amount NUMERIC,
    created_at TIMESTAMP
)
-- Chỉ định cột phân vùng chính là cột created_at được ép kiểu về DATE
PARTITION BY DATE(created_at)
OPTIONS (
    description = "Bảng đơn hàng được phân vùng theo ngày tạo",
    -- Bật chốt chặn an toàn tuyệt đối mà chúng ta đã học ở Bài 6:
    require_partition_filter = TRUE 
);

Điểm nhấn quan trọng trong câu lệnh DDL trên:

  1. PARTITION BY DATE(created_at): Ra lệnh cho BigQuery tự động băm nhỏ bảng này ra thành các phân vùng theo từng ngày dựa trên giá trị của cột created_at.

  2. require_partition_filter = TRUE (Chốt chặn sinh tử): Thuộc tính này bắt buộc bất kỳ ai khi viết câu lệnh SELECT truy vấn vào bảng này phải có điều kiện lọc WHERE trên cột phân vùng. Nếu một lập trình viên quên gõ điều kiện ngày tháng, BigQuery sẽ từ chối chạy ngay lập tức và báo lỗi, giúp công ty không bị đốt tiền oan vì những câu lệnh SELECT * vô ý thức!

4. Cách thức hoạt động của "Partition Pruning" (Cắt tỉa phân vùng)

Khi bạn viết một câu lệnh truy vấn có kèm điều kiện lọc:

SQL

SELECT status, SUM(total_amount)
FROM `my-project.core_dwh.fact_orders_partitioned`
WHERE created_at >= TIMESTAMP('2026-10-01 00:00:00', 'Asia/Ho_Chi_Minh')
  AND created_at < TIMESTAMP('2026-10-02 00:00:00', 'Asia/Ho_Chi_Minh')
  AND status = 'COMPLETED';

Quá trình diễn ra dưới tầng Dremel như sau:

  1. Trình tối ưu hóa truy vấn đọc phần Metadata của bảng, nhận thấy điều kiện WHERE chỉ khoanh vùng đúng ngày 2026-10-01.

  2. Nó ngay lập tức kích hoạt cơ chế Partition Pruning (Cắt tỉa phân vùng): Loại bỏ hoàn toàn các thư mục/phân vùng của hàng ngàn ngày khác trên Colossus.

  3. Chỉ có duy nhất dữ liệu của ngày 1 tháng 10 được nạp lên để tính toán. Câu lệnh hoàn thành trong tích tắc với chi phí gần như bằng 0!

5. Quản lý các phân vùng cũ: Xóa, Thêm và Phân vùng giả (Pseudo-column _PARTITIONDATE)

Trong quá trình vận hành, đôi khi bạn cần thao tác trực tiếp với các phân vùng:

Xem danh sách các phân vùng đang có trong bảng:

Bạn có thể truy vấn vào bảng hệ thống INFORMATION_SCHEMA để kiểm tra dung lượng và số lượng dòng của từng phân vùng:

SQL

SELECT 
    partition_id, 
    total_rows, 
    total_bytes / 1024 / 1024 AS size_in_mb
FROM `my-project.core_dwh.INFORMATION_SCHEMA.PARTITIONS`
WHERE table_name = 'fact_orders_partitioned'
ORDER BY partition_id DESC;

(Trên cột partition_id, bạn sẽ thấy các giá trị dạng 20261018, 20261017... rất trực quan).

Xóa nhanh một phân vùng cũ (Xóa dữ liệu rác):

Thay vì viết câu lệnh DELETE FROM table WHERE date < ... (vừa chậm vừa tốn tiền xử lý transaction), bạn có thể dùng lệnh DROP PARTITION để xóa sạch dữ liệu của một ngày cũ kỹ trong vòng 0.1 giây với chi phí 0 đồng:

SQL

-- Xóa toàn bộ dữ liệu của ngày 1 tháng 1 năm 2024
ALTER TABLE `my-project.core_dwh.fact_orders_partitioned`
DROP PARTITION RANGE(DATE('2024-01-01'), DATE('2024-01-02'));

Tổng kết Bài học 26

Table Partitioning là bước đi đầu tiên và quan trọng nhất để thiết kế một bảng dữ liệu quy mô lớn chuẩn Enterprise trên BigQuery:

  1. Chia nhỏ dữ liệu: Phân tách bảng lớn thành các ngăn kéo theo ngày hoặc số nguyên.

  2. Tiết kiệm tài nguyên: Kích hoạt cơ chế Partition Pruning để chỉ đọc đúng dữ liệu cần thiết, giảm chi phí quét truy vấn xuống hàng ngàn lần.

  3. Phòng thủ chủ động: Luôn bật require_partition_filter = TRUE để ép buộc mọi người phải lọc theo phân vùng khi viết SQL.

Tuy nhiên, điều gì xảy ra nếu bên trong một phân vùng ngày cụ thể đó, bảng của bạn vẫn còn tới 50 triệu dòng, và bạn thường xuyên phải tìm kiếm dữ liệu theo user_id hoặc status? Chỉ phân vùng theo thời gian thôi là chưa đủ!

Làm thế nào để sắp xếp dữ liệu đa chiều bên trong từng phân vùng để BigQuery tăng tốc hơn nữa? Hãy đón xem Bài học 27: Table Clustering (Phân cụm bảng) – Sắp xếp dữ liệu đa chiều để cắt giảm chi phí quét tối đa!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí