第 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 状态变化

✅ 正确做法:
├─ 建立时间窗口基线
├─ 设置有意义的阈值
├─ 定期评审和调整
├─ 包含根本原因而非症状
└─ 最小化告警但最大化信号

总结

有效的监控是理解什么重要,只对可操作的问题告警。逐步构建监控系统:收集数据→建立基线→设置有意义的阈值→根据真实事件演进。最好的监控系统是在用户注意到问题前捕获问题,同时最小化团队的告警疲劳。

本章评分
4.8  / 5  (7 评分)

💬 留言讨论