mysql的long_query_time是会话级变量,set global只对新连接生效,当前会话需用set session修改;log_queries_not_using_indexes可捕获无索引查询,但需配合min_examined_row_limit防日志爆炸;slow_query_log_file路径须有mysql用户写权限,动态设置可能不生效,推荐配置文件方式。

直接开干:慢查询日志不是“开了就能用”,long_query_time 的值必须合理,否则要么日志爆炸、要么漏掉真瓶颈。
为什么 set global long_query_time=1 不一定生效
MySQL 的 long_query_time 是会话级变量,但它的全局设置只对**新建立的连接**生效;已存在的连接仍沿用旧值。这意味着你执行了 SET GLOBAL long_query_time = 1,但当前会话里跑的慢 SQL 还是按原来的 10 秒阈值判断。
- 验证当前会话实际生效值:
SELECT @@long_query_time(不是SHOW VARIABLES) - 临时改本会话阈值:
SET SESSION long_query_time = 1 - 想让所有后续连接都生效,才需要
SET GLOBAL,且要确认应用连接池是否重建了连接 -
long_query_time支持小数,比如0.5表示 500ms,但低于 1 秒需谨慎——高并发下日志写入本身会成瓶颈
log_queries_not_using_indexes 是个隐藏开关
默认情况下,即使一条查询只扫描 10 万行却没走索引,只要执行时间 long_query_time,它就不会进慢日志。这会让你错过大量“不慢但极伤”的全表扫描。
- 启用它:
SET GLOBAL log_queries_not_using_indexes = ON - 但它只在
slow_query_log已开启时才起作用 - 注意副作用:如果库中存在大量低效但快速的无索引查询(如
SELECT * FROM config WHERE key='x'),日志量会剧增 - 建议搭配
min_examined_row_limit = 1000使用,只记录扫描行数超 1000 的无索引查询
文件路径权限和重启陷阱
slow_query_log_file 指定的路径,必须是 MySQL 进程用户(如 mysql)有**写权限**的目录,且父目录必须存在。常见错误是路径写错或权限不足,导致开启后日志文件为空,但 SHOW VARIABLES 显示一切正常。
- 检查路径是否可写:
sudo -u mysql touch /path/to/slow.log 2>/dev/null && echo ok || echo fail - 动态设置
slow_query_log_file后,MySQL 不会自动创建文件,也不会报错,只有首次记录慢查询时才尝试写入 - 用配置文件方式(
my.cnf)设置更可靠,但修改后必须重启 mysqld;而SET GLOBAL方式无需重启,但部分参数(如slow_query_log_file)在某些 MySQL 版本中动态修改不生效 - Linux 下推荐路径:
/var/log/mysql/slow.log,并确保/var/log/mysql/目录属主为mysql:mysql
mysqldumpslow 分析前先确认日志格式是否完整
日志里每条记录必须包含 # Time:、# User@Host:、# Query_time: 等头部字段,否则 mysqldumpslow 会跳过整条——常见于日志被截断、编码异常或手动编辑过。
- 快速校验:
head -n 20 /var/log/mysql/slow.log | grep -E '^(# Time:|# User@Host|# Query_time)',应至少看到三组匹配 - 分析 top 10 最慢查询:
mysqldumpslow -t 10 -s t /var/log/mysql/slow.log - 避免误统计:加
-a参数忽略抽象化(如把WHERE id=123归为WHERE id=?),否则相同结构不同参数的查询会被合并 - 真正影响性能的往往不是单次最慢,而是
-s c(按次数排序)里高频出现的中等耗时查询
最容易被忽略的是:日志里 Rows_examined 和 Query_time 的比值。一个 Query_time=0.8s 但 Rows_examined=500000 的查询,比 Query_time=1.2s 但 Rows_examined=100 的更值得优先优化——前者大概率缺索引,后者可能是锁等待或磁盘 I/O 延迟。











