173 lines
5.1 KiB
Markdown
173 lines
5.1 KiB
Markdown
---
|
|
name: database-observability
|
|
license: Apache-2.0
|
|
description: >
|
|
Grafana Cloud Database Observability — query-level performance insights for MySQL and PostgreSQL.
|
|
Covers setup with Grafana Alloy, query samples, visual explain plans, RED metrics, pg_stat_statements
|
|
and Performance Schema integration, and correlation with application traces. Use when monitoring
|
|
database performance, diagnosing slow queries, setting up database observability for MySQL or
|
|
PostgreSQL (self-managed, RDS, Aurora, Azure, Cloud SQL), or correlating DB metrics with APM data.
|
|
---
|
|
|
|
# Grafana Cloud Database Observability
|
|
|
|
> **Docs**: https://grafana.com/docs/grafana-cloud/monitor-applications/database-observability/
|
|
|
|
Provides query-level insights (RED metrics, query samples, explain plans) for MySQL and PostgreSQL
|
|
without application code changes. Generally Available as of April 2026.
|
|
|
|
## Supported Databases
|
|
|
|
| Database | Variants |
|
|
|----------|---------|
|
|
| **MySQL** | Self-managed, RDS MySQL, Aurora MySQL, Cloud SQL MySQL, Azure Database for MySQL |
|
|
| **PostgreSQL** | Self-managed, RDS PostgreSQL, Aurora PostgreSQL, Cloud SQL PostgreSQL, Azure Database for PostgreSQL |
|
|
|
|
## Prerequisites
|
|
|
|
### PostgreSQL
|
|
|
|
```sql
|
|
-- 1. Enable pg_stat_statements in postgresql.conf
|
|
-- shared_preload_libraries = 'pg_stat_statements'
|
|
-- Then restart PostgreSQL
|
|
|
|
-- 2. Create monitoring user
|
|
CREATE USER grafana_monitoring WITH PASSWORD 'secret';
|
|
GRANT pg_monitor TO grafana_monitoring;
|
|
GRANT CONNECT ON DATABASE mydb TO grafana_monitoring;
|
|
|
|
-- 3. Enable the extension
|
|
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
|
|
```
|
|
|
|
### MySQL
|
|
|
|
```sql
|
|
-- Create monitoring user with least-privilege permissions
|
|
CREATE USER 'grafana_monitoring'@'%' IDENTIFIED BY 'secret';
|
|
GRANT SELECT, PROCESS, REPLICATION CLIENT ON *.* TO 'grafana_monitoring'@'%';
|
|
GRANT SELECT ON performance_schema.* TO 'grafana_monitoring'@'%';
|
|
FLUSH PRIVILEGES;
|
|
```
|
|
|
|
## Alloy Configuration
|
|
|
|
### PostgreSQL
|
|
|
|
```alloy
|
|
database_observability.postgres "mydb" {
|
|
data_source_name = "postgresql://grafana_monitoring:secret@localhost:5432/mydb?sslmode=disable"
|
|
|
|
enable_collectors = ["pg_stat_statements", "query_samples", "schema_details"]
|
|
|
|
forward_metrics_to = [prometheus.remote_write.cloud.receiver]
|
|
forward_logs_to = [loki.write.cloud.receiver]
|
|
}
|
|
|
|
prometheus.remote_write "cloud" {
|
|
endpoint {
|
|
url = sys.env("PROMETHEUS_URL")
|
|
basic_auth {
|
|
username = sys.env("PROMETHEUS_USER")
|
|
password = sys.env("GRAFANA_CLOUD_API_KEY")
|
|
}
|
|
}
|
|
}
|
|
|
|
loki.write "cloud" {
|
|
endpoint {
|
|
url = sys.env("LOKI_URL")
|
|
basic_auth {
|
|
username = sys.env("LOKI_USER")
|
|
password = sys.env("GRAFANA_CLOUD_API_KEY")
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### MySQL
|
|
|
|
```alloy
|
|
database_observability.mysql "mydb" {
|
|
data_source_name = "grafana_monitoring:secret@tcp(localhost:3306)/mydb"
|
|
|
|
enable_collectors = ["query_samples", "explain_plans", "schema_details"]
|
|
|
|
forward_metrics_to = [prometheus.remote_write.cloud.receiver]
|
|
forward_logs_to = [loki.write.cloud.receiver]
|
|
}
|
|
```
|
|
|
|
## Key Metrics
|
|
|
|
```promql
|
|
# Query rate by database
|
|
rate(db_query_total{db_instance="mydb"}[5m])
|
|
|
|
# P95 query latency
|
|
histogram_quantile(0.95, rate(db_query_duration_seconds_bucket[5m]))
|
|
|
|
# Error rate
|
|
rate(db_query_errors_total[5m]) / rate(db_query_total[5m])
|
|
|
|
# Slow queries (over 1 second)
|
|
count(db_query_duration_seconds > 1) by (db_query_digest)
|
|
|
|
# Active connections
|
|
db_connections_active{db_instance="mydb"}
|
|
```
|
|
|
|
## What You Get
|
|
|
|
**Query Performance Dashboard** (auto-provisioned):
|
|
- Top queries by total time, call count, mean latency
|
|
- Query samples with actual parameters and timing
|
|
- Visual explain plans showing index usage, scan types, costs
|
|
- RED metrics per query digest
|
|
|
|
**Correlation with APM:**
|
|
- Link slow DB queries to the application traces that triggered them
|
|
- `db.statement`, `db.system`, `db.name` OTel attributes connect DB spans to query samples
|
|
- Drill from service latency spike → specific slow SQL query → explain plan
|
|
|
|
## Alert Rules
|
|
|
|
```yaml
|
|
groups:
|
|
- name: database-observability
|
|
rules:
|
|
- alert: SlowQueryDetected
|
|
expr: histogram_quantile(0.95, rate(db_query_duration_seconds_bucket[5m])) > 1
|
|
for: 5m
|
|
labels:
|
|
severity: warning
|
|
annotations:
|
|
summary: "P95 query latency > 1s on {{ $labels.db_instance }}"
|
|
|
|
- alert: HighDBErrorRate
|
|
expr: rate(db_query_errors_total[5m]) / rate(db_query_total[5m]) > 0.05
|
|
for: 5m
|
|
labels:
|
|
severity: critical
|
|
annotations:
|
|
summary: "DB error rate > 5% on {{ $labels.db_instance }}"
|
|
|
|
- alert: TooManyConnections
|
|
expr: db_connections_active / db_connections_max > 0.8
|
|
for: 5m
|
|
labels:
|
|
severity: warning
|
|
annotations:
|
|
summary: "DB connection pool >80% on {{ $labels.db_instance }}"
|
|
```
|
|
|
|
## Setup Checklist
|
|
|
|
1. Enable `pg_stat_statements` (PostgreSQL) or Performance Schema (MySQL)
|
|
2. Create least-privilege monitoring user
|
|
3. Add `database_observability.*` block to Alloy config
|
|
4. Verify metrics appear in Grafana Cloud → Database Observability
|
|
5. Set up alerting on slow queries and error rates
|
|
6. Enable trace correlation by ensuring app uses `db.statement` span attributes
|