BigQuery 8 — Cách tính tiền & bytes
Vì sao phải hiểu cách BigQuery tính tiền
BigQuery bán một trải nghiệm gần như kỳ diệu: bạn gõ một câu SQL, không cần dựng cluster, không cần chọn kích cỡ máy, và vài giây sau có kết quả trên hàng terabyte dữ liệu. Nhưng chính vì không có "cái nút bật máy" nào để bạn nhìn thấy, nên mỗi câu query là một khoản chi tiêu vô hình — và với mô hình on-demand mặc định, một câu SELECT * vô ý trên bảng lịch sử giao dịch có thể quét vài TB và đội hóa đơn lên đáng kể chỉ trong một lần nhấn Enter.
Với một ngân hàng như NCB, nơi hàng trăm analyst, mô hình rủi ro và pipeline ETL cùng đâm vào một project BigQuery, câu hỏi không phải "hệ thống chạy được không" mà là "câu query này quét bao nhiêu bytes, và ai đang trả cho nó". Bài này xây mô hình tinh thần về chi phí BigQuery tới từng khái niệm gốc — bytes và slot-time — rồi trang bị các công cụ để ước lượng và chặn trước khi tiền thực sự chảy đi.
Lưu ý về đơn giá: bài này không trích đơn giá USD cụ thể vì giá thay đổi theo thời gian và theo region. Mọi con số tiền tệ hãy tra trực tiếp trang BigQuery pricing. Bài tập trung vào cơ chế tính tiền — thứ ít thay đổi và là chỗ bạn thực sự tối ưu được.
Hai dòng tiền tách biệt: storage và compute
Điều đầu tiên và quan trọng nhất: BigQuery tách rời storage và compute, cả về kiến trúc lẫn hóa đơn. Dữ liệu nằm trên Colossus (storage), còn truy vấn chạy trên Dremel (compute) — hai hệ thống riêng, tính tiền riêng. Bạn có thể lưu 100 TB mà không chạy query nào (chỉ trả storage), hoặc quét đi quét lại cùng một bảng nhỏ (chủ yếu trả compute). Hiểu chi tiết tách biệt này ở BigQuery — Storage & định dạng cột.
Storage pricing — âm thầm nhưng dễ hiểu
Storage tính theo dung lượng đã lưu (đơn vị GB/tháng), và BigQuery chia làm hai bậc:
- Active storage: bảng (hoặc partition) có thay đổi trong 90 ngày gần nhất.
- Long-term storage: bảng/partition không bị chỉnh sửa suốt hơn 90 ngày sẽ tự động chuyển sang giá long-term rẻ hơn — không cần thao tác gì, không giảm hiệu năng, không đổi cách truy vấn. Đây là "khuyến mãi tự động" cho dữ liệu lịch sử.
Một điểm cần nhớ về billing model của storage: BigQuery cho chọn giữa logical bytes (dung lượng chưa nén, theo kiểu dữ liệu) và physical bytes (dung lượng thực đã nén trên đĩa, gồm cả time travel & fail-safe). Chọn model nào rẻ hơn tùy độ nén của dataset — tra bảng so sánh trên trang pricing. Storage thường chiếm phần nhỏ hóa đơn so với compute, nên phần còn lại của bài tập trung vào compute.
Compute pricing — hai mô hình, hai đơn vị đo
Đây là chỗ tiền thực sự chảy, và BigQuery có hai mô hình tính compute khác hẳn nhau:
| On-demand | Capacity / Editions (reservations) | |
|---|---|---|
| Đơn vị tính tiền | Bytes billed (dữ liệu query quét) | Slot-time (slot-hour / slot-second) |
| Trả theo | Mỗi query, theo lượng bytes | Số slot bạn đặt trước, theo thời gian |
| Phù hợp | Workload thưa, khó đoán, mới bắt đầu | Workload đều, khối lượng lớn, cần chi phí dự đoán được |
| Rủi ro chi phí | Một query quét nhiều = đắt ngay | Cố định theo reservation, không phụ thuộc bytes |
| Cách tối ưu | Giảm bytes quét | Tăng hiệu quả dùng slot, xếp workload |
Slot là đơn vị tính toán ảo của Dremel — chi tiết về slot và mô hình thực thi xem BigQuery — Slots & mô hình thực thi. Với mô hình capacity/Editions, bạn mua trước một số slot (reservation) và trả theo slot-time bất kể query quét bao nhiêu bytes; ở đây tối ưu nghĩa là dùng slot cho hiệu quả. Với mô hình on-demand — mặc định và phổ biến nhất khi bắt đầu — bạn trả theo bytes billed, nên toàn bộ nghệ thuật tối ưu chi phí quy về một câu: quét càng ít bytes càng tốt. Phần còn lại của bài đào sâu vế on-demand này.
bytes_processed vs bytes_billed — đừng nhầm hai con số
Hai thuật ngữ nghe giống nhau nhưng khác nhau, và bạn cần phân biệt rạch ròi:
totalBytesProcessed(bytes_processed): lượng dữ liệu BigQuery thực sự đọc và xử lý để trả lời query. Đây là con số kỹ thuật phản ánh query chạm vào bao nhiêu dữ liệu.totalBytesBilled(bytes_billed): lượng bytes bạn bị tính tiền — đây mới là con số vào hóa đơn ở mô hình on-demand.
Vì sao hai số lệch nhau? Vì on-demand có làm tròn tối thiểu: mỗi query bị tính ít nhất một lượng bytes tối thiểu (mức sàn nhỏ, ngay cả query quét gần như không có gì), và bytes billed được làm tròn lên theo đơn vị. Ngoài ra:
- Query cache trúng (
cacheHit = true):bytes_billed = 0— miễn phí hoàn toàn (xem mục cache bên dưới). - Với query rất nhỏ,
bytes_billedcó thể lớn hơnbytes_processedvì sàn tối thiểu. - Với query bị lỗi hoặc
LIMITmà vẫn phải quét cả bảng,bytes_processedphản ánh dữ liệu quét thật —LIMITkhông làm giảm bytes quét (xem cảnh báo dưới).
Quy tắc thực dụng: muốn ước lượng hóa đơn on-demand, nhìn bytes_billed; muốn hiểu query nặng hay nhẹ, nhìn bytes_processed.
Vì sao chọn cột và partition giảm bytes billed
BigQuery lưu dữ liệu ở định dạng cột (Capacitor trên Colossus). Hệ quả tài chính trực tiếp: khi bạn SELECT một vài cột, BigQuery chỉ đọc các cột đó, không đụng tới phần còn lại của bảng. Đây là lý do câu khuyên "đừng bao giờ SELECT *" không phải chuyện phong cách mà là chuyện tiền bạc — SELECT * buộc đọc mọi cột, kể cả cột TEXT lớn bạn chẳng cần.
Tầng giảm bytes thứ hai là partition: nếu bảng được phân vùng theo ngày (ví dụ theo txn_date), một câu query có điều kiện WHERE txn_date phù hợp sẽ cắt tỉa partition (partition pruning) — BigQuery chỉ đọc các partition khớp, bỏ qua toàn bộ phần còn lại trước khi tính bytes. Chi tiết cơ chế ở BigQuery — Phân vùng bảng (Partitioning).
Con số trong sơ đồ là minh hoạ định tính để thấy độ dốc: đi từ SELECT * toàn bảng xuống chọn cột giảm hàng chục lần, rồi thêm partition pruning giảm tiếp hàng chục lần nữa. Cùng một câu hỏi nghiệp vụ, cùng một kết quả, nhưng bytes billed — tức tiền — chênh nhau hai bậc độ lớn.
Cảnh báo về LIMIT: LIMIT 100 không làm giảm bytes quét/billed ở on-demand. BigQuery vẫn phải đọc dữ liệu các cột được chọn (trên các partition khớp) rồi mới cắt lấy 100 dòng. Muốn giảm bytes phải giảm cột và partition đọc vào, không phải giảm dòng xuất ra.
Dry-run — ước lượng bytes TRƯỚC khi chạy
Công cụ chống sốc hóa đơn quan trọng nhất: dry run. BigQuery cho bạn hỏi "câu query này sẽ quét bao nhiêu bytes" mà không thực sự chạy và không tốn tiền. Optimizer trả về ước lượng totalBytesProcessed dựa trên metadata.
Bằng bq CLI:
# Dry run: chỉ ước lượng bytes, không chạy, không tính tiền
bq query --use_legacy_sql=false --dry_run \
'SELECT customer_id, amount, txn_date
FROM `ncb-dwh.core.transactions`
WHERE txn_date BETWEEN "2026-06-01" AND "2026-06-30"'
# → "Query successfully validated. Assuming the tables are not modified,
# running this query will process 15804112384 bytes of data."
Trong console UI, mỗi lần bạn gõ SQL, góc phải trên hiện sẵn dòng "This query will process X when run" — đó chính là dry-run tự động. Nhìn con số này trước mỗi query lớn là thói quen tiết kiệm tiền rẻ nhất. Trong pipeline, client library cũng có cờ dry-run (JobConfig(dry_run=True)) để ước lượng và log bytes trước khi thực thi thật.
maximum_bytes_billed — cầu dao chặn query đắt
Dry-run cảnh báo, nhưng con người vẫn có thể lỡ tay. maximum_bytes_billed là cầu dao tự động: đặt một trần bytes, query nào vượt trần sẽ bị hủy ngay và không tính tiền, thay vì chạy tới cùng rồi mới thấy hóa đơn.
-- Đặt trần cho phiên: query nào quét quá 20 GB sẽ FAIL, không chạy, không tốn tiền
SET @@maximum_bytes_billed = 20000000000; -- 20 GB
SELECT customer_id, SUM(amount) AS total_spent
FROM `ncb-dwh.core.transactions`
WHERE txn_date BETWEEN "2026-06-01" AND "2026-06-30"
GROUP BY customer_id;
Nếu câu trên thực tế quét 15 GB, nó chạy bình thường. Nếu ai đó sửa thành SELECT * không có WHERE khiến quét 2 TB, query bị chặn với lỗi vượt maximum_bytes_billed — cầu dao đã ngắt trước khi tiền chảy. Trần này có thể đặt ở nhiều cấp:
| Cấp đặt | Cách | Phạm vi |
|---|---|---|
| Từng query | --maximum_bytes_billed (bq CLI) hoặc JobConfig | Một job |
| Phiên / script | SET @@maximum_bytes_billed = ... | Cả session |
| Mặc định project | Cấu hình default trong project settings | Mọi job trong project |
Với môi trường ngân hàng, đặt trần mặc định ở cấp project (kết hợp custom cost control quota theo user/project) là hàng rào an toàn nên có ngay từ đầu. Giám sát tổng thể chi phí và cảnh báo bất thường xem BigQuery — Giám sát & chi phí.
Query cache — trúng cache là miễn phí
BigQuery tự động cache kết quả mỗi query. Nếu bạn (hoặc người khác trong cùng điều kiện) chạy lại đúng câu query đó và bảng nguồn không đổi, BigQuery trả kết quả từ cache: cacheHit = true, bytes_billed = 0 — hoàn toàn miễn phí và gần như tức thì.
Điều kiện để trúng cache (định tính, tra doc để đủ danh sách):
- Text query giống hệt (kể cả khoảng trắng/comment cũng ảnh hưởng ở một số trường hợp).
- Bảng nguồn không thay đổi kể từ lần chạy trước.
- Query không chứa hàm phi tất định (
CURRENT_TIMESTAMP(),RAND(),CURRENT_DATE()…) — vì kết quả khác nhau mỗi lần nên không cache được. - Không dùng wildcard table theo cách phá cache, không ghi ra bảng đích, không truy vấn nguồn external thay đổi liên tục.
Hệ quả thực dụng: dashboard làm mới mỗi 5 phút bằng đúng một câu query trên bảng chỉ cập nhật mỗi đêm sẽ trúng cache gần như cả ngày → chi phí gần như bằng 0. Ngược lại, thêm CURRENT_TIMESTAMP() vào query là vô tình tắt cache và trả tiền lại từ đầu mỗi lần.
Truy vết bytes đã tốn qua INFORMATION_SCHEMA
Sau khi query đã chạy, bạn kiểm tra bytes billed thực tế và soi ai đang tốn nhiều nhất qua INFORMATION_SCHEMA.JOBS:
-- Top 10 query tốn bytes billed nhất trong 7 ngày qua
SELECT
job_id,
user_email,
total_bytes_billed,
total_bytes_processed,
cache_hit,
ROUND(total_bytes_billed / POW(1024, 4), 3) AS tib_billed,
creation_time,
SUBSTR(query, 0, 80) AS query_preview
FROM `region-asia-southeast1`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
AND job_type = 'QUERY'
AND state = 'DONE'
ORDER BY total_bytes_billed DESC
LIMIT 10;
Cột total_bytes_billed là con số vào hóa đơn on-demand; so nó với total_bytes_processed và cache_hit để hiểu query nào quét nhiều thật, query nào đã được cache miễn phí. Chi tiết các cột và cách khai thác job_stages xem BigQuery — Giám sát & chi phí.
Use case thực tế
Đội Data của NCB có một dashboard rủi ro tín dụng làm mới mỗi 15 phút, chạy trên bảng transactions 2 năm lịch sử (~2.4 TB). Bản đầu tiên dùng SELECT * ... WHERE 1=1 rồi lọc ở tầng BI, mỗi lần chạy quét gần 2.4 TB; với ~96 lần/ngày ở on-demand, đây là một trong những khoản tốn nhất project.
Ba can thiệp, không đổi kết quả nghiệp vụ:
- Chọn cột: chỉ lấy
customer_id, amount, txn_date, risk_flag→ bytes quét rớt từ 2.4 TB xuống ~180 GB/lần. - Partition pruning: bảng phân vùng theo
txn_date, dashboard chỉ cần 90 ngày gần nhất → mỗi lần chỉ đọc ~45 GB. - Query cache: bảng chỉ nạp mới lúc 2h sáng; bỏ
CURRENT_TIMESTAMP()khỏi query → sau lần chạy đầu buổi, cả ngày trúng cache,bytes_billed = 0cho hầu hết lần làm mới.
Kết quả: từ ~230 TB billed/ngày (2.4 TB × 96) xuống còn ~45 GB thực quét cho lần chạy đầu tiên mỗi ngày, phần còn lại miễn phí nhờ cache — giảm bytes billed hơn 99%. Đội đặt thêm maximum_bytes_billed = 100 GB mặc định cho service account của dashboard làm cầu dao, và --dry_run bắt buộc trong CI cho mọi query mới trước khi lên production.
Ghi nhớ
- BigQuery tách storage (Colossus, theo GB/tháng) và compute (Dremel) thành hai dòng tiền riêng; dữ liệu không đụng >90 ngày tự chuyển long-term storage rẻ hơn.
- Compute có hai mô hình: on-demand trả theo bytes billed (có sàn tối thiểu + làm tròn), capacity/Editions trả theo slot-time.
- Phân biệt
bytes_processed(dữ liệu thực đọc) vớibytes_billed(con số vào hóa đơn on-demand); cache trúng thìbytes_billed = 0. - Giảm bytes billed bằng chọn cột (định dạng cột chỉ đọc cột cần) và partition pruning (
WHEREtheo cột phân vùng) — có thể giảm hai bậc độ lớn. LIMITkhông giảm bytes quét; muốn rẻ phải cắt cột và partition đọc vào, không phải dòng xuất ra.- Dry-run ước lượng bytes miễn phí trước khi chạy;
maximum_bytes_billedlà cầu dao chặn query vượt trần, hủy trước khi tính tiền. - Query cache miễn phí hoàn toàn khi query giống hệt + bảng không đổi + không có hàm phi tất định.
- KHÔNG tra đơn giá USD từ trí nhớ — luôn xem trang pricing chính thức vì giá đổi theo thời gian & region.
Nguồn tham khảo
- BigQuery pricing: https://cloud.google.com/bigquery/pricing
- BigQuery documentation: https://cloud.google.com/bigquery/docs
- Best practices — control costs: https://cloud.google.com/bigquery/docs/best-practices-costs
- Estimate and control query costs (dry run, maximum_bytes_billed): https://cloud.google.com/bigquery/docs/best-practices-performance-overview
- Partitioned tables: https://cloud.google.com/bigquery/docs/partitioned-tables
- INFORMATION_SCHEMA JOBS: https://cloud.google.com/bigquery/docs/information-schema-jobs
- Reservations / slots (Editions): https://cloud.google.com/bigquery/docs/reservations-intro
- "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ẻ!