乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 数据库基础与安装

    • MySQL 简介
    • MySQL 5.6 到 5.7 到 8.0 关键差异速查
    • 安装 MySQL 5.7
    • 连接与断开服务器
    • 创建数据库
    • 创建数据表
    • 数据库与数据表
    • 加载数据
    • 获取数据库信息
    • 批处理模式
    • SHOW 语句汇总
    • FLUSH 与 RESET 语句
    • my.cnf 核心参数
    • 字符集与排序规则
  • 第2章 SQL基础查询

    • SELECT
    • WHERE
    • ORDER BY
    • LIMIT
    • COUNT
    • 聚合函数
    • 比较运算符
    • 逻辑运算符
    • 算术运算符
    • 模式匹配
    • NULL 值处理
    • UPDATE
    • DELETE
    • REPLACE
    • SELECT INTO
  • 第3章 数据类型与运算符

    • 数值类型
    • 字符串类型
    • 日期时间类型
    • BIT 类型
    • ENUM 类型
    • SET 类型
    • JSON 类型
    • 类型转换
  • 第4章 函数与表达式

    • 字符串函数
    • 数值函数
    • 日期函数
    • 全文检索函数
  • 第5章 高级查询与子查询

    • JOIN
    • 子查询
    • UNION
    • GROUP BY
    • HAVING
    • DISTINCT
  • 第6章 表与索引

    • 数据定义语言
    • 修改表结构
    • 视图
    • 修改视图与检查选项
    • 外键
    • 索引
    • 唯一索引
    • 复合索引
    • 存储引擎对比
    • 分区表
    • 第一范式与第二范式
    • 第三范式与 BC 范式
    • 反范式设计
  • 第7章 存储过程与函数

    • 存储过程
    • 存储函数
    • 变量
    • 流程控制
    • 游标
    • 预处理语句
  • 第8章 事务与锁

    • 事务
    • 事务隔离级别
    • 锁机制
    • MVCC
    • 死锁专题分析
    • LOCK TABLES
    • XA 事务
  • 第9章 用户管理与安全

    • 用户管理
    • 权限管理
    • 角色
    • SQL 注入防范
  • 第10章 性能优化入门

    • 执行计划
    • 索引优化
    • 查询优化
    • 查询优化器提示
    • 慢查询日志
    • InnoDB 深入机制
    • InnoDB 专项优化
    • Performance Schema
    • sys Schema
  • 第11章 复制与高可用

    • 主从复制原理
    • 半同步复制配置
    • binlog 开启与 point-in-time 恢复
    • mysqldump 全库备份
    • mysqldump 单表与条件备份
    • mysqldump 恢复与导入
    • xtrabackup 全量热备
    • xtrabackup 准备与恢复
    • xtrabackup 增量与流式备份
  • 第12章 触发器与事件

    • 触发器
    • 事件调度器
  • 参考

    • MySQL 5.7 专业术语大全
    • MySQL 5.7 关键字与保留字大全

慢查询日志

导学

执行计划分析单条 SQL,但生产环境中你需要知道"哪些 SQL 拖慢了系统"。慢查询日志(Slow Query Log)是 MySQL 提供的性能诊断工具,自动记录执行时间超过阈值的 SQL。

定义

慢查询日志:MySQL 记录执行时间超过指定阈值(long_query_time)的 SQL 语句的日志文件。开启后,所有"慢 SQL"都会被记录,便于后续分析和优化。

配置慢查询日志

示例:查看和配置慢查询日志

-- 查看当前慢查询日志配置
SHOW VARIABLES LIKE 'slow_query%';

当前配置状态:

Variable_nameValue
slow_query_logOFF
slow_query_log_file/var/lib/mysql/hostname-slow.log
-- 查看慢查询时间阈值
SHOW VARIABLES LIKE 'long_query_time';
Variable_nameValue
long_query_time10.000000

结果解读:

  • slow_query_log = OFF:慢查询日志默认关闭
  • long_query_time = 10:默认超过 10 秒的查询才被认为是慢查询

开启慢查询日志

-- 临时开启慢查询日志(需 SUPER 权限,重启后失效)
SET GLOBAL slow_query_log = 'ON';

-- 设置慢查询日志文件路径
SET GLOBAL slow_query_log_file = '/var/lib/mysql/slow.log';

-- 设置慢查询时间阈值(超过 1 秒视为慢查询)
SET GLOBAL long_query_time = 1;

-- 开启记录未使用索引的查询(强烈建议开启)
SET GLOBAL log_queries_not_using_indexes = 'ON';

-- 验证配置是否生效
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';

操作后的配置状态:

Variable_nameValue
slow_query_logON
slow_query_log_file/var/lib/mysql/slow.log
Variable_nameValue
long_query_time1.000000

