1. 慢查询日志开启与配置
1.1 核心参数设置
慢查询日志通过MySQL的系统变量控制,主要参数如下:
| 参数名 | 类型 | 默认值 | 说明 | 优化建议 |
|---|---|---|---|---|
| slow_query_log | 布尔 | OFF | 是否启用慢查询日志 | 生产环境建议开启 |
| slow_query_log_file | 字符串 | hostname-slow.log |
慢查询日志文件路径 | 设置到有足够磁盘空间的目录,如/var/lib/mysql/slow.log |
| long_query_time | 数值 | 10.0 | 慢查询阈值(秒) | 建议设置为1秒,根据业务调整 |
| log_queries_not_using_indexes | 布尔 | OFF | 是否记录未使用索引的查询 | 开启后用于索引优化,但可能产生大量日志,建议结合log_throttle_queries_not_using_indexes使用 |
| log_throttle_queries_not_using_indexes | 数值 | 0 | 每秒允许记录的未使用索引查询数量,0表示无限制 | 建议设置为10,避免日志爆炸 |
| min_examined_row_limit | 数值 | 0 | 查询扫描行数阈值,小于该值的慢查询不记录 | 建议设置为1000,过滤掉扫描行数少的查询 |
| log_slow_admin_statements | 布尔 | OFF | 是否记录管理语句(如ALTER TABLE、ANALYZE TABLE) | 按需开启,管理语句通常执行时间较长 |
1.2 配置方法
1.2.1 临时配置(会话级别)
sql
-- 仅当前会话有效
SET SESSION slow_query_log = ON;
SET SESSION long_query_time = 1;
1.2.2 持久配置(全局级别)
sql
-- 全局有效,重启后失效
SET GLOBAL slow_query_log = ON;
SET GLOBAL slow_query_log_file = '/var/lib/mysql/slow.log';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = ON;
SET GLOBAL log_throttle_queries_not_using_indexes = 10;
SET GLOBAL min_examined_row_limit = 1000;
1.2.3 配置文件(永久生效)
编辑MySQL配置文件my.cnf(Linux)或my.ini(Windows):
ini
[mysqld]
# 慢查询日志配置
slow_query_log = 1
slow_query_log_file = /var/lib/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1
log_throttle_queries_not_using_indexes = 10
min_examined_row_limit = 1000
log_slow_admin_statements = 1
重启MySQL服务使配置生效:
bash
# Linux
systemctl restart mysqld
# Windows
net stop mysql && net start mysql
1.3 慢查询日志格式
慢查询日志记录了查询的执行时间、用户、主机、SQL语句等信息,示例格式:
# Time: 2023-12-17T10:00:00.123456Z
# User@Host: root[root] @ localhost [] Id: 12345
# Query_time: 2.500000 Lock_time: 0.100000 Rows_sent: 10 Rows_examined: 1000000
SET timestamp=1671234000;
SELECT * FROM `order` WHERE `user_id` = 123 ORDER BY `create_time` DESC LIMIT 10;
日志字段说明:
Time:查询执行时间User@Host:执行查询的用户和主机Id:连接IDQuery_time:查询执行时间(秒)Lock_time:锁等待时间(秒)Rows_sent:返回给客户端的行数Rows_examined:查询扫描的行数SET timestamp:查询开始时间戳- 最后一行:实际执行的SQL语句
2. 慢查询分析工具
2.1 pt-query-digest
2.1.1 工具简介
pt-query-digest是Percona Toolkit中的核心工具,用于分析慢查询日志,生成结构化报告,帮助定位慢查询的根本原因。
2.1.2 安装方法
bash
# CentOS/RHEL
yum install percona-toolkit -y
# Ubuntu/Debian
apt-get install percona-toolkit -y
# macOS
brew install percona-toolkit
2.1.3 核心使用方法
基本语法:
bash
pt-query-digest [options] slow.log > analysis.txt
常用选项:
--limit:指定输出的查询数量,如--limit=10输出TOP10慢查询--filter:过滤查询,如--filter '$event->{user} eq "app_user"'--since:指定分析的开始时间,如--since='2023-12-17 00:00:00'--until:指定分析的结束时间--output:指定输出格式,如--output=json
2.1.4 输出分析
pt-query-digest的输出分为三个部分:
1. 总体统计信息
# 总查询数、总执行时间、平均执行时间等
# 1000 Queries total, 100 unique
# Total query time: 1000s, Average query time: 1s
# 慢查询分布:按时间、用户、主机等
2. TOP N慢查询详情
# Query 1: 100.00 QPS, 100.00x concurrency, ID 0x1234567890abcdef at byte 1234
# Scores: V/M = 10.00
# Time range: 2023-12-17T10:00:00 to 2023-12-17T11:00:00
# Attribute pct total min max avg 95% stddev median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count 10 100
# Exec time 50 500s 1s 10s 5s 8s 2s 4.5s
# Lock time 20 20s 0s 1s 0.2s 0.8s 0.1s 0.1s
# Rows sent 5 50 0 5 0.5 1 1 0
# Rows examine 80 800000 5000 20000 8000 16000 4000 7000
# Rows affect 0 0 0 0 0 0 0 0
# Bytes sent 10 100000 50 5000 1000 4000 1000 800
# String:
# Databases app_db
# Hosts 192.168.1.100
# Users app_user
# Query_time distribution
# 1us
# 10us
# 100us
# 1ms
# 10ms
# 100ms
# 1s ################################################################
# 10s ########
# 100s
# Tables
# SHOW TABLE STATUS LIKE 'order'\G
# SHOW CREATE TABLE `order`\G
# EXPLAIN
# SELECT * FROM `order` WHERE `user_id` = ? ORDER BY `create_time` DESC LIMIT 10\G
3. 汇总分析
# Profile
# Rank Query ID Response time Calls R/Call V/M Item
# ==== ================== ============= ===== ====== ===== ===============
# 1 0x1234567890abcdef 500.00s 100 5.00s 10.0 SELECT `order`
# 2 0x0987654321fedcba 200.00s 200 1.00s 5.0 SELECT `user`
2.2 MySQL Workbench
2.2.1 工具简介
MySQL Workbench是MySQL官方提供的可视化工具,内置了慢查询日志分析功能,适合直观分析慢查询。
2.2.2 使用方法
- 连接数据库:打开MySQL Workbench,连接到目标数据库实例。
- 打开慢查询分析器 :
- 导航到
Performance->Performance Dashboard->Slow Query Log - 或直接在
Management->Slow Query Log中查看
- 导航到
- 导入慢查询日志 :
- 点击
Import from File,选择慢查询日志文件 - 或点击
Start Logging实时记录慢查询
- 点击
- 分析慢查询 :
- 查看慢查询列表,按执行时间、查询次数等排序
- 点击具体查询,查看执行计划和详细信息
- 使用
Explain按钮分析查询的执行计划 - 利用可视化图表分析慢查询的时间分布和趋势
2.2.3 核心功能
- 实时监控:实时查看正在执行的慢查询
- 可视化分析:通过图表展示慢查询的时间分布、用户分布等
- 执行计划集成:直接在工具中查看执行计划,便于优化
- 导出报告:支持将分析结果导出为CSV或PDF格式
3. 慢查询优化步骤
3.1 步骤一:定位问题
3.1.1 收集慢查询日志
- 确保慢查询日志已开启,参数配置合理
- 收集足够的慢查询样本(建议收集24小时或1周的日志)
- 使用
pt-query-digest或MySQL Workbench初步筛选TOP N慢查询
3.1.2 分析慢查询特征
- 查询类型:SELECT、UPDATE、DELETE等
- 表:涉及哪些表,表的大小和索引情况
- 执行频率:每秒/每分钟执行次数
- 执行时间:平均执行时间、最大执行时间
- 扫描行数:平均扫描行数、返回行数,判断是否存在全表扫描
- 锁时间:锁等待时间,判断是否存在锁竞争
3.2 步骤二:分析执行计划
执行计划是慢查询优化的核心,通过EXPLAIN命令获取。执行计划的关键字段分析:
| 字段 | 含义 | 优化重点 |
|---|---|---|
| id | 查询序列号,标识查询的执行顺序 | id相同:顺序执行;id不同:id大的先执行 |
| select_type | 查询类型 | PRIMARY(主查询)、SUBQUERY(子查询)、DERIVED(派生表)、UNION(联合查询) |
| table | 访问的表 | 优化表的访问顺序,避免全表扫描 |
| type | 访问类型 | 从优到劣:system > const > eq_ref > ref > range > index > ALL(全表扫描,需优化) |
| possible_keys | 可能使用的索引 | 若为NULL,需考虑添加索引 |
| key | 实际使用的索引 | 若为NULL,说明未使用索引,需优化WHERE条件或添加索引 |
| key_len | 索引使用的字节数 | 越小越好,说明索引使用效率高 |
| ref | 索引的引用列 | 显示哪些列或常量被用于查找索引列的值 |
| rows | 估计扫描行数 | 越小越好,说明查询效率高 |
| Extra | 额外信息 | 常见值:Using index(索引覆盖,优)、Using filesort(文件排序,需优化)、Using temporary(临时表,需优化) |
3.2.1 执行计划分析示例
慢查询SQL:
sql
SELECT * FROM `order` WHERE `user_id` = 123 ORDER BY `create_time` DESC LIMIT 10;
执行计划:
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-----------------------------+
| 1 | SIMPLE | order | NULL | ALL | NULL | NULL | NULL | NULL | 1000000 | 10.00 | Using where; Using filesort |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-----------------------------+
分析结论:
type为ALL:全表扫描,需添加索引possible_keys为NULL:没有可用的索引Extra为Using where; Using filesort:使用了WHERE条件和文件排序,需优化
3.3 步骤三:制定优化方案
根据执行计划分析结果,制定具体的优化方案:
3.3.1 索引优化
添加合适的索引 :
针对上述示例,为user_id和create_time添加复合索引:
sql
ALTER TABLE `order` ADD INDEX `idx_user_id_create_time` (`user_id`, `create_time` DESC);
优化后执行计划:
+----+-------------+-------+------------+------+-----------------------+-----------------------+---------+-------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+-----------------------+-----------------------+---------+-------+--------+----------+-------------+
| 1 | SIMPLE | order | NULL | ref | idx_user_id_create_time | idx_user_id_create_time | 4 | const | 10000 | 100.00 | Using index |
+----+-------------+-------+------------+------+-----------------------+-----------------------+---------+-------+--------+----------+-------------+
优化效果:
type变为ref:使用索引查找key为idx_user_id_create_time:使用了添加的复合索引rows从1000000减少到10000:扫描行数大幅减少Extra为Using index:索引覆盖,无需回表查询
3.3.2 SQL语句优化
**1. 避免SELECT ***:
仅查询需要的字段,减少网络传输和IO开销:
sql
-- 优化前
SELECT * FROM `order` WHERE `user_id` = 123 ORDER BY `create_time` DESC LIMIT 10;
-- 优化后
SELECT `order_id`, `user_id`, `total_amount`, `status`, `create_time` FROM `order` WHERE `user_id` = 123 ORDER BY `create_time` DESC LIMIT 10;
2. 优化WHERE条件:
-
确保字段类型匹配,避免隐式转换:
sql-- 优化前(user_id为int,'123'为字符串,隐式转换) SELECT * FROM `order` WHERE `user_id` = '123'; -- 优化后 SELECT * FROM `order` WHERE `user_id` = 123; -
避免在WHERE子句中使用函数:
sql-- 优化前(date函数导致索引失效) SELECT * FROM `order` WHERE DATE(`create_time`) = '2023-12-17'; -- 优化后(范围查询,使用索引) SELECT * FROM `order` WHERE `create_time` BETWEEN '2023-12-17 00:00:00' AND '2023-12-17 23:59:59';
3. 优化JOIN操作:
-
控制JOIN表数量(建议不超过3张):
sql-- 优化前(JOIN 4张表) SELECT * FROM `order` o JOIN `user` u ON o.`user_id` = u.`user_id` JOIN `order_item` oi ON o.`order_id` = oi.`order_id` JOIN `product` p ON oi.`product_id` = p.`product_id` WHERE o.`status` = 1; -- 优化后(拆分查询) SELECT o.* FROM `order` o WHERE o.`status` = 1; -- 应用层获取order_id列表后,批量查询其他表 SELECT * FROM `order_item` WHERE `order_id` IN (1, 2, 3, ...); -
优化ON条件,确保使用索引:
sql-- 优化前(ON条件未使用索引) SELECT * FROM `order` o JOIN `user` u ON o.`user_name` = u.`name`; -- 优化后(ON条件使用主键/索引) SELECT * FROM `order` o JOIN `user` u ON o.`user_id` = u.`user_id`;
3.3.3 数据库配置优化
根据慢查询分析结果,调整数据库配置参数:
-
调整缓冲池大小:
ini[mysqld] innodb_buffer_pool_size = 8G # 建议设为物理内存的70%-80% -
调整日志文件大小:
ini[mysqld] innodb_log_file_size = 1G # 建议设为innodb_buffer_pool_size的25% -
调整连接数:
ini[mysqld] max_connections = 1000 # 根据业务并发量调整
3.4 步骤四:验证优化效果
-
执行计划验证 :
再次使用
EXPLAIN分析优化后的SQL,确认执行计划得到改善。 -
性能测试 :
使用
pt-query-digest或MySQL Workbench对比优化前后的执行时间、扫描行数等指标。 -
生产环境验证 :
在生产环境低峰期部署优化方案,监控系统性能指标,如QPS、响应时间、CPU使用率等。
-
长期监控 :
持续监控慢查询日志,定期分析,确保优化效果持久。
4. 实战示例:慢查询优化案例
4.1 问题描述
某电商平台的订单查询接口响应时间超过5秒,通过慢查询日志定位到如下SQL:
sql
SELECT * FROM `order` WHERE `user_id` = 123 AND `status` = 1 ORDER BY `create_time` DESC LIMIT 10;
4.2 分析过程
-
查看执行计划:
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-----------------------------+ | 1 | SIMPLE | order | NULL | ALL | NULL | NULL | NULL | NULL | 2000000 | 1.00 | Using where; Using filesort | +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-----------------------------+ -
分析表结构:
sqlSHOW CREATE TABLE `order`\G *************************** 1. row *************************** Table: order Create Table: CREATE TABLE `order` ( `order_id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, `total_amount` decimal(10,2) NOT NULL, `status` tinyint(4) NOT NULL, `create_time` datetime NOT NULL, PRIMARY KEY (`order_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2000001 DEFAULT CHARSET=utf8mb4;
4.3 优化方案
添加复合索引,覆盖WHERE条件和ORDER BY字段:
sql
ALTER TABLE `order` ADD INDEX `idx_user_id_status_create_time` (`user_id`, `status`, `create_time` DESC);
4.4 优化效果
-
执行计划优化:
+----+-------------+-------+------------+------+----------------------------------+----------------------------------+---------+-------------+-------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+----------------------------------+----------------------------------+---------+-------------+-------+----------+-------------+ | 1 | SIMPLE | order | NULL | ref | idx_user_id_status_create_time | idx_user_id_status_create_time | 5 | const,const | 20000 | 100.00 | Using index | +----+-------------+-------+------------+------+----------------------------------+----------------------------------+---------+-------------+-------+----------+-------------+ -
性能提升:
- 优化前:响应时间5.2秒
- 优化后:响应时间0.02秒
- 性能提升:260倍
5. 总结
慢查询分析与优化是数据库性能优化的核心工作,通过合理开启和配置慢查询日志,使用专业的分析工具,结合执行计划分析和SQL语句优化,可以显著提升数据库性能。优化过程中需要遵循"定位问题→分析执行计划→制定优化方案→验证优化效果"的流程,持续监控和调整,确保系统长期稳定运行。
关键要点:
- 合理配置慢查询日志参数,确保有效记录慢查询
- 熟练使用pt-query-digest和MySQL Workbench分析慢查询
- 重点关注执行计划中的
type、key、rows和Extra字段 - 优先考虑索引优化,其次是SQL语句优化
- 优化后必须验证效果,确保性能提升
通过持续的慢查询分析和优化,可以有效解决数据库性能瓶颈,提升应用的响应速度和用户体验。