MySQL 磁盘告警飙到 92%?两条 SQL,5分钟定位根因

大家好,我是老张。
做运维最怕什么告警?磁盘使用率。特别是数据库的磁盘告警——处理慢了业务要停,处理快了怕删错数据。上周就遇到一个典型的案例:Meta 库的 MySQL 磁盘飙到 92%,再不处理就要炸。
📊 告警来了
上午10 点,告警钉过来:
规则:Mysql Pod 磁盘使用率(数据+日志总量) 阈值:> 80% 当前值:92.19% 节点:备节点
一个 80GB 本地 SSD 的 MySQL 8.0 主备实例,磁盘吃到了 92%。先上控制台看一眼。
🔍 第一层:控制台快速确认
控制台看到的磁盘明细:
| 节点 | 数据 | Binlog | 其他日志 | 使用率 |
|---|---|---|---|---|
| 主节点 | 71.41 GB | 455 MB | 108 MB | 89.95% |
| 备节点 | 73.36 GB | 500 MB | 3.5 MB | 92.33% |
一眼就看出问题:Binlog 才 500MB,不是 binlog 堆积导致的。
为什么这么判断?因为 Binlog 清理策略设的是:本地保留 6 小时,最大占用 30%(即 24GB),剩余 < 5GB 或总使用率 > 80% 自动清理。策略正常生效,binlog 很干净。
那就只能是数据本身在涨。 进库看看。
🔬 第二层:按库定位
连上 MySQL,先按库统计大小:
SELECT table_schema AS '数据库',
ROUND(SUM(data_length + index_length) / 1024 / 1024 / 1024, 2) AS '大小(GB)'
FROM information_schema.tables
WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys')
GROUP BY table_schema
ORDER BY 2 DESC;
结果:
| 数据库 | 大小 | 占比 |
|---|---|---|
| billing_db(计量计费库) | 48.69 GB | 77% |
| service_a | 6.78 GB | 11% |
| service_b | 4.90 GB | 8% |
| 其余 97 个库 | ~3 GB | 4% |
billing_db 一个库吃掉 77% 的空间。 范围缩得很快。
🎯 第三层:大表定位
继续往深挖,看 billing_db 内部到底哪些表最大:
SELECT table_name,
ROUND((data_length + index_length) / 1024 / 1024 / 1024, 2) AS '大小(GB)',
table_rows, engine
FROM information_schema.tables
WHERE table_schema = 'billing_db'
ORDER BY data_length + index_length DESC
LIMIT 10;
Top 10 大表:
| 表 | 大小 | 行数 | 说明 |
|---|---|---|---|
| t_bill_detail | 11.20 GB | 622万 | 账单明细 |
| t_charge_record | 8.03 GB | 425万 | 计费记录 |
| t_measure_std | 3.99 GB | 459万 | 标准计量 |
| t_measure_base | 3.10 GB | 336万 | 基础计量 |
| t_settle_detail | 2.75 GB | 433万 | 结算任务 |
| t_idempotent | 2.69 GB | 1049万 | 幂等表 |
| t_measure_raw | 2.52 GB | 350万 | 原始计量 |
| t_instance_msg | 2.51 GB | 23万 | 计量实例信息 |
| t_charge_job | 1.62 GB | 368万 | 计费任务 |
| t_producer_msg | 1.49 GB | 30万 | 出账实例信息 |
全部是计量计费相关表。特别注意到 t_instance_msg 只有 23 万行却占了 2.51GB,查下表结构:
SHOW CREATE TABLE billing_db.t_instance_msg\G
发现三个 longtext 字段(instance_data、order_list、measure_list),存的都是序列化 JSON,单行平均 11KB。业务设计如此,不能砍。
💡 根因确认
定位很清晰:
- 根因:
billing_db计量计费库独占 48.69 GB(77%),80GB 磁盘容量不足 - 定性:不是异常,是业务数据正常累积。计费系统数据只增不减
- Binlog:~500MB,清理策略正常,排除干扰项
至此,排查只花了不到 5 分钟,SQL 也就两三条。
🔧 处理方案
思路
两条路:
- 扩容磁盘:80GB → 200GB,一劳永逸
- 清理数据:如果业务还没正式启用,可以直接清空
实际的情况是计费功能尚未启用,所以可以直接清理,不用扩容。
执行
跟业务确认后,truncate 掉最大的三张表:
USE billing_db;
TRUNCATE TABLE t_bill_detail; -- 释放 11.20 GB
TRUNCATE TABLE t_charge_record; -- 释放 8.03 GB
TRUNCATE TABLE t_measure_std; -- 释放 3.99 GB
📈 清理效果
| 指标 | 清理前 | 清理后 |
|---|---|---|
| 磁盘使用率 | 92% | 61% |
| 主节点数据 | 71.41 GB | 47.72 GB |
| 备节点数据 | 73.36 GB | 49.38 GB |
| 释放空间 | — | ~24 GB |
三张表,回收 24GB,磁盘从 92% 降到 61%,告警消除。
🧠 老张的几点总结
1. 排查要有"排除法"思维
看到磁盘告警,不要上来就清 binlog、清日志。先看一眼 binlog 到底多大,确认是不是它的问题。这次如果一上来就调 binlog 保留策略,等于瞎忙活。
排查路径:控制台确认总量 → 排除 binlog → 库级统计 → 大表定位 → 表结构确认。每一层都在缩小范围。
2. information_schema.tables 是排查利器
两条 SQL 定位问题:
-- 按库统计
SELECT table_schema, ROUND(SUM(data_length + index_length)/1024/1024/1024, 2) AS 'GB'
FROM information_schema.tables GROUP BY table_schema ORDER BY 2 DESC;
-- 库内大表
SELECT table_name, ROUND((data_length + index_length)/1024/1024/1024, 2) AS 'GB', table_rows
FROM information_schema.tables WHERE table_schema='xxx' ORDER BY data_length + index_length DESC;
记住这两条,磁盘排查基本够用。
3. 计量/计费类库要有归档策略
计量数据天然只增不减。80GB 磁盘跑计量库,迟早会满。这类库从一开始就应该:
- 定好数据保留周期(比如只保留最近 3 个月)
- 配置定期归档任务
- 或者直接给够磁盘空间(200GB 起步)
📋 排查清单(建议收藏)
下次遇到 MySQL 磁盘告警,按这个顺序走:
- ☐ 控制台确认磁盘使用明细(数据 vs binlog vs 日志)
- ☐ 确认 binlog 清理策略是否正常
- ☐ 进库按库统计大小
- ☐ 定位大表
- ☐ 确认大表表结构(是否有 longtext/json 大字段)
- ☐ 判断是否业务正常增长 or 异常
- ☐ 扩容 or 清理,选一条路
💬 互动话题
- 你遇到过最头疼的 MySQL 磁盘告警是什么样的?
欢迎在评论区聊聊,老张等着你 👇