根本原因是general_log写入table模式触发myisam表级锁或innodb元数据锁,导致高并发下线程排队等待;唯一即时缓解方式是set global general_log = 'off'。

MySQL开启general log后性能抖动,根本原因不是“开了日志”,而是写入TABLE模式时触发了串行化表级锁——这不是配置错误,是RDS MySQL的默认设计缺陷。
为什么general_log写入TABLE会导致Waiting for table level lock
general log在RDS MySQL中强制使用mysql.general_log表存储,而该表是MyISAM引擎(5.6/5.7)或非事务性InnoDB表(8.0),写入时需获取MDL锁+表级锁。所有客户端线程必须排队写入,高并发下大量连接卡在Waiting for table level lock状态。
- 即使只开general log、没开slow log,
log_output参数也同时控制两者,设为TABLE即全量生效 - RDS不支持
log_output=FILE,因为用户无法访问实例文件系统,所以TABLE是唯一可选路径 - 锁等待不是瞬时的:单次写入可能耗时数毫秒,QPS 1000+时平均等待会突破100ms
TRUNCATE mysql.general_log不能解决抖动,反而加重问题
执行TRUNCATE TABLE mysql.general_log会触发DDL操作,在MyISAM下等效于重建表,需要独占表锁;在InnoDB下虽快但仍需元数据锁,期间所有general log写入被阻塞,正在等待的线程全部堆积。
- RDS MySQL 5.6明确不支持该命令,执行直接报错
ERROR 1556 (HY000): You cannot 'TRUNCATE' a log table if it's being used for logging - 即使8.0能执行,也会导致正在写入的连接超时断开,重连后继续卡锁
- 清空后新日志立刻开始写入,抖动立即回归,治标不治本
真正有效的缓解路径只有三条
你无法绕过TABLE模式,但可以切断抖动源头:
- 立即执行
SET GLOBAL general_log = 'OFF'——这是唯一能即时释放锁链的操作,无需重启 - 检查
log_output是否被意外设为TABLE,FILE(RDS不支持FILE,但参数值错误会导致内部逻辑异常),应严格设为TABLE - 如果必须保留日志用于排障,改用
SELECT * FROM mysql.general_log ORDER BY event_time DESC LIMIT 1000替代实时监听,避免触发写入放大
后续必须关闭general log,没有例外场景
线上环境不存在“临时开启”的安全窗口:RDS MySQL的mysql.general_log表无自动分区、无TTL、不支持ALTER TABLE ... DROP PARTITION,一旦开启,日志只会无限膨胀并持续锁表。哪怕只开10分钟,也可能因突发流量导致锁队列积压数万连接。
最常被忽略的一点:general_log开关是全局动态参数,但它的关闭动作不会回滚已持有的锁——必须等当前所有写入完成才能彻底释放,所以关之前最好先杀掉长连接或低优先级会话。