结果解读:

  • 现在执行时间超过 1 秒的查询会被记录到 /var/lib/mysql/slow.log
  • log_queries_not_using_indexes = ON 表示即使查询很快,只要没走索引也会被记录

my.cnf 永久配置

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

结果解读:

  • 修改 my.cnf 后需要重启 MySQL 服务才能生效
  • 永久配置适合生产环境,避免重启后配置丢失

日志内容示例

示例:构造一条慢查询

-- 准备测试数据:创建一个大表
CREATE TABLE slow_test_table (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    description TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    status INT DEFAULT 0
);

-- 插入大量数据(使用存储过程或批量插入)
INSERT INTO slow_test_table (name, description, status)
SELECT 
    CONCAT('user_', seq),
    CONCAT('description_', seq, '_', REPEAT('x', 100)),
    FLOOR(RAND() * 10)
FROM (
    SELECT @row := @row + 1 AS seq
    FROM (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3) t1,
         (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3) t2,
         (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3) t3,
         (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3) t4,
         (SELECT @row := 0) t5
) numbers
WHERE seq <= 100000;

当前数据状态:

  • 表中已有 100,000 行数据
  • 没有为 status 列创建索引
-- 执行一条可能很慢的查询(全表扫描)
SELECT * FROM slow_test_table WHERE status = 5 ORDER BY created_at DESC LIMIT 100;

结果解读:

  • 这条查询需要全表扫描 100,000 行,过滤出 status = 5 的记录,然后排序
  • 如果执行时间超过 long_query_time(1秒),会被记录到慢查询日志

慢查询日志格式

日志文件中会记录类似以下内容:

# Time: 2026-06-10T15:30:01.123456Z
# User@Host: app_user[app_user] @  [192.168.1.100]
# Query_time: 2.345678  Lock_time: 0.000123 Rows_sent: 100  Rows_examined: 100000
SET timestamp=1718011801;
SELECT * FROM slow_test_table WHERE status = 5 ORDER BY created_at DESC LIMIT 100;

关键字段解析:

字段含义
Time查询执行的时间戳
User@Host执行查询的用户和来源 IP
Query_timeSQL 总执行时间(秒)
Lock_time等待锁的时间
Rows_sent返回给客户端的行数
Rows_examined扫描的行数(越大越需优化)

结果解读:

  • Query_time: 2.345678:这条查询执行了约 2.35 秒
  • Rows_sent: 100:只返回了 100 行给客户端
  • Rows_examined: 100000:但扫描了 100,000 行
  • Rows_examined 远大于 Rows_sent,说明过滤条件效率极低,是优化的重点信号

分析慢查询日志

方法一:mysqldumpslow(官方工具)

# 按出现次数排序,显示前 10
mysqldumpslow -s c -t 10 /var/lib/mysql/slow.log

# 按执行时间排序
mysqldumpslow -s t -t 10 /var/lib/mysql/slow.log

# 按平均执行时间排序
mysqldumpslow -s at -t 10 /var/lib/mysql/slow.log

结果解读:

  • -s c:按出现次数(count)排序,找出执行频率最高的慢查询
  • -s t:按总执行时间排序,找出最耗时的慢查询
  • -s at:按平均执行时间排序
  • -t 10:只显示前 10 条

方法二:pt-query-digest(Percona Toolkit,推荐)

# 生成详细的慢查询分析报告
pt-query-digest /var/lib/mysql/slow.log > slow_report.txt

# 只分析最近 1 小时的日志
pt-query-digest --since="1h" /var/lib/mysql/slow.log

# 过滤只包含某张表的查询
pt-query-digest --filter '$event->{arg} =~ m/slow_test_table/i' /var/lib/mysql/slow.log

pt-query-digest 会输出:

  • 总体统计(执行次数、总时间、平均时间)
  • 慢查询排行榜(按执行时间或出现频率)
  • 每条慢查询的详细分析(执行时间分布、rows examined 等)

示例输出解读:

# Profile
# Rank Query ID           Response time Calls R/Call V/M   Item
# ==== ================== ============= ===== ====== ===== ==========
#    1 0x1234567890ABCDEF  45.2345 50.0%   100 0.4523  0.01 SELECT slow_test_table
#    2 0xABCDEF1234567890  22.5678 25.0%    50 0.4514  0.02 SELECT accounts

结果解读:

  • Rank 1 的查询占总响应时间的 50%,执行了 100 次,平均每次 0.45 秒
  • 这是最需要优先优化的查询

SQL 示例

场景一:动态调整慢查询阈值

-- 查看当前阈值
SHOW VARIABLES LIKE 'long_query_time';

当前数据状态:

