MySQL技巧(三):慢查询开启与分析优化案例

一、前言

1.1 什么是慢查询日志

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

1.2 基础知识要求

  • MySQL基础:熟悉配置文件、基本SQL命令

  • 权限要求 :需要SUPERPROCESS权限查看运行状态

  • 运维经验:了解磁盘空间、日志轮转等基本概念


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

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

sql

bash 复制代码
-- 查看当前慢查询状态
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服务:

bash

复制代码
# systemctl
sudo systemctl restart mysqld

# service
sudo service mysql restart

三、参数解析

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

四、慢查询日志分析工具

4.1 使用mysqldumpslow工具

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

bash

bash 复制代码
# 基本用法
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)

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

bash

sql 复制代码
# 安装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 步骤一:开启慢查询并复现问题

sql

sql 复制代码
-- 临时开启慢查询记录阈值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:

sql

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 步骤二:分析慢查询日志

bash

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

日志输出

text

sql 复制代码
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分析执行计划

sql

sql 复制代码
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结果

id select_type table type possible_keys key key_len rows Extra
1 SIMPLE o ALL idx_created_at NULL NULL 5,234,567 Using where; Using filesort
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 1 NULL

问题诊断

  1. type=ALL:orders表全表扫描,未使用任何索引

  2. rows≈523万:扫描全部数据行

  3. Extra包含Using filesort:ORDER BY需要额外排序,无法利用索引

  4. possible_keys显示idx_created_at:虽然有created_at索引,但优化器未选择

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

sql

复制代码
-- 查看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 步骤五:制定优化方案

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

sql

bash 复制代码
-- 创建联合索引,将等值查询字段放前面,范围查询放后面
CREATE INDEX idx_status_created ON orders (order_status, created_at);

-- 验证索引效果
EXPLAIN SELECT ...(同原SQL)\G

优化后EXPLAIN结果

table type key key_len rows Extra
o range idx_status_created 102 185,000 Using where; Using index condition
u eq_ref PRIMARY 4 1 NULL

优化效果

  • 扫描行数从523万降到18.5万(减少96.5%)

  • 执行时间从3.5秒降至0.08秒

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

sql

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

-- 但orders表字段较多,覆盖索引可能过大,需权衡
方案三:SQL语句改写

sql

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 步骤六:验证优化效果

sql

复制代码
-- 再次查看慢查询日志
sql 复制代码
mysqldumpslow -s t -t 5 /var/lib/mysql/slow-query.log

优化后日志

text

bash 复制代码
Count: 156  Time=0.08s (12.48s)  Lock=0.00s (0s)  Rows_sent=20.0 (3120), Rows_examined=185000.0 (28.86M), root[root]@localhost
SELECT ...

优化成果总结

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

六、更多实际案例

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

问题SQL

sql

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

EXPLAIN分析

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

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

优化

sql

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

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

问题SQL

sql

sql 复制代码
SELECT * FROM orders WHERE DATE(created_at) = '2024-01-15';

优化

sql

sql 复制代码
SELECT * FROM orders 
WHERE created_at >= '2024-01-15' 
  AND created_at < '2024-01-16';

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

问题SQL

sql

sql 复制代码
-- 第10000页,每页20条
SELECT * FROM orders ORDER BY order_id LIMIT 200000, 20;

优化方案(延迟关联)

sql

sql 复制代码
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查询分析

sql

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 慢查询分析流程总结

text

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

八、学习建议

  • 循序渐进 :先从mysqldumpslow入手,掌握基础分析后再引入pt-query-digest

  • 结合EXPLAIN :每个慢SQL都要用EXPLAIN分析,理解MySQL优化器的选择

  • 建立知识库:记录常见慢查询模式及优化方案(隐式转换、函数操作、排序问题等)

  • 预防为主 :上线前通过EXPLAIN审核新SQL,避免慢查询流入生产

  • 定期巡检:每周分析慢查询日志,发现潜在性能隐患

相关推荐
liang_jy15 小时前
Activity 启动流程扩展篇(一)—— startActivityInner 任务决策全解析
android·源码
NPE~16 小时前
[App逆向]脱壳实战
android·教程·逆向·android逆向·逆向分析
木易 士心16 小时前
别再只会用 drawCircle 了!一文搞懂 Android Canvas 底层机制
android
AtOR CUES17 小时前
MySQL——表操作及查询
android·mysql·adb
怣疯knight19 小时前
安卓App无法增加自定义图片作为图标功能
android
mOok ONSC19 小时前
mysql9.0windows安装
windows·adb
jinanwuhuaguo20 小时前
OpenClaw联邦之心——从孤岛记忆到硅基集体潜意识的拓扑学革命(第二十三篇)
android·人工智能·kotlin·拓扑学·openclaw
Gary Studio21 小时前
安卓HAL C++基础-命名域
android
xxjj998a1 天前
Laravel8.x核心特性详解
数据库·mysql·adb
诸神黄昏EX1 天前
Android Google XTS
android