通用日志能直接看到客户端发来的原始sql,包括orm拼接后的真实语句、中间件改写结果、空连接或心跳包,是定位“应用执行了但数据库没收到”问题最可靠的手段;需同时满足general_log=on、log_output非none且路径/权限正确,否则日志静默失效。

通用日志能直接看到客户端发来的原始SQL,包括ORM拼接后的真实语句、中间件改写结果、甚至空连接或心跳包——这是定位“应用说执行了,但数据库没收到”这类问题最可靠的手段。
怎么确认 general_log 真的在记录你想要的 SQL
只看 general_log 是 ON 不够,必须三者都对:
-
log_output必须是'FILE'或'TABLE',不能是'NONE'或空值;SHOW VARIABLES LIKE 'log_output';查一下 -
general_log_file路径必须存在,且 MySQL 进程用户(通常是mysql)有写权限;ls -ld /var/log/mysql看属主 - 如果用表方式(
log_output = 'TABLE'),查SELECT COUNT(*) FROM mysql.general_log;是否随新连接增长,比看文件更直观
临时开启时为什么 SET GLOBAL general_log = ON 没反应
常见静默失败原因不是命令错,而是路径或权限卡住:
- 执行
SET GLOBAL general_log_file = '/var/log/mysql/general.log';后,父目录/var/log/mysql/不存在 → MySQL 不会自动创建,得手动mkdir -p /var/log/mysql - 目录存在但属主是
root→chown mysql:mysql /var/log/mysql - SELinux/AppArmor 拦截(尤其 CentOS/RHEL/Ubutnu)→ 临时测试:运行
sudo setenforce 0或sudo aa-disable /usr/sbin/mysqld - 开了
general_log = ON却没设log_output→ 日志可能写进 CSV 表(mysql.general_log),而这张表查起来慢、导出难、无法加索引
从日志里快速抓到异常 SQL 的实操技巧
不要通读日志,按目标反向过滤:
- 文件方式:用
tail -f /var/log/mysql/general.log | grep -E "(SELECT|UPDATE|DELETE|INSERT)"实时盯住 DML - 表方式:查
SELECT * FROM mysql.general_log WHERE argument LIKE '%users%' AND event_time > NOW() - INTERVAL 5 MINUTE;,注意argument是MEDIUMBLOB,模糊匹配要加CONVERT(argument USING utf8mb4)防乱码 - 发现某条 SQL 执行失败?立刻在日志里搜它前后 10 秒的记录:
grep -B 5 -A 5 "UPDATE orders SET status.*" /var/log/mysql/general.log,常能发现前置的USE db_name或连接切换 - ORM 框架报 “No rows affected” 却没执行?日志里找对应线程 ID(
thread_id字段),看它是否真的发了语句,还是被连接池复用导致上下文错乱
关不掉、删不掉、磁盘爆满怎么办
通用日志不是普通文件,操作不当会导致 MySQL 写入异常:
- 别直接
rm /var/log/mysql/general.log→ MySQL 会继续往已删除的 inode 写,磁盘空间不释放,且重启后可能无法重建日志文件 - 正确清空文件方式:
SET GLOBAL general_log = 'OFF';→sudo truncate -s 0 /var/log/mysql/general.log→SET GLOBAL general_log = 'ON'; - 表方式清空:
SET GLOBAL general_log = 'OFF';→TRUNCATE TABLE mysql.general_log;→SET GLOBAL general_log = 'ON'; - 长期开着?QPS 500 的服务一天就能写满 20GB。上线前务必确认监控告警已覆盖
/var/log/mysql/分区使用率
最容易被忽略的是 log_output 和 general_log_file 的生效顺序:先设输出目标,再开开关。很多人只跑第三步,结果日志默默写进数据目录下的主机名.log,而那个路径压根没监控、没轮转、也没权限检查。











