MySQL慢查询开启与优化指南

 更新时间:2026年03月26日 09:08:36   作者:qq_28372005  
文章主要介绍了慢查询日志在MySQL中的应用,包括其定义、开启方式、参数解析及分析工具,通过案例详细讲解了如何通过慢查询日志定位和优化性能瓶颈,如索引失效、隐式类型转换等问题,并给出了生产环境的最佳实践及学习建议,需要的朋友可以参考下

一、前言

1.1 什么是慢查询日志

慢查询日志是MySQL提供的一种性能诊断工具,用于记录执行时间超过指定阈值的SQL语句。通过分析这些“慢SQL”,可以精准定位数据库性能瓶颈,优化索引、SQL写法或表结构。

1.2 基础知识要求

  • MySQL基础:熟悉配置文件、基本SQL命令
  • 权限要求:需要SUPERPROCESS权限查看运行状态
  • 运维经验:了解磁盘空间、日志轮转等基本概念

二、慢查询日志的开启方式

2.1 临时开启(当前会话/全局,重启失效)

-- 查看当前慢查询状态
SHOW VARIABLES LIKE '%slow_query%';
SHOW VARIABLES LIKE '%long_query_time%';
-- 开启慢查询日志(全局,立即生效,重启失效)
SET GLOBAL slow_query_log = ON;
-- 设置慢查询阈值(秒),建议设为0.1~2秒之间
SET GLOBAL long_query_time = 1;
-- 设置日志文件路径(可选,默认在数据目录下)
SET GLOBAL slow_query_log_file = '/var/lib/mysql/slow-query.log';
-- 设置未使用索引的SQL也记录
SET GLOBAL log_queries_not_using_indexes = ON;

2.2 永久开启(修改配置文件)

Linux/Mac/etc/my.cnf 或 /etc/mysql/my.cnf
Windowsmy.ini

[mysqld]
# 开启慢查询日志
slow_query_log = 1
# 日志文件路径
slow_query_log_file = /var/lib/mysql/slow-query.log
# 慢查询阈值(秒)
long_query_time = 1
# 记录未使用索引的查询
log_queries_not_using_indexes = 1
# 日志输出格式(FILE或TABLE,默认FILE)
# log_output = FILE

配置完成后重启MySQL服务:

# systemctl
sudo systemctl restart mysqld
# service
sudo service mysql restart

三、参数解析

参数类型默认值说明建议值
slow_query_logBooleanOFF是否开启慢查询日志ON(生产环境建议开启)
long_query_timeFloat10.0慢查询阈值(秒)1~2秒(业务敏感可设为0.5)
slow_query_log_fileStringhostname-slow.log日志文件路径独立目录,便于监控
log_queries_not_using_indexesBooleanOFF是否记录未使用索引的查询ON(找出索引缺失的SQL)
log_outputEnumFILE日志输出方式FILE 或 TABLE
min_examined_row_limitInteger0扫描行数超过此值才记录1000(过滤小表扫描)
log_slow_admin_statementsBooleanOFF是否记录慢管理语句(如OPTIMIZE)ON(全面监控)

四、慢查询日志分析工具

4.1 使用mysqldumpslow工具

MySQL自带日志分析工具,可对慢查询日志进行聚合统计。

# 基本用法
mysqldumpslow /var/lib/mysql/slow-query.log
# 常用参数
mysqldumpslow -s t -t 10 /var/lib/mysql/slow-query.log   # 按查询时间排序,取前10条
mysqldumpslow -s c -t 10 /var/lib/mysql/slow-query.log   # 按执行次数排序
mysqldumpslow -s r -t 10 /var/lib/mysql/slow-query.log   # 按返回行数排序
mysqldumpslow -a /var/lib/mysql/slow-query.log           # 不抽象数字,显示具体SQL

4.2 使用pt-query-digest(Percona Toolkit)

更强大的第三方分析工具,提供详细的统计报告。

