การติดตาม MySQL Server ให้ทำงานได้อย่างมีประสิทธิภาพต้องอาศัย Metrics ที่ครบถ้วนและต่อเนื่อง ทั้ง Query Performance, Connection Usage, InnoDB Buffer Pool, Replication Lag และอื่น ๆ อีกมาก การเก็บข้อมูลเหล่านี้ด้วยตนเองจากคำสั่ง SHOW STATUS หรือ information_schema อาจใช้ได้กับการ debug เฉพาะหน้า แต่ไม่เหมาะกับการติดตามระยะยาว เพราะไม่มี Historical Data สำหรับฐานข้อมูลและไม่มีระบบแจ้งเตือนอัตโนมัติ
mysqld_exporter คือเครื่องมือ Open Source จากทีม Prometheus ที่แปลง Metrics ของ MySQL ให้อยู่ในรูปแบบที่ Prometheus สามารถดึงไปเก็บได้ ผสานกับ Grafana เพื่อทำ Dashboard และ Alertmanager สำหรับส่งแจ้งเตือน ทำให้ได้ระบบ Observability ครบวงจรสำหรับฐานข้อมูล บทความนี้จะอธิบายการติดตั้ง ตั้งค่า และออกแบบ Alert Rules สำหรับ mysqld_exporter แบบ Production-ready
mysqld_exporter คืออะไร
mysqld_exporter เป็น Prometheus Exporter ที่เขียนด้วยภาษา Go ทำหน้าที่เชื่อมต่อกับฐานข้อมูลผ่าน TCP หรือ Unix Socket จากนั้นรันคำสั่ง SQL เช่น SHOW GLOBAL STATUS, SHOW GLOBAL VARIABLES, SELECT จาก information_schema และ performance_schema เพื่อดึง Metrics แล้ว expose ออกเป็น HTTP Endpoint (ค่า default คือพอร์ต 9104) ให้ Prometheus Server scrape ตามรอบเวลาที่กำหนด
ข้อดีหลักของการใช้ exporter แทนการเขียน script เอง คือมี Metrics Naming มาตรฐาน (เช่น mysql_global_status_threads_connected) ทำให้สามารถใช้ Dashboard และ Alert Rules สำเร็จรูปจาก Grafana Labs ได้ทันที ลดเวลาในการ setup จากหลายสัปดาห์เหลือไม่กี่ชั่วโมง
การเตรียม Database User สำหรับ Exporter
ก่อนติดตั้ง exporter ต้องสร้าง User ในฐานข้อมูลที่มีสิทธิ์จำกัดเฉพาะที่จำเป็น ตามหลัก Principle of Least Privilege ห้ามใช้ root หรือ user ที่มีสิทธิ์ ALL PRIVILEGES เด็ดขาด เพราะหาก exporter ถูก compromise จะทำให้ข้อมูลเสียหายทั้งหมด
CREATE USER 'exporter'@'localhost' IDENTIFIED BY 'StrongPassword!@#'
WITH MAX_USER_CONNECTIONS 3;
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.*
TO 'exporter'@'localhost';
FLUSH PRIVILEGES;
คำอธิบายสิทธิ์ที่ให้: PROCESS ใช้สำหรับ SHOW PROCESSLIST, REPLICATION CLIENT ใช้สำหรับ SHOW SLAVE STATUS / SHOW MASTER STATUS, SELECT ใช้สำหรับอ่านข้อมูลจาก performance_schema และ information_schema MAX_USER_CONNECTIONS=3 ป้องกัน exporter เปิด connection เกินจำเป็น
วิธีที่ 1: ติดตั้งจาก Binary
# ดาวน์โหลด binary
VERSION="0.15.1"
wget https://github.com/prometheus/mysqld_exporter/releases/download/v${VERSION}/mysqld_exporter-${VERSION}.linux-amd64.tar.gz
tar xvzf mysqld_exporter-${VERSION}.linux-amd64.tar.gz
sudo mv mysqld_exporter-${VERSION}.linux-amd64/mysqld_exporter /usr/local/bin/
sudo chmod +x /usr/local/bin/mysqld_exporter
# สร้าง user สำหรับรัน exporter
sudo useradd --no-create-home --shell /bin/false mysqld_exporter
# สร้างไฟล์ config สำหรับ credentials
sudo tee /etc/mysqld_exporter.cnf <<EOF
[client]
user=exporter
password=StrongPassword!@#
host=localhost
EOF
sudo chmod 600 /etc/mysqld_exporter.cnf
sudo chown mysqld_exporter:mysqld_exporter /etc/mysqld_exporter.cnf
สร้าง systemd Service
sudo tee /etc/systemd/system/mysqld_exporter.service <<EOF
[Unit]
Description=Prometheus MySQL Exporter
After=network.target mysqld.service
[Service]
Type=simple
User=mysqld_exporter
Group=mysqld_exporter
ExecStart=/usr/local/bin/mysqld_exporter \\
--config.my-cnf=/etc/mysqld_exporter.cnf \\
--collect.info_schema.innodb_metrics \\
--collect.info_schema.innodb_tablespaces \\
--collect.info_schema.processlist \\
--collect.perf_schema.eventsstatements \\
--collect.perf_schema.file_events \\
--collect.slave_status \\
--web.listen-address=:9104
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now mysqld_exporter
sudo systemctl status mysqld_exporter
วิธีที่ 2: ติดตั้งด้วย Docker Compose
version: '3.8'
services:
mysqld_exporter:
image: prom/mysqld-exporter:v0.15.1
container_name: mysqld_exporter
restart: unless-stopped
ports:
- "9104:9104"
command:
- '--mysqld.username=exporter'
- '--collect.info_schema.innodb_metrics'
- '--collect.info_schema.innodb_tablespaces'
- '--collect.info_schema.processlist'
- '--collect.perf_schema.eventsstatements'
- '--collect.slave_status'
environment:
MYSQLD_EXPORTER_PASSWORD: StrongPassword!@#
networks:
- monitoring
depends_on:
- mysql
networks:
monitoring:
external: true
Collectors ที่สำคัญ
mysqld_exporter มี collectors หลายกลุ่ม บางกลุ่มเปิดเป็น default บางกลุ่มต้องเปิดเพิ่มเติมผ่าน command flag การเปิดทุก collector อาจสร้างภาระให้ฐานข้อมูลเกินจำเป็น ดังนั้นควรเลือกเปิดเฉพาะที่ต้องใช้
| Collector | Default | ข้อมูลที่เก็บ |
|---|---|---|
| global_status | เปิด | SHOW GLOBAL STATUS (Threads, Queries, Connections) |
| global_variables | เปิด | SHOW GLOBAL VARIABLES (max_connections, buffer_pool_size) |
| info_schema.innodb_metrics | ปิด | InnoDB Buffer Pool, Row Lock, I/O |
| info_schema.processlist | ปิด | จำนวน query ที่กำลังรัน แยกตาม state/user |
| perf_schema.eventsstatements | ปิด | Top queries by latency, rows examined |
| slave_status | เปิด | Replication lag และ error (เฉพาะ slave) |
| info_schema.tables | ปิด | ขนาด table แต่ละตัว (หนัก — ใช้เฉพาะเมื่อจำเป็น) |
เชื่อม Prometheus กับ Exporter
แก้ไขไฟล์ prometheus.yml เพิ่ม job สำหรับ scrape mysqld_exporter กำหนด interval ตามความละเอียดที่ต้องการ ปกติ 15-30 วินาทีเพียงพอสำหรับการติดตามฐานข้อมูล
scrape_configs:
- job_name: 'mysql'
scrape_interval: 15s
static_configs:
- targets:
- 'db-primary.internal:9104'
- 'db-replica-1.internal:9104'
- 'db-replica-2.internal:9104'
labels:
env: 'production'
role: 'mysql'
relabel_configs:
- source_labels: [__address__]
regex: '([^:]+):.*'
target_label: instance
replacement: '$1'
หลังแก้ไขแล้ว reload Prometheus ด้วย SIGHUP หรือ curl -X POST localhost:9090/-/reload จากนั้นเข้า Prometheus UI ไปที่ Status -> Targets ตรวจสอบว่า target ขึ้นเป็น UP ทุกตัว
Alert Rules ที่แนะนำ
การตั้ง Alert ที่ดีต้องมีทั้ง Warning (เตือนก่อนถึงขั้นวิกฤต) และ Critical (ต้องแก้ทันที) พร้อมกำหนด for duration เพื่อป้องกัน false positive จาก metric spike ชั่วคราว ต่อไปนี้คือ Alert Rules ที่ใช้งานจริงใน Production สำหรับการติดตามสถานะเซิร์ฟเวอร์ฐานข้อมูล
groups:
- name: mysql_alerts
interval: 30s
rules:
- alert: MySQLDown
expr: mysql_up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "MySQL instance down ({{ $labels.instance }})"
description: "mysqld_exporter ไม่สามารถเชื่อมต่อ MySQL ได้เกิน 1 นาที"
- alert: MySQLHighConnections
expr: |
(mysql_global_status_threads_connected
/ mysql_global_variables_max_connections) > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "MySQL connections สูง ({{ $labels.instance }})"
description: "ใช้ connection {{ $value | humanizePercentage }} ของ max_connections"
- alert: MySQLSlowQueries
expr: |
rate(mysql_global_status_slow_queries[5m]) > 1
for: 10m
labels:
severity: warning
annotations:
summary: "Slow queries เพิ่มขึ้นอย่างต่อเนื่อง"
description: "{{ $value }} slow queries/sec ในช่วง 5 นาทีที่ผ่านมา"
- alert: MySQLInnoDBBufferPoolLowHitRate
expr: |
(rate(mysql_global_status_innodb_buffer_pool_reads[5m])
/ rate(mysql_global_status_innodb_buffer_pool_read_requests[5m])) > 0.05
for: 15m
labels:
severity: warning
annotations:
summary: "InnoDB Buffer Pool hit rate ต่ำ"
description: "Miss rate = {{ $value | humanizePercentage }} ควรเพิ่ม innodb_buffer_pool_size"
- alert: MySQLReplicationLag
expr: mysql_slave_lag_seconds > 30
for: 5m
labels:
severity: warning
annotations:
summary: "Replication lag สูง ({{ $labels.instance }})"
description: "Replica ล่าช้ากว่า master {{ $value }} วินาที"
- alert: MySQLReplicationBroken
expr: mysql_slave_status_slave_sql_running == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Replication หยุดทำงาน ({{ $labels.instance }})"
description: "Slave SQL thread ไม่ทำงาน ต้องตรวจสอบทันที"
- alert: MySQLTableLocksWaited
expr: |
rate(mysql_global_status_table_locks_waited[5m]) > 5
for: 10m
labels:
severity: warning
annotations:
summary: "Table lock wait สูง"
description: "เกิด table lock contention {{ $value }} ครั้ง/วินาที"
PromQL Queries ที่ใช้บ่อย
รวม PromQL queries ที่นักพัฒนาและ DBA ใช้งานบ่อยในการวิเคราะห์ประสิทธิภาพและสุขภาพของเซิร์ฟเวอร์ฐานข้อมูล
# QPS (Queries Per Second)
rate(mysql_global_status_queries[5m])
# Connection usage (%)
mysql_global_status_threads_connected
/ mysql_global_variables_max_connections * 100
# InnoDB Buffer Pool Hit Ratio (%)
(1 - rate(mysql_global_status_innodb_buffer_pool_reads[5m])
/ rate(mysql_global_status_innodb_buffer_pool_read_requests[5m])) * 100
# Disk I/O: rows read/written per second
rate(mysql_global_status_innodb_rows_read[5m])
rate(mysql_global_status_innodb_rows_inserted[5m])
# Uptime ของ DB Server (วินาที)
mysql_global_status_uptime
# Table size (MB) จาก info_schema.tables collector
mysql_info_schema_table_size / 1024 / 1024
# Replica lag
mysql_slave_lag_seconds
Grafana Dashboard สำเร็จรูป
ไม่ต้องสร้าง Dashboard เอง สามารถ import จาก Grafana Labs ได้ทันที Dashboard ยอดนิยมที่แนะนำมีดังนี้
- ID 7362 – MySQL Overview: Dashboard มาตรฐานจาก Percona แสดงทุก metric หลัก ครอบคลุม connections, QPS, buffer pool, replication, query cache
- ID 14057 – Exporter Quickstart and Dashboard: Dashboard ที่ออกแบบเฉพาะสำหรับ exporter version ใหม่
- ID 12826 – DB Details: รายละเอียดลึก เช่น InnoDB row operations, lock waits, temp tables
วิธี import เพียงเข้า Grafana -> Dashboards -> Import ใส่ ID แล้วเลือก Prometheus data source ที่เชื่อมกับ mysqld_exporter ระบบจะสร้าง Dashboard พร้อมใช้ทันทีภายในไม่กี่วินาที
mysql_up = 0 หลังติดตั้งใหม่
ปัญหาส่วนใหญ่เกิดจาก authentication ตรวจสอบ 3 จุดหลัก ได้แก่ password ในไฟล์ config ถูกต้องหรือไม่, user exporter ถูกสร้างและมีสิทธิ์ครบหรือไม่ (ดูด้วย SHOW GRANTS FOR ‘exporter’@’localhost’), และ hostname ใน my.cnf ตรงกับ hostname ที่ MySQL อนุญาตหรือไม่ (localhost ไม่เท่ากับ 127.0.0.1 ใน MySQL authentication)
Scrape timeout เพราะ collector หนัก
หากเปิด collector ที่หนักเช่น info_schema.tables บนฐานข้อมูลขนาดใหญ่ อาจทำให้ exporter ตอบกลับเกิน scrape_timeout ของ Prometheus แก้ด้วย 3 วิธี ได้แก่ ปิด collector ที่หนัก, เพิ่ม scrape_timeout เป็น 30s-60s, หรือแยก job ออกเป็น 2 job (light และ heavy) ที่ interval ต่างกัน
Metrics หาย/ไม่ครบ
หากบาง metric ไม่ปรากฏใน Prometheus ตรวจสอบว่า performance_schema ถูกเปิดในฐานข้อมูลหรือไม่ (SHOW VARIABLES LIKE ‘performance_schema’) หากปิดอยู่ต้องเปิดในไฟล์ my.cnf แล้ว restart service และสำหรับ InnoDB metrics ต้องเปิด innodb_monitor_enable ด้วย
# ใน my.cnf
[mysqld]
performance_schema = ON
innodb_monitor_enable = all
Best Practices
- สร้าง exporter user แยกต่างหาก ให้สิทธิ์เฉพาะ PROCESS, REPLICATION CLIENT, SELECT — ห้ามใช้ root
- จำกัด MAX_USER_CONNECTIONS = 3 ป้องกัน exporter กิน connection เกินความจำเป็น
- เปิด collector เท่าที่จำเป็น — หลีกเลี่ยง info_schema.tables ถ้าฐานข้อมูลมี table หลายพันตัว
- รัน exporter บน host เดียวกับฐานข้อมูล (localhost) ลด latency และไม่ต้องเปิด firewall
- ใช้ HTTPS/TLS และ Basic Auth หากต้อง expose exporter ข้าม network
- เชื่อม metrics เข้า Alertmanager และส่งไป Slack/LINE/Email/PagerDuty ตาม severity
- เก็บข้อมูลอย่างน้อย 30 วันสำหรับ trend analysis และ capacity planning
- Review Alert Rules ทุกไตรมาส ปรับ threshold ตาม baseline ที่เปลี่ยนไป
สรุป
mysqld_exporter เป็นเครื่องมือสำคัญที่ช่วยให้การติดตามเซิร์ฟเวอร์ฐานข้อมูลเปลี่ยนจากงานที่ต้องทำด้วยมือเป็นระบบ Observability อัตโนมัติ ผสานกับ Prometheus และ Grafana ให้ Dashboard ครบถ้วน รวมถึง Alertmanager สำหรับแจ้งเตือนก่อนระบบล่ม หัวใจของการใช้งานที่ดีคือการสร้าง user ที่มีสิทธิ์จำกัด เปิดเฉพาะ collector ที่จำเป็น และตั้ง Alert Rules ที่มีทั้ง Warning และ Critical พร้อม for duration ที่เหมาะสม
ทีมที่ต้องดูแลฐานข้อมูลหลายเครื่อง ควรนำ exporter มา deploy ทุก instance ตั้งแต่วันแรกที่เปิดใช้งาน จะช่วยให้มี Historical Data สำหรับวิเคราะห์ปัญหาในอนาคต และสามารถทำ Capacity Planning ได้อย่างแม่นยำตามข้อมูลจริง ไม่ใช่การคาดเดา

