Understanding BigQuery Monitoring
Monitoring BigQuery means systematically tracking the health, performance, and cost of your data warehouse workloads. Unlike traditional databases where you manage infrastructure, BigQuery is a fully managed, serverless platform — but that doesn't eliminate the need for observability. In fact, because costs scale directly with usage, monitoring becomes a critical discipline for both engineering and finance teams.
At its core, BigQuery monitoring encompasses three pillars:
- Metrics — Quantitative measurements of query execution, slot consumption, storage volume, and data transfer
- Alarms — Automated notifications when metrics cross predefined thresholds, helping you catch anomalies before they become incidents
- Dashboards — Visual representations that consolidate metrics into a single pane of glass for real-time and historical analysis
Together, these pillars form a feedback loop: metrics inform alarms, alarms trigger investigations, and dashboards provide the context needed to diagnose and resolve issues quickly.
Why BigQuery Monitoring Matters
The serverless nature of BigQuery means you never have to provision or resize clusters. However, this abstraction also obscures operational details that would be visible in a self-managed system. Without monitoring, you risk:
- Runaway costs — A single poorly written query scanning petabytes of data can generate a surprising bill. Monitoring slot consumption and bytes processed catches these queries early.
- Performance degradation — Slot contention during peak hours slows down critical pipelines. Dashboards reveal usage patterns so you can schedule workloads intelligently.
- Compliance violations — Queries accessing sensitive datasets without proper filters may breach data governance policies. Alarms on query patterns help enforce guardrails.
- Resource exhaustion — Storage growth unbounded by lifecycle policies leads to unnecessary costs. Monitoring storage metrics triggers cleanup automation.
In short, monitoring transforms BigQuery from a black box into a transparent, controllable service. It empowers teams to optimize continuously rather than reactively firefighting.
Key Metrics You Should Track
Query Performance Metrics
BigQuery exposes query-level metrics through the INFORMATION_SCHEMA views and Cloud Monitoring. The most important query metrics include:
- Execution time — Elapsed time from query submission to result return. High variance often indicates slot contention or complex query plans.
- Slot milliseconds consumed — The total computational work performed. This metric directly correlates with on-demand costs.
- Bytes processed — Amount of data scanned. Excessive bytes processed suggests missing partition filters or clustering.
- Bytes shuffled — Data moved between query stages. High shuffle volumes indicate join-heavy queries that may benefit from optimization.
- Query error rate — Percentage of queries that fail. Spikes often correlate with schema changes or upstream data quality issues.
Slot Utilization Metrics
Slots are BigQuery's unit of computational capacity. Monitoring slot usage is essential for capacity planning and cost control:
- Allocated slots — Total slots provisioned under your reservation model
- Active slots — Slots currently executing query tasks
- Slot contention ratio — Ratio of demanded slots to available slots. Values above 1.0 indicate queries are queuing
- Average slot utilization percentage — How much of your purchased capacity is actually being used
Storage Metrics
- Total logical bytes — Uncompressed data size across all tables
- Total physical bytes — Compressed and replicated storage actually billed
- Table storage growth rate — Daily or weekly delta to detect unexpected ingestion surges
- Long-term vs. short-term storage distribution — Data older than 90 days incurs lower storage costs; monitoring this split helps forecast expenses
Cost Metrics
- On-demand query costs — Derived from bytes processed at the per-TB rate
- Reservation throughput costs — Prorated hourly charges for committed slots
- Storage costs — Based on physical bytes stored
- Data transfer costs — Cross-region query costs or data egress charges
Querying Metrics Using INFORMATION_SCHEMA
BigQuery's INFORMATION_SCHEMA provides native SQL access to job history, reservations, and capacity data. These views are your primary tool for building custom monitoring logic without external instrumentation.
Analyzing Query Performance History
The INFORMATION_SCHEMA.JOBS view contains execution details for completed queries. The following query retrieves the top 10 most expensive queries in the past 24 hours:
SELECT
creation_time,
project_id,
job_id,
user_email,
query,
total_bytes_processed / POW(1024, 4) AS tb_processed,
total_slot_ms / 1000 AS slot_seconds,
SAFE_DIVIDE(total_slot_ms, TIMESTAMP_DIFF(end_time, start_time, MILLISECOND)) AS avg_active_slots,
end_time - start_time AS duration,
cache_hit,
error_result.message AS error_message
FROM
`region-us`.INFORMATION_SCHEMA.JOBS
WHERE
creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR)
AND statement_type = 'SELECT'
AND state = 'DONE'
ORDER BY
total_bytes_processed DESC
LIMIT 10;
This query gives you immediate visibility into heavy hitters. Pay attention to queries with high avg_active_slots — they may be consuming disproportionate compute resources.
Tracking Slot Utilization Over Time
For reservation-based pricing, monitoring slot utilization across time windows helps identify peak usage periods:
SELECT
TIMESTAMP_TRUNC(period_start, MINUTE) AS time_minute,
SUM(period_slot_ms) / (60 * 1000) AS average_slots_in_that_minute,
MAX(period_slot_ms) / 1000 AS peak_slot_demand_that_minute
FROM
`region-us`.INFORMATION_SCHEMA.JOBS_TIMELINE
WHERE
period_start >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 6 HOUR)
AND job_type = 'QUERY'
GROUP BY
time_minute
ORDER BY
time_minute ASC;
The JOBS_TIMELINE view breaks each job into periodic slices, showing how slot consumption evolves throughout query execution. This granularity is invaluable for understanding concurrency patterns.
Monitoring Reservation Capacity
If you use BigQuery Reservations, the RESERVATIONS view reveals current assignment and utilization:
SELECT
project_id,
reservation_name,
slot_capacity,
baseline_slot_capacity,
target_job_concurrency,
autoscale.current_slots AS autoscaled_slots,
autoscale.max_slots AS max_autoscale_slots
FROM
`region-us`.INFORMATION_SCHEMA.RESERVATIONS
WHERE
reservation_name = 'my-production-reservation';
Identifying Queries Blocked by Slot Contention
When demand exceeds capacity, queries queue. This query finds jobs that waited unusually long before execution:
SELECT
job_id,
creation_time,
start_time,
TIMESTAMP_DIFF(start_time, creation_time, SECOND) AS queue_wait_seconds,
total_bytes_processed / POW(1024, 4) AS tb_processed,
query
FROM
`region-us`.INFORMATION_SCHEMA.JOBS
WHERE
creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
AND state = 'DONE'
AND TIMESTAMP_DIFF(start_time, creation_time, SECOND) > 60
ORDER BY
queue_wait_seconds DESC
LIMIT 20;
A queue wait exceeding a few seconds during business hours often signals that reservations need expansion or workload scheduling needs adjustment.
Cloud Monitoring Metrics for BigQuery
Google Cloud Monitoring automatically collects BigQuery metrics and makes them available through the Metrics Explorer, Alerting, and Dashboards services. These metrics require no setup — they exist as soon as you start using BigQuery.
Key Cloud Monitoring metric names include:
bigquery.googleapis.com/query/cpu_usage— Slot utilization as a fraction of available capacitybigquery.googleapis.com/query/execution_times— Distribution of query latenciesbigquery.googleapis.com/query/scanned_bytes— Total bytes scanned across queriesbigquery.googleapis.com/storage/stored_bytes— Logical storage across datasetsbigquery.googleapis.com/storage/table_count— Number of tables per datasetbigquery.googleapis.com/job/job_count— Total job executions by state
These metrics support filtering by project_id, dataset_id, reservation_name, and priority, enabling targeted analysis of specific workloads.
Setting Up Alarms and Alerts
Alerting on Slot Utilization Spikes
Configure an alert when average slot utilization exceeds 85% of capacity for a sustained period. In the Cloud Console, navigate to Monitoring → Alerting → Create Policy, or use the following gcloud command:
gcloud alpha monitoring policies create \
--display-name="BigQuery High Slot Utilization" \
--condition='resource.type="bigquery_project" AND metric.type="bigquery.googleapis.com/query/cpu_usage" AND metric.labels.reservation="default"' \
--condition-threshold="aggregation.alignmentPeriod=300s, aggregation.perSeriesAligner=ALIGN_MEAN, aggregation.crossSeriesReducer=REDUCE_MEAN, comparison=COMPARISON_GT, threshold=0.85, duration=300s" \
--notification-channel="projects/my-project/notificationChannels/1234567890"
This creates an alert that fires when slot utilization remains above 85% for at least 5 minutes. The duration clause prevents flapping from momentary spikes.
Alerting on Excessive Data Scanned
To catch queries that scan abnormally large volumes — a common source of cost surprises — create an alert on the scanned_bytes metric:
gcloud alpha monitoring policies create \
--display-name="BigQuery Excessive Scan Volume" \
--condition='resource.type="bigquery_project" AND metric.type="bigquery.googleapis.com/query/scanned_bytes"' \
--condition-threshold="aggregation.alignmentPeriod=300s, aggregation.perSeriesAligner=ALIGN_SUM, comparison=COMPARISON_GT, threshold=10995116277760, duration=0s" \
--notification-channel="projects/my-project/notificationChannels/1234567890"
The threshold of 10 TB (10,995,116,277,760 bytes) within a 5-minute window catches queries that could individually cost tens of dollars. Adjust the threshold based on your tolerance and typical workload patterns.
Alerting on Query Failure Rate
Monitor the ratio of failed queries to total queries. A sudden increase often indicates upstream schema changes, permission revocations, or data corruption:
-- Use Cloud Monitoring Query Language (MQL) for ratio-based alerts
-- This MQL expression computes failure rate over a 10-minute window
fetch bigquery.googleapis.com/job/job_count
| filter metric.job_state = 'FAILED'
| align delta 600s
| value val() / (
fetch bigquery.googleapis.com/job/job_count
| filter metric.job_state = 'DONE'
| align delta 600s
| value val()
)
| condition gt(val(), 0.05)
This MQL snippet can be used directly in the Cloud Monitoring alerting UI under the Query Editor tab. It triggers when more than 5% of queries fail within a 10-minute period.
Programmatic Alert Creation with Python
For teams managing alerts as code, the Cloud Monitoring API offers full control. Here's how to create an alert using Python:
from google.cloud import monitoring_v3
from google.protobuf import duration_pb2
import google.api_core.protobuf_helpers
client = monitoring_v3.AlertPolicyServiceClient()
project_name = f"projects/{project_id}"
# Define the condition: average slot utilization > 80%
condition = monitoring_v3.AlertPolicy.Condition()
condition.display_name = "High slot utilization"
condition.condition_threshold = monitoring_v3.AlertPolicy.Condition.MetricThreshold()
condition.condition_threshold.filter = (
'resource.type="bigquery_project" AND '
'metric.type="bigquery.googleapis.com/query/cpu_usage"'
)
condition.condition_threshold.aggregations.append(
monitoring_v3.Aggregation(
alignment_period=duration_pb2.Duration(seconds=300),
per_series_aligner=monitoring_v3.Aggregation.Aligner.ALIGN_MEAN,
cross_series_reducer=monitoring_v3.Aggregation.Reducer.REDUCE_MEAN,
)
)
condition.condition_threshold.comparison = monitoring_v3.ComparisonType.COMPARISON_GT
condition.condition_threshold.threshold_value = 0.80
condition.condition_threshold.duration = duration_pb2.Duration(seconds=300)
# Create the alert policy
policy = monitoring_v3.AlertPolicy()
policy.display_name = "BigQuery - High Slot Utilization"
policy.conditions.append(condition)
policy.notification_channels = [f"projects/{project_id}/notificationChannels/{channel_id}"]
created_policy = client.create_alert_policy(
name=project_name,
alert_policy=policy,
)
print(f"Created alert policy: {created_policy.name}")
This approach integrates naturally with CI/CD pipelines, allowing alert definitions to live alongside infrastructure code in version control.
Building Effective Dashboards
Cloud Monitoring Custom Dashboards
The Cloud Monitoring Dashboards service lets you build visualization widgets without leaving the GCP console. A well-structured BigQuery dashboard should include:
- Slot utilization line chart — Time-series view of average and peak slots consumed, faceted by reservation or project
- Query volume bar chart — Total query count per hour, stacked by status (SUCCEEDED, FAILED, CANCELLED)
- Top expensive queries table — A tabular widget showing the 10 most costly queries in the selected time window
- Storage growth area chart — Logical vs. physical bytes over time to detect unmanaged growth
- Query latency heatmap — Distribution of execution times across percentiles
To create a dashboard widget for slot utilization via the gcloud CLI:
gcloud monitoring dashboards create my-bigquery-dashboard.json \
--description="BigQuery operational dashboard"
Where the JSON configuration file might contain:
{
"displayName": "BigQuery Operations",
"dashboardFilters": [],
"mosaicLayout": {
"columns": 12,
"tiles": [
{
"title": "Slot Utilization (Average)",
"dataSource": {
"type": "TIME_SERIES",
"timeSeriesQuery": {
"timeSeriesFilter": {
"filter": "metric.type=\"bigquery.googleapis.com/query/cpu_usage\" resource.type=\"bigquery_project\"",
"aggregation": {
"alignmentPeriod": "300s",
"perSeriesAligner": "ALIGN_MEAN"
}
}
}
},
"chartOptions": {
"displayVisualization": "LINE"
}
}
]
}
}
Building Rich Dashboards with Looker Studio
For more sophisticated visualizations and cross-service reporting, Looker Studio (formerly Data Studio) connects directly to BigQuery and Cloud Monitoring. A common pattern is to create a monitoring dataset containing aggregated metrics from INFORMATION_SCHEMA and then visualize them in Looker Studio.
First, create a scheduled query that materializes aggregated metrics every hour:
-- Scheduled query: populate monitoring_agg table every hour
INSERT INTO `my-project.monitoring.query_metrics_1h`
SELECT
TIMESTAMP_TRUNC(creation_time, HOUR) AS hour,
user_email,
COUNT(*) AS query_count,
SUM(total_bytes_processed) / POW(1024, 4) AS total_tb_processed,
SUM(total_slot_ms) / 1000 AS total_slot_seconds,
AVG(TIMESTAMP_DIFF(end_time, start_time, MILLISECOND)) / 1000 AS avg_duration_seconds,
COUNTIF(error_result IS NOT NULL) AS error_count
FROM
`region-us`.INFORMATION_SCHEMA.JOBS
WHERE
creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)
AND statement_type = 'SELECT'
AND state = 'DONE'
GROUP BY
hour, user_email;
Then connect Looker Studio to this table and create:
- Time-series charts of query volume and TB processed by user
- Scorecards for current hour's total cost estimate
- Heatmaps showing query activity by hour of day and day of week
- Tables with conditional formatting to highlight users exceeding quotas
Real-Time Dashboard with Streaming Metrics
For near-real-time visibility, stream job completion events into a Pub/Sub topic and consume them in a lightweight dashboard. The BigQuery audit logs feed can be routed to Pub/Sub via Cloud Logging sinks:
# Create a log sink that routes BigQuery audit logs to Pub/Sub
gcloud logging sinks create bq-job-sink \
pubsub.googleapis.com/projects/my-project/topics/bq-jobs \
--log-filter='resource.type="bigquery.googleapis.com/Job" AND protoPayload.methodData.jobInsertRequest.resource.jobConfiguration.query IS NOT NULL'
Then a small Python service can subscribe to this topic, compute rolling metrics, and serve them via a web dashboard or push updates to a monitoring backend:
from google.cloud import pubsub_v1
import json
subscriber = pubsub_v1.SubscriberClient()
subscription_path = subscriber.subscription_path('my-project', 'bq-job-sub')
def callback(message):
data = json.loads(message.data.decode('utf-8'))
job_stats = data.get('protoPayload', {}).get('methodData', {}).get('jobInsertResponse', {})
if job_stats:
billed_bytes = int(job_stats.get('totalBilledBytes', 0))
slot_ms = int(job_stats.get('totalSlotMs', 0))
# Update your metrics store (Prometheus, custom DB, etc.)
# For example, increment a counter and record a histogram
print(f"Job completed: {billed_bytes} bytes, {slot_ms} slot_ms")
message.ack()
subscriber.subscribe(subscription_path, callback=callback)
print("Listening for BigQuery job events...")
Best Practices for BigQuery Monitoring
1. Establish Baseline Thresholds from Historical Data
Before setting alerts, collect at least two weeks of metrics to understand your workload's natural rhythm. Query volume and slot usage often follow predictable daily and weekly patterns. Alerts configured without this context will generate noise and lead to alert fatigue.
2. Tier Your Alerts by Severity
Not all anomalies require immediate pager response. Structure alerts in tiers:
- P1 (Critical) — Slot utilization > 95% for 10+ minutes during business hours; query failure rate > 20%
- P2 (Warning) — Average query latency exceeds baseline by 3x; storage growth deviates > 30% from trend
- P3 (Informational) — New users running expensive queries for the first time; approaching 80% of committed reservation capacity
3. Monitor Per-User and Per-Dataset Granularity
Aggregate metrics can hide problematic behavior. A single user scanning petabytes may be invisible in a project-wide average. Build dashboards that break down metrics by user_email, dataset_id, and reservation_name. Use the INFORMATION_SCHEMA.JOBS view to attribute costs accurately.
4. Automate Response Actions
When an alert fires, automate the first diagnostic step. For example, on a high slot utilization alert, automatically run a query that identifies the top-consuming queries at that moment and logs them to a Slack channel or incident ticket. This reduces mean time to resolution.
5. Version-Control Your Monitoring Configuration
Treat alert policies, dashboard definitions, and scheduled monitoring queries as code. Store them in Git, review changes through pull requests, and deploy via CI/CD. This prevents configuration drift and ensures monitoring evolves alongside your data infrastructure.
6. Leverage the JOBS_TIMELINE for Concurrency Analysis
The JOBS_TIMELINE view is underutilized but extremely powerful. It shows how individual queries consume slots over their lifetime. Use it to identify queries that start with high parallelism and then drop to a single thread — classic indicators of skewed join keys or inefficient UDFs.
7. Set Storage Lifecycle Policies Based on Monitoring Data
Don't guess at storage lifecycle rules. Monitor actual access patterns using INFORMATION_SCHEMA.TABLE_STORAGE combined with query logs to identify tables not queried in 90+ days. Apply lifecycle policies surgically rather than blanket time-to-live rules.
8. Correlate BigQuery Metrics with Upstream Systems
BigQuery performance issues often originate upstream. Correlate slot spikes with Airflow DAG executions, Kafka throughput drops, or Fivetran sync failures. A holistic dashboard that overlays BigQuery metrics with pipeline events provides much richer diagnostic context.
Putting It All Together: A Complete Monitoring Stack
Here's a reference architecture for a production-grade BigQuery monitoring stack:
# Architecture components:
# 1. Scheduled INFORMATION_SCHEMA queries → aggregated metrics tables
# 2. Cloud Monitoring native metrics → Alert policies
# 3. Log sink → Pub/Sub → custom metrics processor → Looker Studio
# 4. Terraform-managed alert policies and dashboards
# Example Terraform snippet for an alert policy
resource "google_monitoring_alert_policy" "high_slot_usage" {
display_name = "BigQuery High Slot Usage"
combiner = "OR"
conditions {
display_name = "Slot utilization above threshold"
condition_threshold {
filter = "metric.type=\"bigquery.googleapis.com/query/cpu_usage\" resource.type=\"bigquery_project\""
duration = "300s"
comparison = "COMPARISON_GT"
threshold_value = 0.85
aggregations {
alignment_period = "300s"
per_series_aligner = "ALIGN_MEAN"
}
}
}
notification_channels = [google_monitoring_notification_channel.email.id]
alert_strategy {
notification_rate_limit {
period = "300s"
}
}
}
Conclusion
Monitoring BigQuery is not an optional add-on — it's a foundational practice that separates efficient, cost-effective data platforms from those plagued by surprises and waste. By instrumenting query performance, slot utilization, storage growth, and cost metrics, you gain the observability needed to operate at scale. The native tools — INFORMATION_SCHEMA views, Cloud Monitoring metrics, and Looker Studio dashboards — provide a comprehensive toolkit that requires no third-party integration to deliver immediate value. Start with the queries and alert configurations outlined in this tutorial, establish your baselines, and iterate your thresholds as your workload evolves. The investment in monitoring pays for itself many times over through prevented cost overruns, faster incident resolution, and continuously optimized query performance.