# 安装percona-toolkit
# Ubuntu/Debian
sudo apt-get install percona-toolkit
# CentOS/RHEL
sudo yum install percona-toolkit
# 分析慢查询日志
pt-query-digest /var/lib/mysql/slow-query.log > slow_report.txt
# 分析当前运行的查询(实时)
pt-query-digest --processlist h=localhost,u=root,p=password

五、实际案例:电商订单慢查询优化

5.1 案例背景

某电商平台订单表orders,数据量约500万行,业务反馈订单列表页面加载缓慢(超过5秒),需要定位并优化。

5.2 步骤一:开启慢查询并复现问题

-- 临时开启慢查询记录阈值0.5秒
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.5;
SET GLOBAL log_queries_not_using_indexes = ON;
-- 确认日志文件位置
SHOW VARIABLES LIKE 'slow_query_log_file';
-- 结果:/var/lib/mysql/slow-query.log

执行慢的订单查询SQL:

SELECT 
    o.order_id,
    o.user_id,
    o.order_amount,
    o.order_status,
    o.created_at,
    u.user_name,
    u.phone
FROM orders o
LEFT JOIN users u ON o.user_id = u.user_id
WHERE o.order_status = 'pending'
  AND o.created_at >= '2024-01-01'
  AND o.created_at < '2024-02-01'
ORDER BY o.created_at DESC
LIMIT 20;

5.3 步骤二:分析慢查询日志

# 查看慢查询日志
mysqldumpslow -s t -t 5 /var/lib/mysql/slow-query.log

日志输出

Count: 156  Time=3.52s (549s)  Lock=0.01s (1.56s)  Rows_sent=20.0 (3120), Rows_examined=5234567.0 (816M), root[root]@localhost
SELECT o.order_id, o.user_id, o.order_amount, o.order_status, o.created_at, u.user_name, u.phone 
FROM orders o 
LEFT JOIN users u ON o.user_id = u.user_id 
WHERE o.order_status = 'S' 
  AND o.created_at >= 'YYYY-MM-DD' 
  AND o.created_at < 'YYYY-MM-DD' 
ORDER BY o.created_at DESC 
LIMIT N

关键信息

  • 平均耗时:3.52秒
  • 平均扫描行数:523万行(几乎全表扫描)
  • 执行次数:156次,总耗时549秒

5.4 步骤三:使用EXPLAIN分析执行计划

EXPLAIN SELECT 
    o.order_id,
    o.user_id,
    o.order_amount,
    o.order_status,
    o.created_at,
    u.user_name,
    u.phone
FROM orders o
LEFT JOIN users u ON o.user_id = u.user_id
WHERE o.order_status = 'pending'
  AND o.created_at >= '2024-01-01'
  AND o.created_at < '2024-02-01'
ORDER BY o.created_at DESC
LIMIT 20\G

EXPLAIN结果

idselect_typetabletypepossible_keyskeykey_lenrowsExtra
1SIMPLEoALLidx_created_atNULLNULL5,234,567Using where; Using filesort
1SIMPLEueq_refPRIMARYPRIMARY41NULL

问题诊断

  1. type=ALL:orders表全表扫描,未使用任何索引
  2. rows≈523万:扫描全部数据行
  3. Extra包含Using filesort:ORDER BY需要额外排序,无法利用索引
  4. possible_keys显示idx_created_at:虽然有created_at索引,但优化器未选择

5.5 步骤四:深入分析索引失效原因

-- 查看orders表现有索引
SHOW INDEX FROM orders;

现有索引

  • PRIMARY KEY (order_id)
  • INDEX idx_user_id (user_id)
  • INDEX idx_created_at (created_at)
  • INDEX idx_status (order_status)

索引失效分析

  • WHERE条件包含order_statuscreated_at两个字段
  • MySQL优化器判断使用任一单列索引都需要回表过滤另一个条件,扫描行数依然很大
  • 最终选择了全表扫描

