跳转至

案例:磁盘空间告警,预判扩容和清理大表

场景背景

  • 时间: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 天的备份基本不会用

延伸阅读