Database เป็นหัวใจของระบบ application ส่วนใหญ่ — เมื่อฐานข้อมูลช้าหรือล่ม ผลกระทบจะลามไปทั่วทั้งระบบ การ monitor ที่ดีจึงเน้นการตรวจจับสัญญาณเตือนก่อนที่จะกลายเป็นปัญหา ไม่ใช่รอให้ user แจ้งว่าช้า บทความนี้อธิบาย metric สำคัญสำหรับ MySQL และ PostgreSQL พร้อม query สำหรับ debug และแนวทาง alert rule
ทั้ง MySQL และ PostgreSQL มีปรัชญาการออกแบบต่างกัน แต่ metric พื้นฐานที่ต้องดูคล้ายกัน — throughput, latency, connection, lock, buffer hit ratio และ replication lag
Metric หลักที่ต้อง Monitor
- QPS/TPS — จำนวน query/transaction ต่อวินาที
- Query Latency — p50, p95, p99 ของเวลาที่ query ใช้
- Slow Query Count — จำนวน query ที่ใช้เวลาเกิน threshold
- Connection Count — active, idle, max_connections
- Buffer/Cache Hit Ratio — % ของ request ที่อ่านจาก memory ไม่ใช่ disk
- Lock & Deadlock — จำนวน row/table lock, deadlock ที่เกิดขึ้น
- Replication Lag — เวลาที่ replica ตามหลัง primary
- Disk I/O — read/write throughput, IOPS, queue length
- Table Bloat — (PostgreSQL) พื้นที่ที่สูญเสียจาก MVCC
MySQL Monitoring
MySQL มี built-in views ใน information_schema และ performance_schema ที่ใช้ตรวจสุขภาพได้ทันที โดยไม่ต้องติดตั้ง tool เพิ่ม
-- connection stats
SHOW STATUS LIKE 'Threads_%';
SHOW STATUS LIKE 'Max_used_connections';
SHOW VARIABLES LIKE 'max_connections';
-- query counters
SHOW GLOBAL STATUS LIKE 'Com_%';
SHOW GLOBAL STATUS LIKE 'Questions';
SHOW GLOBAL STATUS LIKE 'Slow_queries';
-- InnoDB buffer pool hit ratio
SELECT
(1 - (SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_STATUS
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') /
(SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_STATUS
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests')) * 100 AS hit_ratio_percent;
-- find top slow queries
SELECT digest_text, count_star, avg_timer_wait/1e9 AS avg_ms
FROM performance_schema.events_statements_summary_by_digest
ORDER BY avg_timer_wait DESC LIMIT 10;
-- replica lag
SHOW REPLICA STATUS\G -- look at Seconds_Behind_Source
PostgreSQL Monitoring
PostgreSQL มี view ชื่อ pg_stat_* ครอบคลุม database, table, index, activity และ replication — รวมทั้ง extension pg_stat_statements ที่ต้องเปิดใน shared_preload_libraries สำหรับเก็บสถิติ query level
-- active connections
SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state;
-- cache hit ratio (> 99% คือดี)
SELECT sum(heap_blks_read) AS disk_read,
sum(heap_blks_hit) AS cache_hit,
sum(heap_blks_hit) * 100.0 /
nullif(sum(heap_blks_hit) + sum(heap_blks_read), 0) AS hit_pct
FROM pg_statio_user_tables;
-- top slow queries (ต้องเปิด pg_stat_statements)
SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC LIMIT 10;
-- replication lag
SELECT client_addr,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes
FROM pg_stat_replication;
-- table bloat (ประมาณ)
SELECT schemaname, relname,
n_dead_tup, n_live_tup,
round(n_dead_tup * 100.0 / nullif(n_live_tup + n_dead_tup, 0), 2) AS dead_pct
FROM pg_stat_user_tables
ORDER BY dead_pct DESC NULLS LAST LIMIT 10;
Threshold และ Alert Rule
| Metric | Warning | Critical |
|---|---|---|
| Connection % ของ max | > 70% | > 90% |
| Buffer/Cache Hit Ratio | < 95% | < 90% |
| Replication Lag | > 10s | > 60s |
| Slow Query Rate | > 5/min | > 20/min |
| Deadlock Rate | > 1/min | > 5/min |
| Disk Usage (DB) | > 75% | > 90% |
| Dead Tuple % | > 10% | > 25% |
Exporter สำหรับ Prometheus
- mysqld_exporter — ดึง metric จาก MySQL/MariaDB (port 9104) รองรับ performance_schema, innodb, replication
- postgres_exporter — ดึง metric จาก PostgreSQL (port 9187) รองรับ custom queries ผ่าน config YAML
- pg_stat_monitor — wrapper แบบขยาย pg_stat_statements ให้มี bucket แบ่งตามเวลา
# docker-compose.yml - MySQL exporter
version: '3.8'
services:
mysqld-exporter:
image: prom/mysqld-exporter:latest
environment:
- DATA_SOURCE_NAME=exporter:password@(db:3306)/
ports:
- "9104:9104"
command:
- --collect.info_schema.processlist
- --collect.info_schema.innodb_metrics
- --collect.info_schema.tables
- --collect.perf_schema.eventsstatements
Prometheus Alert Rule ตัวอย่าง
groups:
- name: database
rules:
- alert: MySQLHighConnections
expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections > 0.9
for: 5m
annotations:
summary: "MySQL {{ $labels.instance }} ใช้ connection เกิน 90%"
- alert: PostgresLowCacheHit
expr: rate(pg_stat_database_blks_hit[5m]) /
(rate(pg_stat_database_blks_hit[5m]) + rate(pg_stat_database_blks_read[5m])) < 0.95
for: 15m
annotations:
summary: "Postgres cache hit ต่ำกว่า 95% ติดต่อกัน — ตรวจ shared_buffers"
- alert: ReplicaLagHigh
expr: mysql_slave_lag_seconds > 60
for: 5m
annotations:
summary: "Replica lag ของ {{ $labels.instance }} สูงกว่า 60 วินาที"
- alert: DeadlockSpike
expr: rate(mysql_global_status_innodb_deadlocks[5m]) * 60 > 5
for: 5m
annotations:
summary: "Deadlock rate สูงผิดปกติบน {{ $labels.instance }}"
Workflow การ Debug Slow Query
- Step 1: เปิด slow_query_log (MySQL) หรือ log_min_duration_statement (Postgres) ให้บันทึก query ที่เกิน threshold
- Step 2: ใช้ pt-query-digest (MySQL) หรือ pg_stat_statements (Postgres) หา top offender
- Step 3: รัน
EXPLAIN ANALYZEดู query plan — มองหา Seq Scan บนตารางใหญ่, nested loop ที่ไม่ควร, สถิติที่ไม่ตรง - Step 4: ตรวจ index ที่เกี่ยวข้อง — missing index หรือ cardinality ต่ำ
- Step 5: ดู connection pool — ถ้าจำนวน connection สูงและเกือบหมด อาจเป็น connection leak ไม่ใช่ DB ช้า
Best Practices
- ใช้ user แยกสำหรับ exporter — มีสิทธิ์แค่ SELECT, PROCESS, REPLICATION CLIENT เท่าที่จำเป็น
- Monitor ทั้ง primary และ replica — อย่า monitor แค่ primary เพราะ replica อาจมีปัญหา lag/disk/lock แยก
- เก็บ query plan snapshot — เมื่อ query plan เปลี่ยน (optimizer เลือก plan ใหม่หลัง ANALYZE) พฤติกรรมจะเปลี่ยนทันที
- ตั้ง alert vacuum freeze — PostgreSQL ต้อง vacuum ก่อน xid wraparound (2^31) — ถ้าปล่อยจนถึงจะ readonly
- Alert disk ให้เผื่อ binlog/WAL — database ไม่ใช่แค่ datadir พังบ่อยเพราะ log เต็ม
- Dashboard แยกระหว่าง OLTP กับ OLAP — metric ที่สำคัญต่างกัน (OLTP เน้น latency, OLAP เน้น throughput)
สรุป
Database monitoring ที่ดีต้องครอบคลุมทั้ง 3 ชั้น — resource (CPU, disk, memory), throughput (QPS, connection) และ query quality (slow query, lock, replication lag) การใช้ built-in views (information_schema, pg_stat_*) ร่วมกับ exporter สำหรับ Prometheus ทำให้เห็น trend ระยะยาวได้ และสามารถตั้ง alert ที่ fire ก่อนปัญหาจะลุกลาม
อย่าลืม monitor replica แยกจาก primary และเก็บ query plan snapshot ไว้เปรียบเทียบ เพราะหลายครั้งปัญหา production ไม่ได้เกิดจาก load สูงขึ้น แต่เกิดจาก optimizer เปลี่ยน plan หลังสถิติอัปเดต การมี baseline และ alert ที่ตรงจุดจะช่วยย่น mean time to detect และทำให้ DBA มีเวลาแก้ก่อนกระทบ end user