5.6 步骤五:制定优化方案

方案一:创建联合索引(推荐)

-- 创建联合索引,将等值查询字段放前面,范围查询放后面
CREATE INDEX idx_status_created ON orders (order_status, created_at);
-- 验证索引效果
EXPLAIN SELECT ...(同原SQL)\G

优化后EXPLAIN结果

tabletypekeykey_lenrowsExtra
orangeidx_status_created102185,000Using where; Using index condition
ueq_refPRIMARY41NULL

优化效果

  • 扫描行数从523万降到18.5万(减少96.5%)
  • 执行时间从3.5秒降至0.08秒

方案二:使用覆盖索引(进一步优化)

-- 创建覆盖索引,避免回表查询
-- 注意:
-- 创建索引需要在线上业务停止时进行,避免死锁
-- 覆盖索引需要包含所有查询字段
-- 重建索引可能需要很长时间,可能破坏数据,建议先备份数据
CREATE INDEX idx_status_created_cover ON orders (order_status, created_at, order_id, user_id, order_amount);

-- 但orders表字段较多,覆盖索引可能过大,需权衡

方案三:SQL语句改写

-- 使用子查询先筛选出订单ID,再关联用户表
SELECT 
    o.order_id,
    o.user_id,
    o.order_amount,
    o.order_status,
    o.created_at,
    u.user_name,
    u.phone
FROM (
    SELECT order_id, user_id, order_amount, order_status, created_at
    FROM orders
    WHERE order_status = 'pending'
      AND created_at >= '2024-01-01'
      AND created_at < '2024-02-01'
    ORDER BY created_at DESC
    LIMIT 20
) o
LEFT JOIN users u ON o.user_id = u.user_id;

5.7 步骤六:验证优化效果

再次查看慢查询日志

mysqldumpslow -s t -t 5 /var/lib/mysql/slow-query.log

优化后日志

优化成果总结

指标优化前优化后提升
平均耗时3.52秒0.08秒97.7% ↓
扫描行数523万18.5万96.5% ↓
总耗时/天549秒12.5秒97.7% ↓

六、更多实际案例

6.1 案例二:隐式类型转换导致索引失效

问题SQL

sql

-- phone字段定义为varchar(20),但传入数字类型
SELECT * FROM users WHERE phone = 13800138000;

EXPLAIN分析

  • type=ALL,key=NULL,rows=全表

原因:MySQL将phone字段自动转换为数字类型,导致索引失效

优化

-- 正确写法,传入字符串
SELECT * FROM users WHERE phone = '13800138000';

6.2 案例三:函数操作导致索引失效(和mysql版本有关系)

问题SQL

SELECT * FROM orders WHERE DATE(created_at) = '2024-01-15';

优化

SELECT * FROM orders 
WHERE created_at >= '2024-01-15' 
  AND created_at < '2024-01-16';

6.3 案例四:分页查询深度过大

问题SQL

-- 第10000页,每页20条
SELECT * FROM orders ORDER BY order_id LIMIT 200000, 20;

优化方案(延迟关联)

SELECT * FROM orders o
INNER JOIN (
    SELECT order_id FROM orders 
    ORDER BY order_id 
    LIMIT 200000, 20
) t ON o.order_id = t.order_id;

七、生产环境最佳实践

7.1 慢查询阈值设置建议

  • OLTP系统(高并发):0.5~1秒
  • OLAP系统(分析查询):2~5秒
  • 核心交易链路0.1~0.3秒(配合监控告警)

7.2 日志管理

  • 定期轮转,避免占满磁盘
  • 使用logrotate工具管理日志
  • 生产环境建议将log_output设为TABLE,便于SQL查询分析
-- 将日志输出到mysql.slow_log表
SET GLOBAL log_output = 'TABLE';

-- 查询慢日志表
SELECT * FROM mysql.slow_log 
WHERE query_time > 2 
ORDER BY start_time DESC 
LIMIT 10;

