1. 故障现象与影响范围
那天凌晨3点17分,监控系统突然发出刺耳的警报声——数据库服务器的磁盘MBPS指标飙升至正常值的8倍。作为值班工程师,我第一时间登录服务器查看,发现磁盘读写速率持续维持在1200MB/s以上(正常业务时段峰值不超过150MB/s)。这种异常状态直接导致数据库响应时间从平时的20ms激增到1200ms,最终引发全站服务不可用,持续时间长达47分钟。
这次故障影响范围极广:
- 前端API响应超时率达到92%
- 支付系统出现大面积交易失败
- 用户会话频繁中断
- CDN回源请求堆积造成级联故障
关键提示:磁盘MBPS(Megabytes per second)不同于IOPS,它反映的是磁盘吞吐量而非操作次数。当这个指标异常时,往往意味着有大体量数据正在被连续读写。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 故障根因深度分析
2.1 直接诱因:失控的批量作业
通过检查当时的进程快照,发现一个本该在业务低峰期运行的报表生成作业异常活跃。该作业具有以下特征:
- 全表扫描6张超过500GB的核心业务表
- 未使用合理索引导致大量无效I/O
- 并行度设置过高(32线程)
- 缺乏资源使用限制策略
sql复制-- 问题SQL示例(已脱敏)
SELECT * FROM order_detail
WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'
ORDER BY total_amount DESC
2.2 底层磁盘性能瓶颈
我们的监控数据显示磁盘队列长度一度达到78(建议值<2),%Disk Time持续100%。进一步分析发现:
| 指标 | 正常值 | 故障时值 | 风险阈值 |
|---|---|---|---|
| Avg.Disk sec/Read | 5ms | 89ms | >20ms |
| Avg.Disk sec/Write | 3ms | 112ms | >15ms |
| Disk Transfers/sec | 2000 | 9500 | >5000 |
2.3 系统架构的脆弱性
更深层次的问题在于:
- 共享存储架构:所有MySQL实例共用同一套SAN存储
- 缺乏资源隔离:关键业务库与报表库未做物理隔离
- 监控盲区:未对批量作业设置独立的性能监控项
3. 应急处理全记录
3.1 第一阶段:快速止血(0-15分钟)
- 立即识别问题进程:
bash复制iotop -oP -d 5
- 临时限制资源使用:
bash复制cgcreate -g cpu,blkio:/report_job
echo "10000" > /sys/fs/cgroup/blkio/report_job/blkio.throttle.read_bps_device
- 紧急调整MySQL参数:
sql复制SET GLOBAL innodb_io_capacity=200;
SET GLOBAL innodb_flush_neighbors=0;
3.2 第二阶段:服务恢复(15-30分钟)
- 分批重启受影响服务:
bash复制# 采用滚动重启策略
for service in $(cat /tmp/affected_services.list); do
kubectl rollout restart deployment/$service --namespace=production
sleep 90
done
- 启用降级方案:
- 关闭非核心功能(如推荐引擎)
- 静态化首页内容
- 限流策略调整为"拒绝而非等待"
3.3 第三阶段:根因修复(30-47分钟)
- 终止异常作业进程:
sql复制KILL QUERY 48721;
- 清理临时文件释放空间:
bash复制find /var/lib/mysql/tmp -type f -mtime -1 -exec rm -f {} \;
- 重建损坏的索引:
sql复制ALTER TABLE order_detail REBUILD PARTITION ALL;
4. 长效改进方案
4.1 资源隔离策略
- 存储层改造:
- 关键业务库迁移至本地NVMe SSD
- 报表查询专用只读副本部署在独立存储
- Cgroup强制限制:
bash复制# /etc/cgconfig.conf
group report_limits {
cpu {
cpu.cfs_quota_us = 50000;
}
memory {
memory.limit_in_bytes = "16G";
}
blkio {
blkio.throttle.read_bps_device = "252:0 10000000";
}
}
4.2 批量作业优化
- SQL重写原则:
- 禁止全表扫描,强制使用索引提示
- 分页查询必须包含
LIMIT子句 - 大结果集采用游标分批处理
- 执行时间窗口控制:
sql复制CREATE EVENT report_generation
ON SCHEDULE
EVERY 1 DAY
STARTS '2023-08-01 02:00:00'
ENDS '2023-08-01 04:00:00'
DO
CALL generate_daily_report();
4.3 监控体系升级
新增以下监控指标:
- 单SQL语句的磁盘MBPS影响
- 存储阵列各LUN的队列深度
- 批量作业的资源占用趋势图
配置分级告警策略:
code复制规则1: disk_mbps > 300 → 警告
规则2: disk_mbps > 500 + 持续时间>5min → 严重
规则3: disk_mbps > 800 → 立即呼叫
5. 经验总结与避坑指南
5.1 必须建立的检查项
- 批量作业上线前检查表:
- [ ] 执行计划是否避免全表扫描
- [ ] 是否设置合理的执行超时
- [ ] 是否有资源占用评估报告
- [ ] 是否配置执行时间窗口
- 日常巡检重点:
bash复制# 检查磁盘健康状态
smartctl -a /dev/sdX
# 监控InnoDB缓冲池命中率
mysqladmin ext -i10 | grep -E 'Buffer_pool_reads|Buffer_pool_read_requests'
5.2 典型误操作警示
-
切忌在故障时立即重启数据库——这可能导致更长时间的恢复过程。我们曾因此使故障时间延长了3倍。
-
避免盲目调整内核磁盘调度参数。有一次我们将调度器从deadline改为noop后,随机读写性能下降了60%。
-
不要依赖单纯的IOPS监控。某次故障中IOPS看似正常,但MBPS已爆表,导致我们错过了最佳处理时机。
5.3 实用诊断命令集
快速定位磁盘瓶颈:
bash复制# 实时磁盘吞吐
dstat -td --disk-util --disk-tps 5
# 查看哪些文件正在被频繁读写
lsof +D /var/lib/mysql | awk '{print $9}' | sort | uniq -c | sort -nr | head
# MySQL活跃线程I/O统计
SELECT th.THREAD_ID, tr.EVENT_NAME,
tr.NUMBER_OF_BYTES/1024/1024 AS MB_READ,
fs.COUNT_READ, fs.SUM_TIMER_WAIT/1000000000 AS SECONDS
FROM performance_schema.events_waits_history_long tr
JOIN performance_schema.threads th ON tr.THREAD_ID = th.THREAD_ID
JOIN performance_schema.file_summary_by_event_name fs ON tr.EVENT_NAME = fs.EVENT_NAME
ORDER BY MB_READ DESC LIMIT 10;
这次故障给我们的最大教训是:磁盘吞吐量监控必须与查询级监控关联分析。现在我们在Grafana中建立了这样的关联视图,可以立即识别出哪个SQL语句正在"烧毁"磁盘。
