1. 问题现象与初步排查
春节假期后第一天上班,我像往常一样检查生产环境MySQL服务状态时,突然发现应用日志中大量报错:"ERROR 3 (HY000): Error writing file '/var/lib/mysql/dbname/tablename.MYD' (Errcode: 28 - No space left on device)"。这个错误看起来像是磁盘空间不足,但当我执行df -lh查看磁盘使用情况时,结果却显示:
code复制Filesystem Size Used Avail Use% Mounted on
/dev/vda1 50G 35G 15G 70% /
明明还有15G的可用空间,为什么MySQL会报磁盘空间不足?这种矛盾现象在Linux系统中其实并不罕见,背后往往隐藏着更深层次的文件系统问题。
经验提示:当
df -lh显示空间充足但系统报"no space"错误时,90%的情况是inode耗尽或特定分区空间不足,需要进一步排查。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. inode耗尽:被忽视的元数据瓶颈
2.1 inode的基本原理
inode是Unix/Linux文件系统中的核心数据结构,每个文件或目录都会占用一个inode。它存储了文件的元信息(权限、所有者、大小等)以及指向实际数据块的指针。关键特性包括:
- 每个文件系统在创建时就固定了inode总数
- 即使磁盘有剩余空间,inode耗尽也会导致无法创建新文件
- 小文件密集型场景特别容易耗尽inode
2.2 如何检查inode使用情况
使用df -i命令查看inode使用率:
code复制Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 3276800 3276800 0 100% /
这个输出清晰地显示inode已经100%耗尽。对比之前的df -lh,我们终于找到了问题根源——磁盘空间充足但inode资源枯竭。
2.3 MySQL场景下的inode消耗特点
MySQL数据库特别容易导致inode耗尽,主要原因包括:
- 表结构设计:使用MyISAM引擎时,每个表会生成.MYD(数据)、.MYI(索引)、.frm(结构)三个文件
- 分区表:每个分区都会产生独立的文件
- 临时文件:复杂查询会生成大量临时表文件
- 二进制日志:未及时清理的binlog会持续累积
3. 应急处理与inode释放方案
3.1 临时解决方案
当生产环境出现inode耗尽时,可以按以下优先级快速释放inode:
- 清理MySQL临时文件:
bash复制rm -rf /tmp/ib* rm -rf /var/lib/mysql/tmp/* - 删除旧的MySQL binlog:
bash复制PURGE BINARY LOGS BEFORE '2023-01-01 00:00:00'; - 查找并删除小文件:
bash复制# 查找占用inode最多的目录 find / -xdev -printf '%h\n' | sort | uniq -c | sort -k1 -n
3.2 长期预防措施
-
调整MySQL存储配置:
ini复制[mysqld] tmpdir = /bigdisk/tmp # 指向空间大的分区 expire_logs_days = 7 # 自动清理binlog -
监控inode使用率:
bash复制# 加入crontab每日检查 df -i | grep -v "Filesystem" | awk '{if($5 > 90) print "WARNING: "$1" inode usage "$5}' -
文件系统规划建议:
- 对MySQL数据目录使用单独分区
- 创建文件系统时指定更高的inode数量:
bash复制
mkfs.ext4 -N 5000000 /dev/sdb1
4. 其他可能原因与排查方法
4.1 用户磁盘配额限制
即使全局空间充足,单个用户也可能达到配额限制:
bash复制# 检查配额
quota -v
repquota -a
4.2 特定分区空间耗尽
df -lh显示的是全局空间,但MySQL可能使用特定挂载点:
bash复制# 查找文件实际所在分区
df -h /var/lib/mysql/dbname/tablename.MYD
4.3 文件系统损坏
极端情况下可能需要修复文件系统:
bash复制umount /dev/vda1
fsck -y /dev/vda1
mount -a
5. 生产环境最佳实践
根据多年运维经验,我总结出MySQL存储管理的黄金法则:
-
分离存储策略:
- /var/lib/mysql → SSD高性能存储
- /backup/mysql → HDD大容量存储
- /tmp → 内存文件系统(tmpfs)
-
监控指标体系:
- 空间使用率(<80%)
- inode使用率(<70%)
- 磁盘IOPS(<70%容量)
- 文件描述符使用量
-
自动化维护脚本:
bash复制#!/bin/bash # 自动清理7天前的binlog mysql -e "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY)" # 清理超过1天的临时文件 find /tmp -type f -mtime +1 -delete -
架构设计建议:
- 避免使用大量小表,考虑合并设计
- 分区表数量控制在100个以内
- 定期归档历史数据
这次故障让我深刻认识到,看似简单的"磁盘空间"问题,实际上需要从文件系统设计、存储规划、监控预警等多个维度建立完整的管理体系。特别是对于数据库这种核心服务,存储管理绝不能停留在简单的df -lh检查层面。