7.3 监控告警

  • 接入Prometheus/Grafana,监控慢查询数量趋势
  • 设置告警:每分钟慢查询数 > 10 或 某SQL耗时 > 5秒

7.4 慢查询分析流程总结

开启慢查询 → 收集日志 → 分析TOP慢SQL → EXPLAIN执行计划 → 定位问题
    ↑                                                      ↓
监控告警 ← 验证效果 ← 上线变更 ← 制定优化方案 ← 索引失效/扫描行数多

八、学习建议

  • 循序渐进:先从mysqldumpslow入手,掌握基础分析后再引入pt-query-digest
  • 结合EXPLAIN:每个慢SQL都要用EXPLAIN分析,理解MySQL优化器的选择
  • 建立知识库:记录常见慢查询模式及优化方案(隐式转换、函数操作、排序问题等)
  • 预防为主:上线前通过EXPLAIN审核新SQL,避免慢查询流入生产
  • 定期巡检:每周分析慢查询日志,发现潜在性能隐患

以上就是MySQL慢查询开启与优化指南的详细内容,更多关于MySQL慢查询开启与优化的资料请关注脚本之家其它相关文章!

相关文章

  • com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver 的区别

    com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver&n

    大家在连接mysql的时候,启动项目,会警告你推荐使用com.mysql.cj.jdbc.Driver 而不是com.mysql.jdbc.Driver,本文主要介绍了com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver 的区别,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • MySQL参数innodb_force_recovery详解

    MySQL参数innodb_force_recovery详解

    innodb_force_recovery是InnoDB存储引擎的一个重要参数,用于在数据库崩溃恢复时控制恢复行为的级别,下面就来详细的介绍一下,具有一定的参考价值,感兴趣的可以了解一下
    2025-07-07
  • MySQL备份恢复设计思路

    MySQL备份恢复设计思路

    这篇文章主要介绍了MySQL备份恢复设计思路,帮助大家更好的维护数据库,感兴趣的朋友可以了解下
    2020-10-10
  • MySQL分区表实现按月份归类

    MySQL分区表实现按月份归类

    mysql 单表数据量达到千万、亿级,可以通过分表与表分区提升服务性能。本文主要介绍了MySQL分区表实现按月份归类,感兴趣的可以了解一下
    2021-10-10
  • MySql中表单输入数据出现中文乱码的解决方法

    MySql中表单输入数据出现中文乱码的解决方法

    这篇文章主要介绍了MySql中表单输入数据出现中文乱码的解决方法的相关资料,需要的朋友可以参考下
    2016-07-07
  • sql语句优化的一般步骤详解

    sql语句优化的一般步骤详解

    网上关于SQL优化的教程很多,但是比较杂乱,近日有空整理了一下,写出来跟大家分享,下面这篇文章主要给大家分享介绍了关于sql语句优化的一般步骤,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2017-09-09
  • MySQL分库分表的几种方式

    MySQL分库分表的几种方式

    这篇文章主要介绍了MySQL分库分表的几种方式,分库分表方案是对关系型数据库数据存储和访问机制的一种补充,下文更多相关介绍需要的小伙伴可以参考一下
    2022-04-04
  • window系统mysql无法输入和无法显示中文的解决方法

    window系统mysql无法输入和无法显示中文的解决方法

    这篇文章主要介绍了window系统mysql无法输入和无法显示中文的解决方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-06-06
  • mysql连接过多和死掉以及拒绝服务的解决方法

    mysql连接过多和死掉以及拒绝服务的解决方法

    mysql连接过多和死掉以及拒绝服务的解决方法...
    2007-12-12
  • 使用python连接mysql数据库之pymysql模块的使用

    使用python连接mysql数据库之pymysql模块的使用

    这篇文章主要介绍了使用python连接mysql数据库之pymysql模块的使用,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-09-09

最新评论