案例:磁盘空间告警,预判扩容和清理大表¶
场景背景¶
- 时间:2026-06-18 周四 09:00
- 现象:Zabbix 告警:jump 数据库服务器磁盘使用率 82%,预计 7 天内爆满
- 数据库:MySQL 8.0.32,业务库
jump,磁盘总容量 100 GB - 环境:云服务,扩容需要提工单,审批周期 1-3 天
- 目标:判断是真要扩容,还是有大表/日志可以清理
生活化比喻:数据库磁盘就像家里的冰箱。平时 60% 满,突然 82% 了。要先看看是买了太多菜(业务数据增长),还是冰箱里堆了过期食品(日志、临时表、碎片),再决定是买新冰箱(扩容)还是清理。
排查步骤¶
1. 查看磁盘空间现状(30 秒)¶
dbskiter --database=jump diagnose space
输出:
空间诊断结果:
====================================
数据库总大小: 23.4 GB
数据文件: 18.2 GB
索引文件: 4.8 GB
日志文件: 0.4 GB (binlog)
临时表空间: 0.0 GB
表空间分布(Top 10):
1. orders 8.5 GB (36.3%)
2. logs 6.2 GB (26.5%)
3. user_actions 3.1 GB (13.2%)
4. payment_records 2.8 GB (12.0%)
5. ...
分析:数据库本身只占 23.4 GB,但磁盘总 100 GB 用了 82 GB。剩下的 58 GB 去哪了?——不是数据库数据本身的问题,可能是其他系统文件、日志、备份占用了。
2. 检查表膨胀和碎片(1 分钟)¶
dbskiter --database=jump diagnose bloat
输出:
表膨胀分析:
====================================
高膨胀表:
1. logs
实际数据: 2.1 GB
表空间占用: 6.2 GB
膨胀率: 195%(大量删除后未回收空间)
建议: OPTIMIZE TABLE 或 ALTER TABLE ENGINE=InnoDB
2. user_actions
实际数据: 1.8 GB
表空间占用: 3.1 GB
膨胀率: 72%
建议: 同上
3. orders
实际数据: 8.1 GB
表空间占用: 8.5 GB
膨胀率: 5%(正常)
发现:
logs表实际数据只有 2.1 GB,但占用了 6.2 GB 空间!膨胀率 195%,这是 InnoDB 删除记录后不释放空间导致的。
3. 容量预测:未来会涨到多少(1 分钟)¶
dbskiter --database=jump monitor capacity-advanced --resource=disk
输出:
容量预测(未来 30 天):
====================================
当前数据库大小: 23.4 GB
历史增长趋势: 0.8 GB/周(最近 4 周平均)
预测:
7 天后: 25.0 GB
14 天后: 25.8 GB
30 天后: 26.8 GB
磁盘总容量: 100 GB
其他占用: 58.6 GB(系统、日志、备份等)
可用空间: 18.0 GB
结论:数据库本身增长平缓,无容量风险
风险来源: 其他系统文件占用 58.6 GB,需排查
关键发现:数据库本身30 天后只增长到 26.8 GB,但磁盘上其他东西占了 58.6 GB。这才是磁盘告警的真正原因。
4. 检查日志文件和备份(1 分钟)¶
# 查看 binlog 占用(MySQL 日志)
dbskiter --database=jump diagnose sql "
SHOW MASTER STATUS;
SHOW BINARY LOGS;
"
# 或者手动登录服务器检查(系统层命令)
du -sh /var/lib/mysql/binlog.*
du -sh /var/log/mysql/
du -sh /backup/
典型发现:
/binlog 日志: 12.3 GB
- 未清理的 binlog 文件: 45 个
- 最早 binlog: 2026-05-01(已过期 48 天)
/backup 目录: 35.2 GB
- 全量备份: 8 个,每个 4.2 GB
- 保留策略: 未配置自动清理
/var/log/mysql: 8.1 GB
- error log: 5.2 GB(未轮转)
- slow query log: 2.9 GB(未启用,但旧日志残留)
元凶锁定: 1. binlog 过期未清理:12.3 GB(应保留 7 天) 2. 备份未清理:35.2 GB(应保留最近 4 个) 3. 日志未轮转:8.1 GB(应自动压缩归档)
合计可回收:约 45 GB,磁盘使用率将从 82% → 37%
解决方案¶
立即清理(可释放 45 GB)¶
# 1. 清理过期 binlog(保留最近 7 天)
dbskiter --database=jump diagnose sql "
PURGE BINARY LOGS BEFORE DATE(NOW() - INTERVAL 7 DAY);
"
# 释放: 约 10 GB
# 2. 清理旧备份(保留最近 4 个全量备份)
# 手动删除 /backup/ 下超过 30 天的备份文件
find /backup -name "*.sql.gz" -mtime +30 -delete
# 释放: 约 25 GB
# 3. 日志轮转(配置 logrotate)
# 手动清空当前 error log(先备份)
cp /var/log/mysql/error.log /var/log/mysql/error.log.$(date +%Y%m%d)
> /var/log/mysql/error.log
# 释放: 约 5 GB
优化表空间(回收 InnoDB 碎片)¶
-- 业务低峰期执行(凌晨 2-4 点)
-- logs 表膨胀率 195%,可以优化
-- 方案 A:OPTIMIZE TABLE(会锁表,小表可用)
OPTIMIZE TABLE logs;
-- 方案 B:ALTER TABLE(Online DDL,MySQL 8.0 不锁表)
ALTER TABLE logs ENGINE=InnoDB;
-- 预期回收: 6.2 - 2.1 = 4.1 GB
注意:
OPTIMIZE TABLE在 MySQL 8.0 对 InnoDB 实际上就是ALTER TABLE ... ENGINE=InnoDB,支持 Online DDL(不锁表或短锁)。
验证效果¶
# 清理后重新检查
dbskiter --database=jump diagnose space
预期结果:
| 项目 | 清理前 | 清理后 | 变化 |
|---|---|---|---|
| 磁盘使用率 | 82% | 37% | -45% |
| 可用空间 | 18 GB | 63 GB | +45 GB |
| binlog | 12.3 GB | 2.1 GB | -10 GB |
| 备份 | 35.2 GB | 8.4 GB | -25 GB |
| logs 表膨胀 | 6.2 GB | 2.5 GB | -3.7 GB |
长期预防措施¶
1. 配置自动清理策略¶
-- MySQL 配置(my.cnf)
[mysqld]
# binlog 自动清理,保留 7 天
expire_logs_days = 7
binlog_expire_logs_seconds = 604800
# 慢查询日志自动轮转
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
# 错误日志轮转(通过 logrotate 配置)
# /etc/logrotate.d/mysql 配置
/var/log/mysql/*.log {
daily
rotate 7
missingok
compress
delaycompress
notifempty
create 640 mysql mysql
postrotate
test -x /usr/bin/mysqladmin && /usr/bin/mysqladmin flush-logs
endscript
}
2. 备份清理脚本(每日执行)¶
#!/bin/bash
# /opt/scripts/cleanup_backup.sh
# 保留最近 4 个全量备份 + 30 天内增量
BACKUP_DIR="/backup"
KEEP_FULL=4
KEEP_DAYS=30
# 删除超过 30 天的备份
find ${BACKUP_DIR} -name "*.sql.gz" -mtime +${KEEP_DAYS} -delete
find ${BACKUP_DIR} -name "*.xbstream" -mtime +${KEEP_DAYS} -delete
# 只保留最近 4 个全量备份
cd ${BACKUP_DIR}
ls -t full_backup_*.sql.gz | tail -n +$((KEEP_FULL + 1)) | xargs -r rm -f
echo "Backup cleanup completed: $(date)"
加入 crontab:
0 3 * * * /opt/scripts/cleanup_backup.sh >> /var/log/backup_cleanup.log 2>&1
3. 设置容量告警(提前预警)¶
# 用 dbskiter 定期巡检
dbskiter --database=jump monitor capacity --resource=disk
# 或者配置 Zabbix 告警阈值
# 磁盘使用率 > 70% 警告,> 80% 严重,> 90% 紧急
4. 表膨胀定期监控¶
# 每月巡检时检查膨胀表
dbskiter --database=jump diagnose bloat
# 膨胀率 > 50% 的表,安排低峰期优化
复盘要点¶
1. 磁盘告警排查口诀¶
磁盘告警 → 看数据库大小 → 看表膨胀 → 看日志/备份 → 看容量预测 → 清理 or 扩容
2. 数据库空间 vs 磁盘空间¶
| 占用来源 | 占比 | 是否可清理 | 排查方法 |
|---|---|---|---|
| 数据库数据文件 | 通常 20-40% | 否(业务数据) | diagnose space |
| 索引文件 | 通常 5-15% | 否(除非删除索引) | diagnose space |
| binlog 日志 | 可能 10-30% | 是(保留 N 天) | SHOW BINARY LOGS |
| 备份文件 | 可能 30-50% | 是(保留策略) | du -sh /backup |
| 系统日志 | 可能 5-15% | 是(logrotate) | du -sh /var/log |
| 临时表空间 | 通常 < 5% | 重启释放 | diagnose space |
关键结论:80% 的磁盘告警不是数据库数据太多,而是日志和备份没清理。
3. 扩容 vs 清理的决策¶
| 情况 | 行动 | 成本 |
|---|---|---|
| 数据库数据增长快,预测 30 天内爆满 | 扩容磁盘 | 云服务按量付费 |
| 数据库数据稳定,日志/备份占大头 | 清理 + 配置自动策略 | 零成本 |
| 表膨胀率高(> 50%) | 优化表(ALTER TABLE) |
低峰期执行,几乎零成本 |
| 所有表都在增长,业务正常扩张 | 扩容 + 归档历史数据 | 长期规划 |
4. 常见误区¶
| 误区 | 正确做法 |
|---|---|
| "磁盘满了,赶紧删数据" | 先分析空间占用构成,80% 情况不需要删业务数据 |
| "binlog 不能删,会丢数据" | 保留最近 7 天即可,已有全量备份 + 增量备份可以恢复 |
| "OPTIMIZE TABLE 会锁表" | MySQL 8.0 支持 Online DDL,不锁表或短锁 |
| "备份越多越好" | 保留最近 4 个全量 + 增量即可,超过 30 天的备份基本不会用 |
延伸阅读¶
- 案例:业务卡顿查慢查询
- 案例:CPU 飙升根因分析
- 案例:连接数打满
- dbskiter 命令:
diagnose space、diagnose bloat、monitor capacity、monitor capacity-advanced