mysql慢查询sql
在 MySQL 中,慢查询 SQL指的是执行时间超过预设阈值(通常由long_query_time
参数定义)的 SQL 语句。这类 SQL 会消耗大量数据库资源(如 CPU、IO、内存),是导致数据库性能下降的常见原因。以下从慢查询的识别、分析、常见原因及优化方法展开详细说明。
一、如何识别慢查询?—— 开启慢查询日志
要监控慢查询,首先需要开启 MySQL 的慢查询日志(slow query log),它会记录所有执行时间超过long_query_time
的 SQL 语句。
1. 慢查询日志核心配置参数
参数名 | 作用说明 | 默认值 |
---|---|---|
slow_query_log | 是否开启慢查询日志(ON 开启,OFF 关闭) | OFF |
slow_query_log_file | 慢查询日志存储路径(需确保 MySQL 有写入权限) | 取决于系统 |
long_query_time | 慢查询阈值(单位:秒),执行时间超过此值的 SQL 会被记录 | 10 秒 |
log_queries_not_using_indexes | 是否记录未使用索引的 SQL(即使执行时间未超过long_query_time ) | OFF |
配置慢查询日志的方法
(1)通过配置文件永久生效(推荐)
修改 MySQL 配置文件(如my.cnf
或my.ini
),在[mysqld]
节点下添加以下配置:
[mysqld]
slow_query_log = ON # 开启慢查询日志
slow_query_log_file = /var/log/mysql/mysql-slow.log # 日志路径
long_query_time = 1 # 阈值设为1秒(根据业务调整)
log_queries_not_using_indexes = ON # 记录未用索引的SQL
配置后重启 MySQL 服务使生效:
# CentOS/RHEL
systemctl restart mysqld
# Ubuntu/Debian
systemctl restart mysql
(2)通过 SQL 动态生效(临时,重启失效)
-- 开启慢查询日志
SET GLOBAL slow_query_log = ON;
-- 设置日志路径(需MySQL有权限写入)
SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
-- 设置阈值为1秒
SET GLOBAL long_query_time = 1;
-- 记录未使用索引的SQL
SET GLOBAL log_queries_not_using_indexes = ON;
二、如何分析慢查询日志?
慢查询日志生成后,需通过工具分析关键信息(如执行频率最高、耗时最长的 SQL)。
1. 自带工具:mysqldumpslow
MySQL 内置的mysqldumpslow
工具可汇总慢查询日志,按不同维度排序(如执行时间、次数),适合快速定位问题 SQL。
常用参数:
-s
:排序方式(t
按时间,l
按锁等待,r
按返回行数,c
按执行次数);-t N
:显示前 N 条结果;-g
:通过正则匹配 SQL(不区分大小写)。
示例:
# 查看执行时间最长的前10条SQL
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log# 查看执行次数最多的前10条SQL
mysqldumpslow -s c -t 10 /var/log/mysql/mysql-slow.log# 查看包含"SELECT"且执行时间最长的SQL
mysqldumpslow -s t -t 5 -g "select" /var/log/mysql/mysql-slow.log
2. 更强大的工具:pt-query-digest
Percona Toolkit 中的pt-query-digest
功能更全面,可分析 SQL 执行频率、平均耗时、锁等待等,还能识别重复 SQL(如参数不同但结构相同的语句)。
安装:
bash
# CentOS/RHEL
yum install percona-toolkit
# Ubuntu/Debian
apt install percona-toolkit
示例:
bash
# 分析慢查询日志并输出详细报告
pt-query-digest /var/log/mysql/mysql-slow.log
报告中会标注 “总耗时占比最高”“执行次数最多” 的 SQL,是优化的优先目标。
3. 实时监控:Performance Schema
与sys
库
若需实时查看慢查询(无需等待日志写入),可通过Performance Schema
(性能字典)和sys
库(简化性能查询)实现。
示例:查看当前正在执行的慢查询:
sql
-- 利用sys库的processlist视图,筛选执行时间超过1秒的SQL
SELECT * FROM sys.processlist WHERE time > 1 AND state != 'Sleep';
三、慢查询 SQL 的常见原因
慢查询的本质是 “低效的执行计划”,常见原因包括:
1. 索引问题(最常见)
- 无索引:SQL 扫描全表(
type: ALL
),数据量大时耗时极长; - 索引失效:如索引列被函数 / 运算操作(
WHERE YEAR(create_time)=2023
)、隐式类型转换(WHERE phone = 13800138000
,若phone
是字符串类型)、使用NOT IN
/!=
等导致索引失效; - 索引不合理:如索引选择性低(如 “性别” 列建索引)、重复索引(同一列建多个索引)。
2. SQL 结构不合理
- 全表扫描:
SELECT *
读取无关字段,增加 IO 和内存消耗; - 子查询效率低:多层嵌套子查询可能导致重复扫描,不如
JOIN
高效; - 分页偏移量大:
LIMIT 100000, 10
需扫描前 100010 条数据,偏移量越大越慢; - 大表
JOIN
:未优化的JOIN
可能导致笛卡尔积,尤其大表关联大表时。
3. 数据量与表设计问题
- 表数据量过大:单表千万级以上未分表,扫描成本高;
- 锁竞争:SQL 涉及行锁 / 表锁,长时间持有锁导致其他请求阻塞。
四、慢查询 SQL 的优化方法
针对上述原因,可通过以下方法优化:
1. 优化索引
- 添加合适的索引:为
WHERE
、JOIN
、ORDER BY
后的字段建索引(如CREATE INDEX idx_user_name ON users(name)
); - 避免索引失效:
- 不对索引列做函数 / 运算(如
WHERE create_time >= '2023-01-01'
代替WHERE YEAR(create_time)=2023
); - 避免隐式转换(如
WHERE phone = '13800138000'
,确保参数类型与字段一致);
- 不对索引列做函数 / 运算(如
- 删除冗余索引:通过
sys.schema_unused_indexes
查看未使用的索引并删除。
2. 优化 SQL 结构
- 避免
SELECT *
:只查询需要的字段(如SELECT id, name FROM users
); - 用
JOIN
替代子查询:子查询可能重复执行,JOIN
更高效(如SELECT a.id FROM a JOIN b ON a.b_id = b.id
代替SELECT id FROM a WHERE b_id IN (SELECT id FROM b)
); - 优化分页查询:偏移量过大时,用 “条件定位” 代替
OFFSET
(如SELECT * FROM users WHERE id > 100000 LIMIT 10
,需id
有索引); - 拆分大
JOIN
:将大表JOIN
拆分为多次查询,或用中间表暂存结果。
3. 分析执行计划:EXPLAIN
EXPLAIN
可查看 SQL 的执行计划,判断是否使用索引、扫描行数等,是优化的核心工具。
示例:分析一条查询的执行计划:
sql
EXPLAIN SELECT id, name FROM users WHERE age > 30 AND status = 1;
关键输出字段:
type
:访问类型(ALL
全表扫描,ref
索引查找,range
范围扫描,const
常量查找,性能从差到好);key
:实际使用的索引(NULL
表示未用索引);rows
:估计扫描的行数(值越小越好);Extra
:额外信息(如Using filesort
表示需要排序,Using temporary
表示用临时表,均需优化)。
4. 其他优化
- 分表分库:大表按时间 / ID 分表(如
users_2023
、users_2024
),降低单表数据量; - 批量操作:用
INSERT INTO ... VALUES (...), (...)
代替循环单条插入; - 调整数据库参数:如增大
join_buffer_size
(JOIN
缓存)、sort_buffer_size
(排序缓存)等(需结合服务器资源)。
五、注意事项
- 阈值合理设置:
long_query_time
不宜过大(如默认 10 秒可能放过很多问题 SQL),建议根据业务设置为 0.5-2 秒; - 日志管理:慢查询日志会占用磁盘空间,需定期轮转(如用
logrotate
)或清理; - 开发规范:上线前用
EXPLAIN
检查 SQL,禁止全表扫描、复杂子查询等高危操作。