mysql开启general_log会高频触发fsync导致io剧增,应立即set global general_log = off关闭,并清空日志文件内容(如echo '' > /var/lib/mysql/general.log),再于my.cnf中设general_log = 0并重启以永久禁用。

开启 general_log 就等于让 MySQL 每条语句都强制刷一次磁盘,IO 剧增是必然结果,不是配置不当,而是设计使然。
general_log 写入机制本身就会高频触发 fsync
MySQL 8.0 的 general_log 默认输出到文件(log_output = FILE)时,每条 SQL(包括 COM_CONNECT、COM_QUIT、USE db 这类协议级操作)都会调用一次 fwrite() + fflush(),最终触发 fsync() —— 即使你没设 sync_binlog,它也独立走自己的刷盘逻辑。
常见错误现象:
- QPS 500+ 时,
iostat -x 1显示 %util 接近 100%,await突破 50ms -
SHOW PROCESSLIST里大量线程卡在Writing to net或Waiting for query cache lock(实际是日志缓冲区争用)
实操建议:
- 不要依赖
SET GLOBAL general_log = ON后“只开几分钟”,哪怕 60 秒,在高并发下也可能写入数万行,IO 持续冲高 - 关闭后需手动清空日志文件内容(
echo '' > /var/lib/mysql/general.log),否则文件句柄仍被 mysqld 占用,磁盘空间不释放 -
log_output = TABLE在 RDS 环境虽规避了文件 IO,但引入表级锁(见下一条),同样导致系统抖动
log_output = TABLE 在 RDS 中反而引发 Waiting for table level lock
RDS MySQL(5.6/5.7/8.0)强制将 mysql.general_log 表建为 MyISAM(旧版)或非事务性 InnoDB 表(8.0),写入时必须获取 MDL 锁 + 表级锁。所有客户端线程串行排队写入,不是慢,是彻底阻塞。
关键事实:
-
TRUNCATE TABLE mysql.general_log不仅无效,还会加重锁竞争;RDS 5.6 直接报错ERROR 1556 (HY000) -
SELECT * FROM mysql.general_log查询本身不触发写入,但会加读锁,高并发下仍可能干扰写入线程 -
log_output参数同时控制general_log和slow_query_log,设为TABLE意味着两者全量启用
所以 RDS 用户别幻想“换 TABLE 就安全”——它只是把磁盘 IO 压力,转成了锁等待压力。
为什么不能靠调大 innodb_flush_log_at_trx_commit 缓解?
innodb_flush_log_at_trx_commit 只控制 InnoDB redo log 刷盘策略,对 general_log 完全无效。后者走的是独立日志子系统,不受任何 InnoDB 参数影响。
容易踩的坑:
- 误以为关掉
sync_binlog能缓解 general_log IO —— 两者路径完全隔离 - 在 my.cnf 里写了
general_log = 0却忘了执行SET GLOBAL general_log = OFF,导致配置未热生效 - 用
rm -f /var/lib/mysql/general.log清理,结果df不变而du看不到该文件——这是文件句柄未释放的典型表现
真正有效的动作只有两个:立刻 SET GLOBAL general_log = OFF,然后清空文件内容。
最常被忽略的一点:general_log 关闭后,已打开的日志文件描述符不会自动关闭,mysqld 仍可能继续往旧文件刷数据,直到下次 flush logs 或重启。线上环境不存在“临时开启”的安全窗口,关了就得清、清了就得配死。











