MySQL 8.0中动态开启general_log须按序执行:先SET GLOBAL log_output = 'FILE',再SET GLOBAL general_log_file = '/var/log/mysql/general.log'(确保写权限),最后SET GLOBAL general_log = 'ON';仅设general_log = 'ON'无效,因log_output若为NONE或TABLE会导致静默失败。

通用查询日志(general_log)不是上线前该开的配置,而是上线后出问题时临时启用的“手术探针”——开错时机、开错方式、不开清理机制,三者任一都会让数据库在十分钟内响应变慢甚至卡死。
如何用 SET GLOBAL 动态开启,且不重启 MySQL
生产环境不能重启,必须走运行时开关。但注意:SET GLOBAL general_log = 'ON' 本身不会生效,除非 log_output 已设为有效目标:
- 先确认输出方式:
SELECT @@log_output;若返回NONE,general_log开了也白开 - 推荐优先设为
FILE:SET GLOBAL log_output = 'FILE'(比TABLE安全、轻量) - 再设日志路径(需 MySQL 进程有写权限):
SET GLOBAL general_log_file = '/var/log/mysql/general.log' - 最后启用:
SET GLOBAL general_log = 'ON' - 验证是否真在写:
tail -f /var/log/mysql/general.log看是否有新行实时追加
为什么不要用 TABLE 输出模式查问题
log_output = 'TABLE' 表面方便(SELECT * FROM mysql.general_log),实际是性能陷阱:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
mysql.general_log是CSV引擎表,每次 INSERT 都触发 fsync,QPS 超过 200 就明显拖慢主线程 - 日志写入和查询共用同一套 buffer pool,容易挤占业务查询内存
- 表无索引,
SELECT历史记录时会全表扫描,查一次等于给库加压一次 - 更隐蔽的问题:如果误设
log_output = 'TABLE,FILE',MySQL 会双写,I/O 压力翻倍
开完必须立刻做的三件事
通用日志不是“开了就能看”,它本身就会制造问题。上线排查期间必须同步控制风险:
- 加文件轮转:用
logrotate配置每日切割 + 保留 3 天,避免单文件突破 2GB 导致tail卡死或磁盘写满 - 限定开启时长:用
at或简单脚本,在 15 分钟后自动执行SET GLOBAL general_log = 'OFF'(别依赖人肉关) - 过滤敏感字段:如果应用连接带明文密码(如
mysql -uadmin -ptest123),日志里会直接出现Connect admin@localhost as anonymous on test123—— 这类场景必须提前改用 socket 或 SSL 认证,不能靠日志脱敏
查到问题后怎么快速定位原始 SQL
日志内容不是拿来全文搜索的,要盯住三个关键字段组合:
- 时间戳(精确到微秒)+
thread_id:能对应上SHOW PROCESSLIST里的活跃线程,确认是不是那个卡住的会话 -
command_type为Query的行,其argument字段才是真实 SQL;但注意它可能被截断(默认最大 1024 字节),超长语句看不到结尾 - 如果看到大量重复的
Connect→Quit,说明应用没复用连接,极可能是连接池配置错误,不是 SQL 本身问题
真正难的从来不是“怎么开日志”,而是判断“哪一行日志对应哪个请求”——因为通用日志不记录应用层 trace_id、HTTP path 或调用堆栈,它只忠实地记下 MySQL 收到的字节流。一旦应用并发高、SQL 动态拼接多,日志就会变成需要交叉比对时间、线程、上下文才能还原现场的“密码本”。

















