-
Check connection utilization:
- PostgreSQL:
SELECT count(*) AS active_connections, (SELECT setting::int FROM pg_settings WHERE name = 'max_connections') AS max_connections, round(count(*)::numeric / (SELECT setting::int FROM pg_settings WHERE name = 'max_connections') * 100, 1) AS utilization_pct FROM pg_stat_activity
- MySQL:
SELECT VARIABLE_VALUE AS connections FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Threads_connected'
- Alert threshold: utilization above 80%
-
Monitor query throughput and error rate:
- PostgreSQL:
SELECT datname, xact_commit AS commits_total, xact_rollback AS rollbacks_total, xact_rollback::float / GREATEST(xact_commit, 1) AS rollback_ratio FROM pg_stat_database WHERE datname = current_database()
- MySQL:
SHOW GLOBAL STATUS LIKE 'Com_commit' and SHOW GLOBAL STATUS LIKE 'Com_rollback'
- Alert threshold: rollback ratio above 5% or throughput drops more than 50% from baseline
-
Check disk usage and growth:
- PostgreSQL:
SELECT pg_size_pretty(pg_database_size(current_database())) AS db_size and SELECT tablename, pg_size_pretty(pg_total_relation_size(tablename::text)) AS size FROM pg_tables WHERE schemaname = 'public' ORDER BY pg_total_relation_size(tablename::text) DESC LIMIT 10
- Alert threshold: disk usage above 80% or growth rate projecting full disk within 7 days
-
Monitor cache hit ratio:
- PostgreSQL:
SELECT sum(heap_blks_hit)::float / GREATEST(sum(heap_blks_hit) + sum(heap_blks_read), 1) AS cache_hit_ratio FROM pg_statio_user_tables
- MySQL:
SELECT (1 - (VARIABLE_VALUE / (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'))) AS hit_ratio FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads'
- Alert threshold: cache hit ratio below 95% indicates shared_buffers or innodb_buffer_pool_size needs increasing
-
Check vacuum and autovacuum health (PostgreSQL):
SELECT relname, last_vacuum, last_autovacuum, n_dead_tup, n_live_tup, round(n_dead_tup::numeric / GREATEST(n_live_tup, 1) * 100, 1) AS dead_pct FROM pg_stat_user_tables WHERE n_dead_tup > 1000 ORDER BY n_dead_tup DESC LIMIT 10
- Alert threshold: dead tuple percentage above 20% or autovacuum not running for more than 24 hours on active tables
-
Monitor replication lag (if replicas exist):
- PostgreSQL:
SELECT client_addr, state, pg_wal_lsn_diff(sent_lsn, replay_lsn) AS lag_bytes FROM pg_stat_replication
- MySQL:
SHOW REPLICA STATUS\G - check Seconds_Behind_Source
- Alert threshold: lag above 30 seconds or replication stopped
-
Check for long-running queries:
- PostgreSQL:
SELECT pid, now() - query_start AS duration, state, query FROM pg_stat_activity WHERE state != 'idle' AND now() - query_start > interval '5 minutes' ORDER BY duration DESC
- Alert threshold: any query running longer than 10 minutes (OLTP) or 1 hour (analytics)
-
Monitor lock contention:
- PostgreSQL:
SELECT count(*) AS waiting_queries FROM pg_stat_activity WHERE wait_event_type = 'Lock'
- Alert threshold: more than 10 queries waiting for locks simultaneously
-
Compile all health checks into a single monitoring script that runs via cron every 60 seconds, outputs metrics in a structured format (JSON), and triggers alerts when thresholds are breached.
-
Create a health summary dashboard query that returns a single-row result with RAG (Red/Amber/Green) status for each health dimension: connections, throughput, disk, cache, vacuum, replication, queries, and locks.