防磁盘写爆需OS层与MySQL协同:启用innodb_file_per_table=ON、限制ulimit -f或systemd LimitFSIZE、关闭general_log、规范tmpdir路径、监控日志与临时表。

innodb_data_file_path 不能设 max_size?那怎么防磁盘写爆
MySQL 没有内置的「数据库总大小上限」开关,innodb_data_file_path 里的 max_size 实际上只对单个 ibdata 文件生效,且仅在 innodb_file_per_table=OFF 时起作用——而这个模式现在基本没人用。真要防磁盘被写满,得靠操作系统层+MySQL 配合控制,不是改一个配置就能搞定。
常见错误现象:ERROR 1114 (HY000): The table is full 突然报出,但磁盘还有空间;或者 mysqld 进程因 No space left on device 被内核 OOM kill,日志里却没明显大事务记录。
- 优先启用
innodb_file_per_table=ON(默认已开启),让每张表独立生成.ibd文件,便于后续按库/表做硬链接或配额限制 - 禁用
innodb_undo_log_truncate=OFF(默认 ON),否则 undo 表空间可能持续膨胀且不自动收缩 - 定期检查
information_schema.INNODB_TABLESPACES中FILE_SIZE和ALLOCATED_SIZE,比du -sh更准——因为稀疏文件、预留空间会导致磁盘占用和逻辑大小不一致
用 ulimit + systemd 限制 mysqld 进程能写的磁盘总量
MySQL 自身不提供 per-instance 磁盘配额,但 Linux 的 ulimit -f(file size limit)可以限制进程能创建/追加的文件总大小,对防恶意 INSERT/LOAD DATA 很有效——前提是 MySQL 是以普通用户运行,且没用 prlimit 绕过。
使用场景:多租户共享 MySQL 实例、测试环境防脚本误跑、CI 流水线中临时 DB 容器。
- 在 systemd service 文件(如
/etc/systemd/system/mysqld.service)里加:LimitFSIZE=10G
,单位支持 K/M/G/T,重启服务后生效 - 验证是否生效:
cat /proc/$(pgrep mysqld)/limits | grep fsize,看到Max file size行即表示已加载 - 注意:该限制对已有打开的文件句柄无效,只限制新 write() 和 truncate() 调用;如果某张表已经占了 9.5G,再 insert 一行触发扩展就直接报错
OS error code 28: No space left on device
监控 slow_log + general_log 会悄悄吃光磁盘?关掉或轮转它
很多人以为只有数据文件才占空间,其实 general_log 和 slow_query_log 默认写入文件时,一旦开启又忘了配轮转,几小时就能生成几十 GB 日志,尤其在调试阶段开全量日志。
参数差异:log_output='FILE'(默认)会写进 datadir 下的 hostname.log 或 hostname-slow.log;而 log_output='TABLE' 写进 mysql.general_log 表,虽然可查,但同样占 ibdata1 或对应表空间,且不自动清理。
- 生产环境必须关闭
general_log=OFF;慢日志如需保留,设slow_query_log=ON+long_query_time=1+log_output='FILE',再配合 logrotate 每天切分并压缩 - 检查当前日志路径:
SHOW VARIABLES LIKE 'general_log_file';和SHOW VARIABLES LIKE 'slow_query_log_file'; - 如果已写爆磁盘,别直接
rm日志文件——MySQL 还持有着 fd,磁盘空间不会释放;正确做法是SET GLOBAL general_log = 'OFF';后再echo '' > /path/to/file.log清空
tmpdir 路径不在 datadir 里?临时表和排序可能绕过所有限制
tmpdir 是 MySQL 存放临时表、ORDER BY 排序缓冲、JOIN 缓冲等中间结果的地方。如果它指向根分区(比如 /tmp),而你的 datadir 在 /var/lib/mysql(单独挂载),那即使你给 datadir 加了配额或 ulimit,tmpdir 仍可能被 GROUP BY 大查询写满整个系统盘。
性能影响:tmpdir 若在机械盘或网络存储上,大量临时表还会拖慢查询响应;兼容性上,某些云厂商 RDS 不允许改 tmpdir,只能提工单或换实例类型。
- 务必确认
tmpdir和datadir在同一块有配额的磁盘上:SHOW VARIABLES LIKE 'tmpdir'; - 避免设成
/tmp或/dev/shm(后者内存有限,容易报ERROR 3);推荐设为/var/lib/mysql/tmp并提前chown mysql:mysql - 监控临时表创建频率:
SHOW GLOBAL STATUS LIKE 'Created_tmp%';,若Created_tmp_disk_tables占比高,说明 sort_buffer 或 tmp_table_size 太小,被迫落地磁盘——这反而会加剧磁盘压力
真正难防的是那些不走 InnoDB 引擎的写操作:CSV 表、FEDERATED 表、外部脚本直连写 MyISAM,它们完全绕过 InnoDB 的空间管理逻辑。这类场景下,文件系统级配额(xfs_quota)或容器层限制(docker --storage-opt size=10G)才是最后一道防线。


















