Google BigQuery - Bài học 32: 10 thói quen xấu gây "cháy túi" trên BigQuery – Tại sao SELECT * và LIMIT 10 không hề giúp bạn tiết kiệm tiền như trong MySQL
Chào anh em! Qua Bài học 31, chúng ta đã biết cách sử dụng Search Indexes để "mò kim đáy bể" tìm kiếm log văn bản với tốc độ chớp nhoáng.
Hôm nay, chúng ta sẽ đến một bài học mang tính chất "cảnh tỉnh" và thực chiến nhất trong toàn bộ chuỗi series: 10 thói quen xấu khiến hóa đơn tiền Cloud của công ty bạn "nổ tung" vào cuối tháng.
Khi mới chuyển từ thế giới RDBMS truyền thống (MySQL, PostgreSQL) sang Google BigQuery, phần lớn các lập trình viên đều mang theo những thói quen cũ. Đáng sợ nhất là câu thần chú quen thuộc: "Cứ gõ SELECT * đi, rồi thêm LIMIT 10 ở cuối là an toàn, vừa xem được dữ liệu vừa nhanh gọn!"
Trong MySQL, thói quen đó vô hại vì bộ lưu trữ theo dòng (Row-oriented) cho phép ổ cứng dừng đọc ngay sau khi lấy đủ 10 dòng đầu tiên. Nhưng trong hệ thống phân tán Serverless của BigQuery, SELECT * kết hợp với LIMIT 10 chính là cú lừa thế kỷ, biến một câu lệnh xem thử đơn giản thành một hóa đơn tốn kém.
Hãy cùng điểm mặt 10 thói quen xấu kinh điển và cách sửa chữa ngay lập tức nhé!
1. Thói quen 1: Gõ SELECT * vì lười biếng
-
Sai lầm: Bạn nghĩ rằng lấy toàn bộ cột cũng chẳng sao vì dung lượng bảng nhỏ.
-
Hậu quả thực tế: Nhớ lại cấu trúc lưu trữ dạng cột (Capacitor - Bài 3): Khi bạn gõ
SELECT *, Dremel buộc phải mở và đọc toàn bộ các file vật lý của tất cả các cột có trong bảng (kể cả những cột chứa mô tả dài lê thê hoặc log JSON nặng hàng trăm GB mà bạn hoàn toàn không dùng tới). Bạn dùng bảng 50 cột mà chỉ cần 2 cột, nhưng lại trả tiền cho cả 50 cột! -
Cách sửa: Luôn chỉ định tường minh tên các cột thực sự cần lấy:
SELECT order_id, customer_id, total_amount FROM ....
2. Thói quen 2: Tin tưởng tuyệt đối vào LIMIT 10 để tiết kiệm tiền
-
Sai lầm: "Tôi chỉ lấy 10 dòng đầu xem mặt mũi dữ liệu thế nào, chắc chắn tốn có vài byte!"
-
Hậu quả thực tế: Đây là hiểu lầm tai hại nhất!
LIMITchỉ là bước lọc kết quả cuối cùng (Final Post-processing Filter) nằm ở tầng trên cùng sau khi Dremel đã hoàn thành việc đọc toàn bộ dữ liệu từ ổ cứng Colossus, tính toán, và chuyển qua mạng Jupiter. -
Cách sửa: Để xem trước mẫu dữ liệu miễn phí 100%, hãy bấm vào tab
Previewtrên giao diện BigQuery Studio, hoặc sử dụng lệnhbq headtrên Terminal, hoặc dùng cú phápTABLESAMPLE SYSTEM (1 PERCENT).
3. Thói quen 3: Quên không lọc Partition (Quét nguyên cả lịch sử 5 năm)
-
Sai lầm: Viết câu lệnh
SELECT SUM(revenue) FROM orders WHERE status = 'COMPLETED'mà quên mất mệnh đềWHERElọc theo ngày tháng. -
Hậu quả thực tế: Dremel không biết phải bỏ qua phần nào, nên nó thò tay quét toàn bộ lịch sử 5 năm của bảng
orders. Thay vì chỉ quét dữ liệu của ngày hôm nay (vài chục MB), bạn quét nguyên 2 Terabyte dữ liệu và "đốt" ngay một khoản tiền không nhỏ cho một câu lệnh báo cáo cẩu thả. -
Cách sửa: Luôn luôn đưa điều kiện lọc ngày tháng (Partition filter) lên hàng đầu tiên, và cấu hình
require_partition_filter = TRUEcho các bảng lớn.
4. Thói quen 4: Dùng hàm biến đổi trên cột phân vùng làm hỏng "Partition Pruning"
-
Sai lầm: Bảng đã được phân vùng theo ngày dựa trên cột
created_at(kiểuTIMESTAMP), nhưng trong câu lệnh bạn lại viết:SQL
-- SAI LẦM: Làm mất tác dụng của Partition Pruning! WHERE DATE(created_at) = '2026-10-18' OR SUBSTR(string_date, 1, 10) = '2026-10-18' -
Hậu quả thực tế: Khi bạn bọc cột phân vùng bên trong một hàm (
DATE(),SUBSTR(),CAST()), trình tối ưu hóa truy vấn không thể nhận diện trước giá trị để cắt tỉa phân vùng (Partition Pruning), dẫn đến việc BigQuery vẫn phải quét toàn bộ các phân vùng khác trên ổ đĩa. -
Cách sửa: So sánh trực tiếp giá trị thời gian theo chuẩn khoảng hở (Range comparison):
WHERE created_at >= TIMESTAMP('2026-10-18 00:00:00') AND created_at < TIMESTAMP('2026-10-19 00:00:00').
5. Thói quen 5: Đặt các hàm động (Volatile Functions) làm mất hiệu lực Query Cache
-
Sai lầm: Sử dụng
CURRENT_TIMESTAMP()hoặcRAND()trong điều kiện lọc để lấy dữ liệu động liên tục. -
Hậu quả thực tế: Như đã học ở Bài 6 và Bài 15, các hàm thay đổi liên tục theo từng giây làm mất hoàn toàn cơ chế Query Cache miễn phí 24h của BigQuery, ép hệ thống phải tính toán và trừ tiền lại từ đầu mỗi khi bạn bấm chạy lại.
-
Cách sửa: Nếu chỉ cần lấy dữ liệu tính theo ngày, hãy dùng
CURRENT_DATE()hoặc lưu mốc thời gian vào biến cụ thể nếu không cần thiết phải lấyTIMESTAMPđến từng mili-giây.
6. Thói quen 6: JOIN hai bảng khổng lồ mà không lọc trước dữ liệu (Pre-filtering)
-
Sai lầm: Lấy toàn bộ bảng
orders(1 tỷ dòng)JOINtrực tiếp với bảngusers(50 triệu dòng) rồi mới viết lệnhWHEREở cuối cùng. -
Hậu quả thực tế: Tạo ra một thảm họa Shuffle qua mạng Jupiter. Hàng trăm Gigabyte dữ liệu rác của cả hai bảng bị băm và bắn chéo qua lại trước khi bị lọc bỏ.
-
Cách sửa: Luôn luôn lọc (
WHERE) các bảng con cho gọn gàng bằng các khối CTE (WITH ... AS (...)) trước khi đem chúng điJOINvới nhau.
7. Thói quen 7: Không bao giờ kiểm tra Dry Run trước khi chạy
-
Sai lầm: Viết một câu lệnh dài dòng, phức tạp rồi bấm nút
Runngay lập tức mà không nhìn xem nó sẽ quét bao nhiêu dữ liệu. -
Hậu quả thực tế: Trúng "bẫy" nhân chéo (Cartesian Product) hoặc quên mất một điều kiện quan trọng, khiến câu lệnh quét sạch hàng chục Terabyte và tiêu tốn ngân sách chỉ trong 2 giây.
-
Cách sửa: Tập phản xạ có điều kiện: Luôn nhìn vào góc trên bên phải màn hình xem dung lượng dự kiến (
This query will process X GB), hoặc luôn gõ cờ--dry_runtrên Terminal trước khi thực thi.
8. Thói quen 8: Unnest nhiều mảng độc lập vô tội vạ gây bùng nổ dòng (Row Explosion)
-
Sai l]={}. Dùng nhiều dấu phẩy để
UNNESThai mảng con khác nhau của cùng một bảng mà không có quan hệ trực tiếp. -
Hậu quả thực tế: Gây ra phép nhân chéo (Cartesian Product), làm số lượng dòng tăng vọt theo cấp số nhân (từ vài triệu dòng thành hàng tỷ dòng), dẫn đến tràn bộ nhớ (
Resources exceeded) và chi phí tính toán tăng phi mã. -
Cách sửa: Chỉ
UNNESTmột mảng tại một thời điểm hoặc xử lý chúng qua các CTE độc lập.
9. Thói quen 9: Lưu trữ dữ liệu thô dạng chuỗi JSON mãi mãi ở tầng Mart báo cáo
-
Sai lầm: Ở các bảng báo cáo tổng hợp dùng cho BI, thay vì bóc tách thành các cột định dạng chuẩn (
INT64,NUMERIC,STRING), bạn vẫn giữ nguyên cột kiểuJSONhoặc chuỗiSTRINGthô và bắt Dremel parse lại bằng hàmJSON_VALUEở mọi câu báo cáo. -
Hậu quả thực tế: Tiêu tốn cực nhiều chu kỳ CPU (Slot time) để phân tích chuỗi văn bản liên tục cho hàng triệu dòng báo cáo mỗi ngày.
-
Cách sửa: Chuẩn hóa dữ liệu ngay từ tầng Silver sang tầng Gold (Denormalization / Flat table) để các báo cáo BI đọc trực tiếp các cột vật lý dạng Capacitor đã nén tối ưu.
10. Thói quen 10: Không thiết lập các "Chốt chặn an toàn" cho Project và User
-
Sai lầm: Để tài khoản của toàn bộ nhân viên ở chế độ mở tự do, không cấu hình giới hạn hạn ngạch (Quotas) hay cảnh báo hóa đơn (Billing Alerts).
-
Hậu quả thực tế: Khi một nhân viên mới sơ suất viết nhầm một vòng lặp hoặc câu lệnh
SELECT *không điều kiện trên bảng khủng, toàn bộ hạn mức ngân sách tháng của công ty bay màu chỉ trong một buổi sáng. -
Cách sửa: Ngay lập tức cài đặt
maximum_bytes_billedcho các Project, bậtrequire_partition_filter = TRUEcho các bảng lớn, và thiết lập Billing Alerts gửi email cảnh báo về cho Tech Lead khi chi phí đạt mốc 50%.
Tổng kết Bài học 32
Hiểu rõ 10 thói quen xấu này chính là tấm khiên bảo vệ ví tiền của doanh nghiệp bạn trên Google Cloud:
-
Từ bỏ tư duy MySQL: Nhớ rằng
SELECT *vàLIMIT 10không hề giúp tiết kiệm tiền trên kiến trúc Columnar Storage của BigQuery. -
Tuân thủ quy tắc phòng thủ: Luôn lọc Partition, dùng CTE tiền xử lý, kiểm tra Dry Run và tận dụng tối đa các chốt chặn an toàn phần cứng.
Khép lại bài học cảnh tỉnh này, chúng ta sẽ bước vào những bài học cuối cùng của Phần 4 – nơi chúng ta học cách tự động hóa và quản lý mã nguồn SQL theo chuẩn Git CI/CD chuyên nghiệp với các công cụ hiện đại như Dataform và dbt.
Làm thế nào để quản lý các đoạn mã SQL biến đổi dữ liệu (Transformation) theo mô hình kỹ thuật phần mềm chuẩn chỉnh? Hãy đón xem Bài học 33: Hiện đại hóa quy trình biến đổi dữ liệu – Sử dụng Dataform hoặc dbt (data build tool)!
All rights reserved