磁盘写满时需立即干预写入路径并释放空间:先确认tmpdir与datadir所在分区,清理ibtmp1或binlog,调整tmpdir、内存参数及expire_logs_days,排查deleted文件。

磁盘写满时大事务无法回滚,直接 kill 会卡住甚至导致实例 hang 住——这不是“等它结束”或“强行 stop”的问题,而是必须立刻干预写入路径和释放空间。
查清 tmpdir 和 datadir 到底写在哪
大事务常触发磁盘写入爆炸,但很多人没意识到 tmpdir 和 datadir 可能不在同一分区。比如 datadir 在 /var/lib/mysql,而 tmpdir 指向 /tmp(CentOS 默认是内存盘,大小受限)。
- 执行
SHOW VARIABLES LIKE 'tmpdir';确认临时文件落盘位置 - 执行
SHOW VARIABLES LIKE 'datadir';确认数据文件位置 - 用
df -h /var/lib/mysql /tmp对比两处使用率,85% 以上就要动手 - 别只盯着
/var/lib/mysql——/tmp满了,COPYING TO TMP TABLE一样报错ERROR 3: Error writing file '/tmp/'
安全清理 ibtmp1 而不是删 ibdata1
ibtmp1 是 InnoDB 的全局临时表空间,大事务中频繁创建/销毁临时表会把它撑爆;但它可以在线收缩,而 ibdata1 一旦删了就等于放弃整个实例。
- 先确认无长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60; - 如果结果为空,执行
ALTER TABLESPACE innodb_temp_tablespaces DISCARD;(MySQL 8.0.13+) - 或更通用的做法:重启 MySQL(需业务允许),重启后
ibtmp1会自动重建为初始大小(通常 12M) -
绝对不要手动
rm ibdata1或ib_logfile*—— 这些是共享表空间和重做日志核心文件,删完实例无法启动
快速释放 binlog 并防止再爆
大事务本身不写 binlog,但事务提交瞬间会一次性刷入大量 binlog,若 expire_logs_days = 7 却长期没 FLUSH LOGS,旧日志堆积极易占满磁盘。
- 查当前最老 binlog:
SHOW MASTER LOGS;,记下第一个文件名(如mysql-bin.000098) - 确认从库已追平(避免误删):
SELECT MASTER_POS_WAIT('mysql-bin.000098', 123456789);(位点取自SHOW MASTER STATUS) - 安全清理:
PURGE BINARY LOGS TO 'mysql-bin.000099';(保留从该文件开始的所有日志) - 补一刀自动策略:
SET GLOBAL expire_logs_days = 3;+FLUSH LOGS;,让下次自动清理更激进
扩容前必须做的三件事
扩容是兜底手段,但盲目扩可能掩盖真实瓶颈。真正关键的是把“写入压力”从磁盘转移到可控路径。
- 改
tmpdir到更大分区:编辑/etc/my.cnf,加一行tmpdir = /data/tmp(确保/data有空间且 MySQL 用户可写),然后systemctl restart mysqld - 调小内存临界值,减少落盘概率:
SET GLOBAL tmp_table_size = 64*1024*1024;、SET GLOBAL max_heap_table_size = 64*1024*1024;(根据可用内存调整) - 检查是否开了
innodb_file_per_table = OFF:如果是,删表不释放空间,后续必须重建表或迁移至新实例,不能靠清理解决
最易被忽略的一点:df -h 显示满,但 du -sh /var/lib/mysql 却不大——这往往说明有已删除但未释放的文件(lsof | grep deleted),得杀掉对应进程或重启 mysqld 才能真正释放。别只盯着“看得见”的文件。











