general_log写入是同步阻塞操作,每条sql执行前必须完成日志落盘(文件)或表写入(table),无缓冲、不异步,高并发下i/o成瓶颈,导致tps骤降、iowait飙升;file模式触发fsync,table模式全表锁csv写入,二者均不可控;替代方案是slow_query_log+long_query_time=0,仅执行后判定记录,开销更低且更安全。

general_log 写入是同步阻塞操作
MySQL 每执行一条语句,只要 general_log 开启,就会立即将完整 SQL 文本、连接 ID、时间戳等写入日志(文件或表),这个动作发生在语句解析和执行前的阶段,且是同步的。它不走异步队列,也不缓冲——意味着每个请求都得等日志落盘(或写入系统表)完成才能继续。在高并发下,I/O 成为瓶颈,大量线程卡在日志写入环节,CPU 空转等待,TPS 断崖式下跌。
FILE 和 TABLE 两种输出方式性能差异极大
虽然 log_output 可设为 'FILE' 或 'TABLE',但两者都不是“轻量级”:
-
FILE模式:每次写入触发一次fsync()(尤其在sync_binlog=1或日志文件挂载为data=ordered的 ext4 下),磁盘随机小写密集,SSD 也扛不住 -
TABLE模式:日志写入mysql.general_log表,该表默认是 CSV 引擎,无索引、无事务、全表锁写入;每条 INSERT 都是一次独立的磁盘追加,同样无法批量合并
实测显示:QPS 2000+ 场景下开启 general_log,响应延迟从 5ms 跳到 200ms+,CPU iowait 占比超 60%。
日志体积爆炸导致 IO 和磁盘空间双重压力
一条简单 SELECT 1 在 general_log 中记录约 150 字节;按每秒 1000 条算,1 小时就生成 500MB+ 日志。这带来两个实际问题:
- 磁盘吞吐打满,挤占 InnoDB 刷脏页、redo log 写入等关键路径的 IO 带宽
- 日志文件滚动不及时(
flush-logs不自动清理旧文件),/var/log/mysql/分区被撑爆,MySQL 进程可能因写入失败直接退出 - 即使配置了
max_relay_log_size类似机制,general_log 也没有内置轮转逻辑,必须靠外部脚本或 logrotate 手动干预
替代方案:用 slow_query_log + long_query_time=0 更可控
真要捕获全部语句用于分析,slow_query_log 配合 long_query_time=0 是更安全的选择:
- 它只在语句执行**结束后**才判定是否记录,不阻塞执行流程
- 支持
log_output='TABLE'且mysql.slow_log是 MyISAM 表,写入开销显著低于 CSV 的general_log - 可配合
log_queries_not_using_indexes=ON定向抓取低效语句,避免全量噪音
注意:long_query_time=0 在 MySQL 5.7+ 生效,5.6 需设为极小值如 0.000001;且仍需严格控制开启时长——超过 10 分钟就该评估风险。
真正危险的不是“要不要记日志”,而是忘了 general_log 是把双刃剑:它不提供采样、不支持条件过滤、不区分读写、不压缩内容。上线前没压测过 general_log 开启状态的 QPS 曲线,等于没做过容量评估。











