很多人在MySQL遇到问题的时候,第一反应就是打开慢查询日志、binlog,但常常忽略一个更基础、更直接的家伙——general_log。它不像慢查询日志那样只记录执行慢的语句,而是事无巨细地把到达MySQL服务器的每一条请求都记下来,包括连接建立、断开,以及每一条SQL文本。正因为如此,它在绝大多数生产环境里是默认关闭的,也成了很多人眼里“听过但没用过”的功能。这篇就从一个实际运维和开发双修的角度,把这个日志的方方面面讲透。
1. 先搞清楚 general_log 到底是干什么的
1.1 它能记录什么
general_log 是 MySQL 的通用查询日志,核心功能就一句话:记录所有客户端发往 MySQL 服务器的连接事件和 SQL 语句。不管SQL写得好不好、执行快不快、有没有命中索引、最终是成功还是报错,只要MySQL收到了这个语句,就会写进general_log。这一点和慢查询日志有本质区别,慢查询只记录执行时间超过阈值的语句,而general_log不挑不拣,全都收。
举个例子,你在应用里执行了一条 SELECT * FROM users WHERE id = 1,MySQL收到之后,general_log里会依次出现这样的内容:
text复制2025-01-15T10:23:45.123456Z 12345 Query SELECT * FROM users WHERE id = 1
注意这里面的几项信息:
- 时间戳:精确到微秒,记录的是语句到达服务器的时间。
- 线程ID:
12345是MySQL为该连接分配的线程编号,可以用来关联同一个连接的多条SQL。 - 命令类型:
Query表示这是一条查询语句,还有一些其他类型,比如Connect表示连接建立、Quit表示连接断开、Prepare表示预处理语句的prepare阶段。 - SQL原文:完整记录客户端发送过来的SQL文本,一字不差。
很多朋友说general_log“日志量太大、没什么用”,其实是因为没有真正理解它的价值。它最大的用处不是日常监控,而是事后审计和问题回溯。比如有一天线上突然有人改动了一张核心表的数据,业务方坚称“不是我们改的”,这时候binlog只能告诉你“这条语句执行了”,但如果想知道“到底是哪个连接、从哪个客户端发起的”,general_log往往能提供线索。
1.2 它和 binlog、慢查询日志的边界
这里一定要把 general_log 和另外两个常见日志的区别理清楚,很多人一开始学的时候就混淆了。用一张表来说明:
| 对比项 | general_log | binlog | 慢查询日志 |
|---|---|---|---|
| 记录范围 | 所有到达MySQL的SQL/连接事件 | 所有变更数据的操作(DDL/DML) | 执行时间超过阈值的SQL |
| 是否记录SELECT | 记录所有SELECT | 通常不记录(除非开启row格式下的某些情况) | 只记录超时的SELECT |
| 记录时机 | 收到语句就写 | 事务提交时写 | 语句执行完成后判断 |
| 主要用途 | 审计、问题回溯 | 主从复制、数据恢复 | 性能优化 |
| 是否默认开启 | 关闭 | 取决于配置,通常开启 | 关闭 |
从这张表能看出来,三个日志各管一摊。general_log管的是“谁来了、发了什么”,binlog管的是“数据变成了什么样”,慢查询管的是“谁比较慢”。实际排查问题的时候,经常需要把这几类日志搭配起来看,单靠任何一个都不够完整。
我自己就遇到过这样一个场景:线上有个报表统计接口每天凌晨3点会跑一条特别重的SQL,导致数据库CPU飙高。慢查询日志里抓到了这条SQL,但不知道是哪个业务方发的。后来我打开general_log,通过SQL文本和线程ID反查,发现竟然是一个已经下线的老定时任务还在跑,而那个任务所在的机器配置已经很低,跑一次要很久。如果当时没有general_log,这个“幽灵任务”估计还得折腾好几天才能定位。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 打开 general_log 之前先想清楚:日志写到哪里去
2.1 log_output 参数的两种选择
MySQL 里控制 general_log 输出位置的参数叫 log_output,可以设置三个值:FILE、TABLE、NONE。
FILE:写到文件系统,这是最常用的方式。日志文件路径由general_log_file参数指定,可以自定义文件名和路径。写入文件的好处是性能开销相对较小,日志查看方便,用tail -f就能实时看。TABLE:写到 `mys
