Database Monitoring: MySQL และ PostgreSQL Health Check

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

MetricWarningCritical
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