Variable_nameValue
long_query_time1.000000
-- 临时调整为 0.5 秒(用于排查问题时捕获更多查询)
SET GLOBAL long_query_time = 0.5;

-- 验证修改
SHOW VARIABLES LIKE 'long_query_time';

操作后的状态:

Variable_nameValue
long_query_time0.500000
-- 排查完成后恢复为 1 秒
SET GLOBAL long_query_time = 1;

结果解读:

  • 调低阈值可以捕获更多"潜在慢查询",帮助发现性能隐患
  • 注意:long_query_time 对已有连接不立即生效,新连接才会使用新阈值

场景二:验证慢查询日志是否记录

-- 确保慢查询日志已开启
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.1;

-- 执行一个故意很慢的查询:使用 SLEEP 函数
SELECT SLEEP(0.5), 'this is a slow query' AS msg;

查询结果:

SLEEP(0.5)msg
0this is a slow query

结果解读:

  • SLEEP(0.5) 会让查询暂停 0.5 秒
  • 因为 long_query_time = 0.1,这条查询会被记录到慢查询日志
  • 可以查看日志文件验证是否记录成功

场景三:分析未使用索引的查询

-- 确保开启记录未使用索引的查询
SET GLOBAL log_queries_not_using_indexes = 'ON';

-- 执行一个未走索引的查询(假设 status 列没有索引)
SELECT COUNT(*) FROM slow_test_table WHERE status = 3;

查询结果:

COUNT(*)
约 10000

结果解读:

  • 这条查询进行了全表扫描,没有使用任何索引
  • 即使执行时间很快(比如 0.05 秒),也会被记录到慢查询日志(因为开启了 log_queries_not_using_indexes)
  • 这是发现"潜在性能问题"的重要手段

优化方向:

-- 为 status 列添加索引
CREATE INDEX idx_status ON slow_test_table(status);

-- 再次执行相同查询
SELECT COUNT(*) FROM slow_test_table WHERE status = 3;

优化后的结果:

  • 查询会使用 idx_status 索引,扫描行数从 100,000 降到约 10,000
  • 不再出现在"未使用索引"的慢查询日志中

场景四:使用 performance_schema 分析慢查询

MySQL 5.7 还可以通过 performance_schema 分析查询性能:

-- 查看 statements_summary_by_digest 表(需提前开启 performance_schema)
SELECT 
    DIGEST_TEXT AS query,
    COUNT_STAR AS exec_count,
    SUM_TIMER_WAIT/1000000000000 AS total_latency_sec,
    AVG_TIMER_WAIT/1000000000000 AS avg_latency_sec,
    SUM_ROWS_EXAMINED AS rows_examined,
    SUM_ROWS_SENT AS rows_sent
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 5;

示例输出:

queryexec_counttotal_latency_secavg_latency_secrows_examinedrows_sent
SELECT * FROM slow_test_table WHERE status = ? ORDER BY created_at DESC LIMIT ?10045.23450.45231000000010000

结果解读:

  • exec_count = 100:这条查询被执行了 100 次
  • total_latency_sec = 45.23:总耗时 45.23 秒
  • rows_examined = 10000000:总共扫描了 1000 万行
  • rows_sent = 10000:只返回了 1 万行
  • rows_examined 远大于 rows_sent,说明索引优化空间很大

常见误区

误区正解
慢查询日志默认开启默认关闭,需要手动开启。
long_query_time 对已有连接立即生效只对新连接生效,已有连接需重连。
所有慢查询都需要优化有些查询业务上本来就慢(如大数据量报表),应区分场景。

面试考点

Q:慢查询日志的开启步骤?

  1. SET GLOBAL slow_query_log = 'ON';2. 设置日志路径 slow_query_log_file;3. 设置阈值 long_query_time;4. 建议开启 log_queries_not_using_indexes。

Q:Rows_examined 和 Rows_sent 的区别?

Rows_examined 是 MySQL 扫描的行数;Rows_sent 是返回给客户端的行数。两者差距越大,说明过滤条件效率越低(扫描了大量数据却只返回少量结果)。

Q:pt-query-digest 和 mysqldumpslow 的区别?

mysqldumpslow 是 MySQL 自带的简单工具;pt-query-digest 是 Percona Toolkit 的高级工具,输出更详细,支持更复杂的过滤和分析。

小结

  • 慢查询日志记录执行时间超过阈值的 SQL
  • 建议开启 log_queries_not_using_indexes,捕获未走索引的查询
  • 用 pt-query-digest 分析日志,找出最需要优化的 SQL
  • 关注 Rows_examined,扫描行数远大于返回行数是优化信号

下一章引子:发现了慢查询,接下来学习如何通过索引优化来解决问题。

上一页
查询优化器提示
下一页
InnoDB 深入机制