MySQL慢查询日志开启与优化入门

    发布时间:2026-09-15 12:31 更新时间:2026-09-15 12:31 阅读量:0

    网站跑了一段时间之后,页面偶尔变慢,第一反应往往是“服务器配置不够”。但真正的原因常常藏在数据库里:某条 SQL 没走索引,扫了几十万行,单次执行几百毫秒甚至更久。这类问题平时不显山不露水,一旦并发上来就会拖垮整站。好消息是,MySQL 自带了一套慢查询记录机制,只要打开它,就能把“拖后腿”的语句一条条揪出来。本文面向站长和运维新手,讲清楚怎么开启慢查询日志、怎么设置阈值、怎么用 mysqldumpslow 做汇总,以及怎么判断一条慢 SQL 是不是缺索引。

    开启慢查询日志:先让它记下来

    慢查询日志默认在多数 MySQL 版本里是关闭的,需要手动打开。这里涉及几个核心参数:slow_query_log 控制开关,slow_query_log_file 指定日志文件路径,long_query_time 是判定“慢”的时间阈值,单位是秒,可以带小数。log_queries_not_using_indexes 则会把未使用索引的查询也记下来,调试阶段可以临时开,但线上长期开启容易把日志撑爆,要谨慎。

    修改方式有两种。临时生效可以直接在会话里执行,重启后失效;长期生效建议写进配置文件,比如 /etc/my.cnf 或 /etc/mysql/mysql.conf.d/mysqld.cnf,具体路径以实际环境为准。下面这段配置放在 [mysqld] 段落下:

    [mysqld]
    slow_query_log = 1
    slow_query_log_file = /var/log/mysql/slow.log
    long_query_time = 1
    log_queries_not_using_indexes = 0
    

    改完配置需要重启 MySQL 服务,或者用 SET GLOBAL 动态生效。动态修改的写法如下,注意 long_query_time 对已建立的连接不会立即生效,新连接才会用新值:

    SET GLOBAL slow_query_log = ON;
    SET GLOBAL long_query_time = 1;
    SHOW VARIABLES LIKE 'slow_query_log%';
    SHOW VARIABLES LIKE 'long_query_time';
    

    阈值设多少合适?这取决于业务。刚开始排查问题可以设 0.5 秒甚至更低,先把可疑语句捞出来;稳定运行阶段一般设 1 到 2 秒,既能发现明显问题,又不会产生海量日志。要注意的是,慢查询日志记录的是“执行时间超过阈值”的语句,测试环境数据量小,同一条 SQL 可能跑得飞快,所以优化验证最好在接近生产的数据量下做。

    用 mysqldumpslow 做汇总,别一条条看

    日志文件打开一看,往往成百上千条,人眼逐条翻效率太低。MySQL 自带一个 Perl 脚本 mysqldumpslow,专门用来做聚合统计。它会按“相同语句结构”归并,把数字和字符串替换成占位符,只保留 SQL 骨架,这样同一类查询就能合并计数。

    常用参数里,-s 指定排序方式,比如 c 按次数、t 按总耗时、at 按平均耗时;-t 表示只显示前 N 条;-g 支持按关键字过滤。一个典型的用法是看总耗时最高的前 10 条:

    mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
    mysqldumpslow -s at -t 10 /var/log/mysql/slow.log
    mysqldumpslow -s c -t 10 -g "SELECT" /var/log/mysql/slow.log
    

    输出里会看到类似“Count: 120 Time=2.31s”这样的汇总行,说明这类 SQL 被记录了 120 次,平均耗时 2.31 秒。次数多、平均耗时又高的语句,就是优先要处理的对象。如果日志文件很大,mysqldumpslow 解析会慢一点,可以先按时间段切分日志,或者用 pt-query-digest(Percona Toolkit 提供)做更细的报告,具体以官方文档为准。

    定位缺索引:EXPLAIN 是第一步

    拿到慢 SQL 骨架之后,下一步是搞清楚它为什么慢。最常见的答案就是没走索引,或者索引用错了。把候选 SQL 拿出来,前面加上 EXPLAIN 执行,重点看几列:type 如果是 ALL,说明全表扫描;key 为 NULL 表示没用上索引;rows 估算扫描行数,数值越大越危险;Extra 里出现 Using filesort 或 Using temporary,通常意味着排序、分组开销偏高。

    举个例子,假设有一张订单表 orders,字段 user_id、status、created_at,慢查询是按用户和状态过滤再按时间排序:

    EXPLAIN SELECT id, amount, created_at
    FROM orders
    WHERE user_id = 10086 AND status = 'paid'
    ORDER BY created_at DESC
    LIMIT 20;
    

    如果结果显示 type=ALL、key=NULL、rows 接近全表行数,就可以考虑建联合索引。按“等值条件在前、范围或排序在后”的思路,可以建 (user_id, status, created_at)。建索引之前建议先评估写放大和磁盘占用,索引不是越多越好,具体以业务查询模式为准。

    还有几个容易踩的坑值得提醒。第一,函数或隐式类型转换会让索引失效,比如 WHERE DATE(created_at) = '2026-09-01' 这种写法,改成范围条件更好。第二,前缀模糊匹配 LIKE '%abc' 基本用不上 B+ 树索引。第三,联合索引要遵循最左前缀原则,跨列顺序不对可能只用上一部分。第四,慢查询日志本身也会带来一点 I/O 开销,日志文件要纳入 logrotate 轮转,避免无限增长。

    总结一下操作路径:先打开 slow_query_log 并把 long_query_time 设到合理区间,让它稳定记录;再用 mysqldumpslow 按总耗时或平均耗时排序,锁定高频慢语句;最后用 EXPLAIN 看执行计划,判断是否缺索引并针对性加索引。优化完不要忘了对比优化前后的慢查询数量和平均耗时,用数据确认效果。如果站点数据量持续增长,后续还可以关注 performance_schema 里的语句统计、连接池配置以及缓存层的配合,一步步把数据库这块短板补齐。

    继续阅读

    📑 📅
    Docker Compose 部署多容器应用实战:环境变量、数据卷与重启策略配置要点 2026-09-15
    Nginx 日志按天切割与过期清理:logrotate 实战 2026-09-15
    Let's Encrypt 证书自动续期失败排查:日志、80端口校验与 reload 钩子 2026-09-13
    Docker 磁盘占用过高清理实战:overlay2、容器日志与悬空镜像 2026-09-12
    Nginx 502/504 排查实战:从 upstream 超时到 php-fpm 进程池 2026-09-12
    服务器定时任务实战:crontab 语法与不生效排查 2026-09-15
    Nginx 499 状态码排查实战:客户端断连与 upstream 超时的区别 2026-09-16
    容器内存超限被 OOM Kill 排查:指标、cgroup 与堆参数对应 2026-09-16
    MySQL 主从延迟排查:Seconds_Behind_Master 忽高忽低怎么定位 2026-09-16
    rsync 增量同步与断点续传实战:exclude、--delete 与限速 2026-09-16