DevOps Peach

GCP Basics · Data on GCP – BigQuery, Pub/Sub, Dataflow and a First Pipeline · Lesson 2 of 5

BigQuery Cost and Performance: Partitioning, Clustering, Slots

Partition tables by date so queries read only the days they need, cluster by frequent filter columns, check bytes with dry runs, cap them with maximum bytes billed, and choose on-demand or editions with slot reservations.

Practice interactively →

BigQuery is fast by default, but careless queries on large tables can be expensive. Most savings come from reading fewer bytes.

flowchart LR
  Q["Query: orders of 5 Nov<br/>for customer C-1042"] --> P["Partition pruning<br/>only 2026-11-05"]
  P --> C["Clustering by customer_id<br/>only matching blocks"]
  C --> S["Bytes scanned: 40 MB<br/>instead of 2 TB"]
  DR["--dry_run"] --> S
  MB["maximum_bytes_billed"] --> S
  %% step Q: The query filters on the partition column and on a clustered column.
  %% step P: With a table partitioned by order_date, BigQuery skips every other day’s partition.
  %% step C: Inside the partition, clustering by customer_id lets it read only the blocks that can match.
  %% step S: The query scans megabytes instead of terabytes, so it is cheaper and faster.
  %% step DR: A dry run reports the bytes a query would scan without running it.
  %% step MB: A maximum bytes billed limit makes a query fail instead of running up a large bill.

Partitioning

Type Example
Time-unit column PARTITION BY DATE(created_at) (day, hour, month, year)
Ingestion time _PARTITIONTIME set at load time
Integer range customer_bucket 0–999

Clustering

Sort storage by up to four columns you filter or join on (for example customer_id, country). BigQuery maintains it automatically. Clustering helps most on large tables (over ~1 GB).

CREATE TABLE shop.orders_p
PARTITION BY DATE(created_at)
CLUSTER BY customer_id, country
OPTIONS (require_partition_filter = TRUE, partition_expiration_days = 730)
AS SELECT * FROM shop.orders;

Checking and capping cost

bq query --use_legacy_sql=false --dry_run   'SELECT total FROM shop.orders_p WHERE DATE(created_at) = "2026-11-05"'
bq query --use_legacy_sql=false --maximum_bytes_billed=10000000000 '…'

Performance tips

Slots and editions

A slot is a unit of compute. On-demand projects share a large pool. With editions you create reservations (baseline slots plus autoscaling) and assign projects to them; commitments lower the price. Use editions when on-demand bills are high and steady, or when you need workload isolation.

Command Meaning
bq query --dry_run --use_legacy_sql=false '…' Estimate bytes
bq update --time_partitioning_expiration=63072000 shop.orders_p Partition expiry (seconds)
bq mk --reservation --location=asia-southeast1 --edition=ENTERPRISE --slots=100 prod Slot reservation

Traps: wrapping the partition column in a function the planner cannot use can disable pruning; clustering on too many or rarely used columns gives little benefit; the query cache does not apply to queries with non-deterministic functions such as CURRENT_TIMESTAMP().

Practice interactively →