慢查询日志就是 MySQL 的SQL性能记录仪,专门自动记录数据库里执行卡顿、性能差的SQL。

⚠️ 注意:慢日志只是被动记录,不会阻止SQL执行,不影响业务运行。
作用:线上项目CPU飙升、接口超时、页面加载慢,第一时间靠慢日志找到问题SQL,是运维和开发必备排障工具。
慢日志的默认记录条件只有一个:执行时间 > long_query_time 阈值。
log_queries_not_using_indexes 是额外的附加开关,开启后,无索引的SQL即使不超时也会被记录。
两者是**"或"**的关系——满足任一条件即被记录:
| 条件 | 是否记录 |
|---|---|
| 执行时间 > 阈值 | ✅ 记录 |
| 执行时间 ≤ 阈值,但无索引 + 开启了无索引记录 | ✅ 记录 |
| 执行时间 ≤ 阈值,且无索引记录未开启 | ❌ 不记录 |
所以:全表扫描 ≠ 慢查询。小表全表扫描可能只需0.01秒,低于阈值就不会被记录(除非开了无索引记录)。
核心结论:线上慢日志总开关必须永久开启,绝对不能关闭!
很多人觉得开慢日志占用性能,其实完全不用担心:
关闭慢日志是重大隐患——线上出故障时无日志可排查,无法定位问题根源。
不关闭开关,只调优参数:调高耗时阈值、关闭无索引SQL记录,避免日志爆炸。
查询所有慢日志参数命令:SHOW VARIABLES LIKE 'slow_query%';
| 参数 | 含义 | 推荐值 |
|---|---|---|
| slow_query_log | 慢日志总开关 | 线上永久 ON |
| long_query_time | 慢查询判定阈值(单位:秒) | 开发:0.1秒 / 线上:1~2秒 |
| log_queries_not_using_indexes | 是否记录无索引全表扫描SQL | 开发:ON / 线上:OFF |
阈值说明:
| 参数 | 含义 | 推荐值 |
|---|---|---|
| slow_query_log_file | 日志文件存储路径 | 确保有磁盘空间,路径可访问 |
| min_examined_row_limit | 最少扫描行数阈值 | 100~1000,过滤小表噪音 |
| log_output | 日志输出方式 | FILE(默认)/ TABLE |
min_examined_row_limit 配合无索引记录使用:即使开了 log_queries_not_using_indexes,扫描行数低于此值的SQL也不会被记录,有效减少小表无效日志。
线上不想重启MySQL时,可以用动态命令:
-- 临时开启慢日志(重启后失效)SET GLOBAL slow_query_log = ON;-- 临时调整阈值SET GLOBAL long_query_time = 2;-- 永久生效需同时修改 my.cnf 配置文件
⚠️ long_query_time 修改后,需要新连接才生效,已有连接仍用旧值。
核心规则:慢日志默认只看耗时,不看是否全表扫描。
全表扫描 ≠ 慢查询!你的4000行小表,全表扫描仅需0.03秒,低于阈值,所以不记录;数据量过10万后,耗时暴涨超过阈值,立刻被记录。
如果开了 log_queries_not_using_indexes,则无索引SQL不超时也会被记录——但小表可以用 min_examined_row_limit 过滤掉。
答案取决于列上是否有索引,不存在所谓的"隐性优化规则":
| 场景 | 执行方式 | 是否被记录 |
|---|---|---|
| 列有索引 + LIKE '王%' | 索引范围扫描(不是全表扫描) | 不触发无索引记录;超时才记录 |
| 列无索引 + LIKE '王%' | 全表扫描 | 开了无索引记录就记录,超时也记录 |
| LIKE '%王' 左模糊 | 无论有无索引,都无法走索引范围扫描 | 全表扫描,开了无索引记录就记录 |
对比记忆:LIKE '王%' 有索引时能走范围扫描,不是全表扫描;LIKE '%王' 左模糊无法利用B+树索引有序性,一定全表扫描。
这条SQL扫描全表4400行、返回4300+行,被记录有两个可能原因:
判断 扫描行数 >> 返回行数 是索引缺失的信号,但这不是慢日志记录的原因,而是需要优化的原因。
拿到慢日志,不用看杂乱内容,只看这4个字段就能定位问题:
| 字段 | 含义 | 问题判断 |
|---|---|---|
| Query_time | SQL总执行耗时 | 核心判断依据,数值大 = SQL本身慢 |
| Lock_time | 锁等待耗时 | 数值高是锁竞争问题,不是SQL本身慢 |
| Rows_examined | 实际扫描行数 | 风险核心,数值大 = 可能在全表扫描 |
| Rows_sent | 最终返回的行数 | 用于和扫描行数对比 |
万能判断口诀:Rows_examined ≫ Rows_sent(扫描行数远大于返回行数)= 索引缺失/索引失效,必须优化
典型场景速判:
| 现象 | 诊断 |
|---|---|
| Query_time大,Lock_time小 | SQL本身慢,需要优化索引或改写SQL |
| Query_time大,Lock_time大 | 锁竞争严重,需要优化事务/锁粒度 |
| Rows_examined >> Rows_sent | 索引缺失或失效,补索引或改写SQL |
| Rows_examined ≈ Rows_sent | 扫描行都是需要的,考虑是否业务需求合理 |
直接找到 slow.log 文件,用编辑器打开,直观查看原始日志,适合本地调试。
自动合并重复SQL、排序统计,解决日志杂乱问题。
常用命令:
# 查耗时最长Top10mysqldumpslow -s t -t 10 /var/log/mysql/slow.log# 查执行次数最多Top10mysqldumpslow -s c -t 10 /var/log/mysql/slow.log# 查平均耗时最长Top10mysqldumpslow -s at -t 10 /var/log/mysql/slow.log
⚠️ mysqldumpslow 只能分析 FILE 格式的日志,如果 log_output=TABLE 则无法使用,需用 SELECT * FROM mysql.slow_log 查询。
比 mysqldumpslow 强大得多,是实际运维最常用的分析工具:
# 分析慢日志,输出完整报告pt-query-digest /var/log/mysql/slow.log# 只分析最近1小时的慢查询pt-query-digest --since '1h' /var/log/mysql/slow.log# 将结果保存到数据库pt-query-digest --review h=host,D=db,t=review /var/log/mysql/slow.log
无服务器权限,直接控制台操作:实例详情 → 日志管理 → 慢查询日志,支持筛选、导出、一键分析。
线上慢日志会持续增长,必须配置自动切割,避免单文件过大占满磁盘。
创建配置文件 /etc/logrotate.d/mysql-slow:
/var/log/mysql/slow.log { daily rotate 30 missingok compress delaycompress notifempty create 640 mysql mysql postrotate # 通知MySQL重新打开日志文件 mysql -e "SELECT 1" >/dev/null 2>&1 || true endscript}# 1. 重命名当前日志mv /var/log/mysql/slow.log /var/log/mysql/slow.log.bak# 2. 刷新MySQL日志句柄(MySQL自动创建新文件)mysql -e "FLUSH SLOW LOGS;"
固定流程:慢日志抓取问题SQL → EXPLAIN分析执行计划 → 补索引/改写SQL → 复测性能
这是业界最常用的数据库性能排查标准流程,适用于绝大多数卡顿问题的定位和解决。
发现问题 → 慢日志抓SQL → EXPLAIN分析 → 优化方案 → 上线复测 ↑ | └────────────── 未解决则循环 ←───────────────────────┘
| # | 核心要点 | 一句话记忆 |
|---|---|---|
| 1 | 慢日志记录条件 | 耗时超阈值 或 无索引(需开启),两者是"或"关系 |
| 2 | 线上核心规范 | 慢日志永久开启,关闭无索引记录,调高耗时阈值 |
| 3 | 全表扫描 ≠ 慢查询 | 小表全表扫描可能很快,不会触发耗时记录 |
| 4 | 模糊查询索引问题 | LIKE '王%' 有索引走范围扫描,LIKE '%王' 一定全表扫描 |
| 5 | 问题判断核心 | 扫描行数远大于返回行数 = 需要优化索引 |
| 6 | 优化固定流程 | 抓慢SQL → EXPLAIN分析 → 优化SQL/索引 → 复测 |
| 7 | 动态修改 | SET GLOBAL 可不停机修改,但 long_query_time 需新连接才生效 |