第 23 章
监控与告警
MySQL 监控与告警完全指南
有效的监控是维护MySQL性能和可用性的关键。本指南涵盖关键指标、工具、告警设计和实践策略。
1. 关键监控指标
查询性能指标:
QPS和延迟:
├─ Questions / 60秒 = QPS
├─ P99延迟应 0.1秒应告警
└─ 建立时间窗口基线
连接监控:
├─ Threads_connected: 当前活跃连接
├─ max_connections: 最大连接数
├─ 告警: > 80% max_connections
└─ 最常见的pool耗尽问题
复制监控:
├─ Seconds_Behind_Master: 复制延迟
├─ 30秒: 严重(需告警)
├─ Slave_IO_Running/Slave_SQL_Running: 复制状态
└─ 任何Not Yes = 立即告警
Buffer Pool:
├─ 命中率 > 99% (目标)
├─ < 95% = 可能问题
├─ 免费buffer < 5% = 内存不足
└─ 监视驱逐率
InnoDB I/O:
├─ Fsync操作 < 100/秒
├─ 读写次数
├─ Pending operations = 0理想
└─ 磁盘成为瓶颈时升级
内存使用:
├─ Created_tmp_disk_tables: 磁盘临时表
├─ 应该很少 (< 10/分钟)
├─ 剧增 = 查询优化问题
└─ 或增加tmp_table_size
2. 监控栈部署
监控架构:
MySQL → mysqld_exporter → Prometheus → Alertmanager → Slack/PagerDuty
↓
Grafana (仪表板)
安装步骤:
1. mysqld_exporter
wget 下载、解压、运行
CREATE USER exporter 权限配置
curl验证 http://localhost:9104/metrics
2. Prometheus配置
设置抓取间隔 (推荐15秒)
配置告警规则 (.yml文件)
指定Alertmanager地址
3. Alertmanager
配置通知渠道 (Slack/Email/PagerDuty)
设置告警路由 (严重级别)
配置告警抑制规则 (避免告警风暴)
4. Grafana
添加Prometheus数据源
创建仪表板 (6行面板)
设置告警通知
关键告警规则:
Critical(立即告警):
├─ 复制停止 (Slave_IO_Running != 'Yes')
├─ 连接池 > 90%
├─ 磁盘空间 30秒
Warning(30分钟内告警):
├─ QPS > 150% 基线
├─ P99延迟 > 2x基线
├─ 磁盘增长率过高
└─ Buffer池命中率 < 95%
Monitoring Stack Components:
├─ mysqld_exporter: MySQL指标导出
├─ node_exporter: 系统指标
├─ Prometheus: 时间序列数据库
├─ Alertmanager: 告警路由
└─ Grafana: 可视化仪表板
3. 性能基线建立
基线收集 (第1周):
各时间窗口的基准指标:
时段 QPS P95延迟 连接数 CPU
─────────────────────────────────────
00-08 150 3ms 5 5%
08-12 800 8ms 20 25%
12-18 1200 10ms 30 40%
18-22 3500 25ms 50 75%
22-00 500 5ms 15 20%
告警阈值(相对基线):
离线时段:
└─ QPS > 2x 基线 = 告警
峰值时段:
├─ QPS > 1.5x 基线 = 告警
├─ P99 > 2x基线 = 告警
└─ 建立时间窗口特定阈值
趋势分析:
周增长率监控:
├─ Week 1: 1000 QPS 平均
├─ Week 2: 1100 QPS (+10%)
├─ Week 3: 1250 QPS (+25%)
└─ 趋势: +15% 每周
└─ 投影: 基线将在6-7周内翻倍
└─ 行动: 在饱和前规划扩展
容量规划:
当前状态 (第4周):
├─ 平均QPS: 1350 (增长)
├─ 峰值QPS: 4500
├─ 连接池: 80/500 (16%)
├─ CPU: 60% 使用率
└─ 磁盘I/O: 500 IOPS (of 5000可用)
8周后投影:
├─ 平均QPS: 2700 (容量3000, 仅13%余量)
├─ 行动: 第6周扩展前达到7000峰值
└─ 不要等到饱和再扩展
4. 常见监控场景
案例1: 复制延迟飙升
─────────────────
症状:
├─ 告警: 延迟 45秒
├─ 无明显慢查询
└─ 数据库看起来正常
调查步骤:
1. SHOW SLAVE STATUS 检查
2. SHOW PROCESSLIST 查看主库长事务
3. 如果是批量操作,等待完成
4. 如果是查询问题,优化或KILL
解决:
├─ 短期: 等待或KILL
├─ 长期: 优化查询,为主库操作添加索引
└─ 预防: 为已知批量操作调整告警
案例2: 连接池耗尽
──────────────
症状:
├─ 490/500 连接
├─ 应用收到 "too many connections"
└─ 连接数突增
调查:
1. SHOW PROCESSLIST 查看谁在连接
2. KILL 僵尸连接 (Sleep时间过长)
3. SET wait_timeout 缩短空闲超时
解决:
├─ 应用层: 使用连接池,设置max_pool_size
├─ 数据库: 增加max_connections
└─ 监控: 连接数达到60%和75%时告警
案例3: Buffer Pool 命中率下降
────────────────────────
症状:
├─ 命中率从99% 降至91%
├─ 查询变慢
└─ 明显的性能下降
调查:
1. 是否有大查询扫描全表?
└─ SELECT * FROM huge_table (4GB 扫描)
2. 是否有批量操作?
└─ LOAD DATA INFILE ... (临时占用内存)
3. 内存是否受限?
解决:
├─ 短期: KILL 低效查询,等待缓冲池预热
├─ 中期: 添加WHERE子句,创建索引
└─ 长期: 增加buffer_pool_size 或分离OLTP/OLAP
案例4: 查询延迟增加
──────────────
症状:
├─ P99延迟从50ms 飙升至180ms
├─ QPS 增加20%
├─ 用户投诉: 网站变慢
调查:
1. SHOW SLOW LOG 查看新的慢查询
2. 发现: SELECT ... WHERE status='active'
└─ 没有索引,需扫描5000万行
3. EXPLAIN 验证
解决:
CREATE INDEX idx_status ON users(status);
└─ 查询时间: 1000ms → 5ms ✓
预防:
├─ 代码审查中检查查询性能
├─ EXPLAIN 新SELECT语句
└─ 用生产数据量测试
5. 最佳实践
告警设计最佳实践:
1. 避免告警风暴
问题: 主库故障 → 10个从库都告警 → 50个仪表板告警
结果: 500+通知 → 告警疲劳
解决: 使用告警抑制规则
├─ 主库故障 (告警)
├─ 自动抑制: 从库连接失败告警
├─ 自动抑制: 复制延迟告警
└─ 结果: 只有1个关键告警 → 人工处理
2. 基于上下文的阈值
❌ 固定: QPS > 1000 告警
└─ 问题: 早上QPS 200时未告警,8PM QPS 4000时也未告警
✅ 基于时间窗口:
├─ 早上 6AM: QPS > 300 (2x基线)
├─ 工作时段: QPS > 2000 (1.67x基线)
├─ 晚上 8PM: QPS > 5500 (1.5x峰值基线)
└─ 捕获真实异常,避免误报
3. 告警级别设计
严重 (立即page):
├─ 复制停止
├─ 磁盘空间 95%
└─ 响应时间: 30秒内处理
警告 (30分钟内通知):
├─ 性能下降 25%+
├─ 磁盘增长率过高
└─ 缓冲池命中率 < 95%
信息 (日志/仪表板):
├─ 连接增加
├─ 慢查询增多
└─ 例行性监控
4. 监控开销
❌ 复杂的监控查询拖累性能
├─ 每次监控查询都 join 5表
├─ 计算聚合 (COUNT/SUM) 超过100M行
└─ 监控CPU占用: 5%+ (太多!)
✅ 轻量级监控:
├─ 使用原生导出器 (mysqld_exporter)
├─ 避免复杂聚合
├─ 缓存结果
└─ 监控开销: < 1% CPU (可接受)
5. 仪表板评审周期
每日 (on-call工程师):
├─ 检查过夜告警
├─ 验证关键指标健康
└─ 5分钟评审
每周 (团队):
├─ 分析告警趋势
├─ 调整告警阈值
└─ 30分钟会议
每月 (运维+工程):
├─ 容量规划讨论
├─ 识别扩展需求
└─ 1-2小时深度分析
6. 定期测试
❌ 假设: "我们的告警会捕获它"
└─ 现实: 告警配置错误,从不触发
✅ 季度演习:
├─ 模拟高QPS: 运行benchmark
├─ 验证告警是否触发
├─ 验证通知到达on-call
└─ 完整工作流测试
常见错误:
❌ 监控系统指标,忽视应用级指标
├─ CPU 90% 但查询仍快速
└─ 应该监控: 查询延迟,用户体验
❌ 告警过多导致漠视
├─ 每天500+告警
└─ 人脑无法处理,重要的也被忽视
❌ 不建立基线
├─ 不知道"正常"是什么
└─ 任何偏离都可能是误报
❌ 复制监控不完整
├─ 仅监控 Seconds_Behind_Master
└─ 忽视 Slave_IO_Running 状态变化
✅ 正确做法:
├─ 建立时间窗口基线
├─ 设置有意义的阈值
├─ 定期评审和调整
├─ 包含根本原因而非症状
└─ 最小化告警但最大化信号
总结
有效的监控是理解什么重要,只对可操作的问题告警。逐步构建监控系统:收集数据→建立基线→设置有意义的阈值→根据真实事件演进。最好的监控系统是在用户注意到问题前捕获问题,同时最小化团队的告警疲劳。