核心技术点:MySQL性能指标采集、Grafana数据源优化、业务级监控体系构建
一、监控体系设计:从"救火式"到"预测式"的进化
那个漏报的监控告警
我们最初的监控体系就是个"花瓶":CPU、内存、磁盘IO,三大件配齐就以为高枕无忧。结果慢查询把数据库打挂时,CPU才到70%,监控系统一脸无辜:"指标正常啊"。
MySQL监控的四个层次:
-
基础设施层:CPU/内存/磁盘/网络(基础但不够)
-
MySQL实例层:连接数/缓存命中率/锁状态(关键指标)
-
查询性能层:慢查询/全表扫描/临时表(问题根源)
-
业务影响层:事务成功率/响应时间P99(最终影响)
完整的监控指标清单
essential_metrics:
-
global_status: # SHOW GLOBAL STATUS
- threads_connected
- threads_running # 关键!真正在执行查询的线程
- innodb_buffer_pool_reads
- innodb_row_lock_time_avg
-
global_variables: # SHOW GLOBAL VARIABLES
- max_connections
- innodb_buffer_pool_size
-
performance_schema: # 性能模式数据
- events_statements_summary_by_digest
- file_summary_by_event_name
-
sys_schema: # sys库的友好视图
- statement_analysis
- schema_table_statistics
-
监控数据采集的三种武器
我们对比过各种采集方案,每种都有适用场景:
方案1:Prometheus MySQL Exporter(简单但肤浅)
# prometheus.yml 配置
scrape_configs:
- job_name: 'mysql'
static_configs:
- targets: ['mysql-exporter:9104']
params:
collect[]:
- global_status
- innodb_metrics
- performance_schema
# 缺点:采集间隔固定,可能错过瞬时高峰
方案2:Percona Monitoring Plugins(专业但复杂)
# 安装Percona监控套件
#!/bin/bash
wget https://www.percona.com/downloads/percona-monitoring-plugins.tar.gz
tar -xzf percona-monitoring-plugins.tar.gz
cd percona-monitoring-plugins
# 需要配置cron job定期收集数据
# 优点:指标全面,缺点:部署维护复杂
方案3:自定义采集脚本(灵活但需自研)
#!/usr/bin/env python3
# custom_mysql_monitor.py
import pymysql
import time
def collect_deep_metrics():
conn = pymysql.connect(host='localhost', user='monitor')
with conn.cursor() as cursor:
# 采集锁等待信息
cursor.execute("SHOW ENGINE INNODB STATUS")
innodb_status = parse_innodb_status(cursor.fetchone()[0])
# 采集当前活动连接
cursor.execute("SHOW PROCESSLIST")
processlist = analyze_processlist(cursor.fetchall())
return {**innodb_status, **processlist}
实战选择:中小团队用Prometheus Exporter快速上手,大型企业用Percona全面监控,特殊需求自己写脚本补充。
二、Grafana数据源配置:MySQL直连的陷阱与机遇
那个拖垮Grafana的"全表扫描"
我们曾经让Grafana直连生产MySQL,结果一个仪表盘查询SELECT * FROM big_table,直接触发全表扫描,业务查询被阻塞。
Grafana直连MySQL的三大坑:
- 查询性能影响:复杂查询可能拖垮生产库
- 连接数占用:Grafana每个面板一个连接,可能占满连接数
- 安全性风险:需要给Grafana生产库读权限
优化方案:查询代理层 + 只读从库
-- 为Grafana创建专用账号
CREATE USER 'grafana_monitor'@'%' IDENTIFIED BY 'strong_password';
GRANT SELECT ON performance_schema.* TO 'grafana_monitor';
GRANT SELECT ON sys.* TO 'grafana_monitor';
-- 关键:限制最大查询时间
SET SESSION MAX_EXECUTION_TIME = 10000; -- 10秒超时
数据源优化配置模板
# grafana.ini 数据源配置
[database]
type = mysql
host = mysql-readonly:3306
name = grafana
user = grafana_monitor
password = ******
ssl_mode = disable
# 连接池配置(防止连接泄露)
max_open_conn = 25
max_idle_conn = 25
conn_max_lifetime = 14400
# 查询优化
query_retries = 3
query_timeout = 30
[metrics]
# 启用内置指标监控,监控Grafana自身性能
enabled = true
interval_seconds = 60
三、核心监控仪表盘设计实战
实例级监控:数据库健康度的"心电图"
我们设计了一套"四象限"监控大屏:
第一象限:资源使用率
- CPU使用率(系统vsMySQL进程)
- 内存使用(Buffer Pool命中率关键)
- 磁盘使用量和IOPS
第二象限:连接与线程
- 总连接数 vs 最大连接数
- 活跃线程数(threads_running)
- 线程缓存命中率
第三象限:InnoDB引擎状态
- Buffer Pool命中率(<99%要报警)
- 锁等待时间
- 日志写入量
第四象限:查询性能
- 慢查询数量
- 全表扫描比例
- 临时表创建数量
sql
-- Buffer Pool命中率计算(关键指标)
SELECT
(1 - (variable_value / (SELECT variable_value
FROM information_schema.global_status
WHERE variable_name = 'innodb_buffer_pool_read_requests')))
* 100 AS hit_rate
FROM information_schema.global_status
WHERE variable_name = 'innodb_buffer_pool_reads';
慢查询分析:找到真正的"罪魁祸首"
我们曾经被SHOW PROCESSLIST骗过------显示状态是Sending data,其实是表设计问题导致的全表扫描。
慢查询监控升级方案:
sql
-- 启用性能模式慢查询监控(MySQL 5.7+)
UPDATE performance_schema.setup_consumers
SET ENABLED = 'YES'
WHERE NAME LIKE 'events_statements%long%';
-- 查询最耗时的SQL模板
SELECT digest_text,
count_star,
avg_timer_wait/1000000000 as avg_ms,
max_timer_wait/1000000000 as max_ms
FROM performance_schema.events_statements_summary_by_digest
ORDER BY avg_timer_wait DESC
LIMIT 10;
在Grafana中配置这个查询,就能实时看到哪些SQL模板最耗时。
锁竞争监控:隐藏的性能杀手
我们遇到过最诡异的问题:CPU不高,IO正常,但查询就是慢。最后发现是行锁竞争。
锁监控配置:
sql
-- 实时锁等待监控
SELECT r.trx_id waiting_trx_id,
r.trx_mysql_thread_id waiting_thread,
r.trx_query waiting_query,
b.trx_id blocking_trx_id,
b.trx_mysql_thread_id blocking_thread,
b.trx_query blocking_query
FROM information_schema.innodb_lock_waits w
INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;
四、业务级监控:从数据库指标到业务影响
那个价值百万的监控洞察
我们曾经发现订单库的innodb_row_lock_time_avg指标异常,深入排查后发现是某个商家批量更新库存导致的锁等待。优化后,当天订单处理能力提升30%。
业务指标与数据库指标的关联:
sql
-- 业务事务监控视图
CREATE VIEW business_transaction_metrics AS
SELECT
-- 业务指标
COUNT(DISTINCT order_id) as successful_orders,
AVG(processing_time) as avg_processing_time,
-- 关联的数据库指标
(SELECT variable_value FROM global_status WHERE variable_name = 'threads_running') as active_threads,
(SELECT variable_value FROM global_status WHERE variable_name = 'innodb_row_lock_time_avg') as avg_lock_time
FROM order_processing_log
WHERE create_time >= NOW() - INTERVAL 5 MINUTE;
在Grafana中,我们可以把业务成功率与数据库锁等待时间放在同一个面板,一眼就能看出关联性。
容量预测与自动扩容
基于历史数据预测容量需求:
sql
-- 容量预测查询
SELECT
date,
table_schema,
table_name,
data_length,
-- 计算7天增长趋势
AVG(data_length) OVER (
PARTITION BY table_schema, table_name
ORDER BY date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) as avg_growth_rate
FROM table_growth_history
WHERE date >= CURDATE() - INTERVAL 30 DAY;
五、告警配置:从"噪声"到"信号"的进化
那个让我们麻木的"狼来了"告警
最初我们设置了50多个告警规则,结果每天收到几百条告警,团队逐渐麻木。后来精简到15个关键告警,响应速度反而提升了。
MySQL告警黄金清单:
# alert_rules.yml
groups:
- name: mysql_critical
rules:
- alert: MySQLHighRunningThreads
expr: mysql_global_status_threads_running > 20 # 并发线程数
for: 2m
labels:
severity: critical
annotations:
description: '高并发线程数:{{ $value }},可能发生锁竞争'
- alert: MySQLBufferPoolHitRateLow
expr: mysql_global_status_buffer_pool_hit_rate < 95 # 缓存命中率
for: 5m
labels:
severity: warning
- alert: MySQLSlowQueriesHigh
expr: rate(mysql_global_status_slow_queries[5m]) > 10 # 慢查询激增
for: 1m
labels:
severity: warning
智能告警:基于基线动态调整
固定阈值告警太僵化,我们实现了动态基线告警:
sql
-- 动态阈值计算
SELECT
metric_name,
AVG(metric_value) as avg_value,
STDDEV(metric_value) as stddev,
-- 动态阈值 = 平均值 + 2倍标准差
AVG(metric_value) + 2 * STDDEV(metric_value) as upper_threshold
FROM metric_history
WHERE timestamp >= NOW() - INTERVAL 7 DAY
GROUP BY metric_name;
六、性能优化与故障排查实战
那个诡异的"间歇性卡顿"
我们遇到过最头疼的问题:数据库偶尔卡顿几秒,然后自动恢复。最后发现是InnoDB的脏页刷盘导致的。
排查工具箱:
# 实时监控脚本
#!/bin/bash
while true; do
# 检查当前活动线程
mysql -e "SHOW PROCESSLIST" | grep -v Sleep
# 检查InnoDB状态
mysql -e "SHOW ENGINE INNODB STATUS\G" | grep -A 10 "LATEST DETECTED DEADLOCK"
# 检查锁等待
mysql -e "SELECT * FROM information_schema.innodb_lock_waits"
sleep 5
done
Grafana排查仪表盘配置:
- 瞬时卡顿检测:1秒粒度的线程数、锁等待时间
- 历史对比:与上周同时段的性能对比
- 关联分析:数据库指标与应用日志的关联查询
容量规划与预测
基于历史增长趋势预测未来容量需求:
-- 容量预测查询
SELECT
table_schema,
table_name,
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) as size_mb,
-- 预测30天后的大小(基于线性增长)
ROUND(SUM(data_length + index_length) * POWER(1.1, 30) / 1024 / 1024, 2) as predicted_size_mb
FROM information_schema.tables
GROUP BY table_schema, table_name
ORDER BY size_mb DESC;
七、高可用与备份监控
主从复制监控的隐藏陷阱
我们曾经因为主从延迟监控不准确,导致切库时数据丢失。
全面的复制监控:
sql
-- 主从状态深度检查
SELECT
channel_name,
service_state,
last_error_number,
last_error_message,
last_error_timestamp,
-- 真正的延迟时间(考虑时钟同步)
TIMESTAMPDIFF(SECOND, last_processed_timestamp, NOW()) as real_delay_seconds
FROM performance_schema.replication_connection_status;
备份监控策略:
backup_monitoring:
full_backup:
schedule: "0 2 * * 0" # 每周日全量备份
timeout: 4h
verification: true # 备份后验证
incremental_backup:
schedule: "0 2 * * 1-6" # 周一到周六增量
retention: 30d
monitoring:
- backup_duration_seconds # 备份耗时
- backup_size_bytes # 备份大小
- restore_test_status # 定期恢复测试
八、总结与展望
MySQL监控不是装个Exporter就完事了,而是从基础设施到业务影响的完整可观测性体系 。五年来我最大的体会是:好的监控能让你在问题发生前预警,差的监控只能让你事后救火。
核心经验:
- 监控要有层次:从硬件到查询到业务,层层深入
- 告警要精准:减少噪声,关注真正影响业务的指标
- 数据要关联:数据库指标必须与业务表现关联分析
- 容量要预测:基于历史数据预测未来需求
未来挑战:AIOps在数据库监控中的应用如何?能否实现故障自愈?监控数据如何驱动架构优化?