Mysql定位慢查询
当页面加载过慢、接口压测响应时间过长(超过1s)时,很可能是因为SQL的执行时间缓慢导致。
MySQL 提供了慢查询日志功能,可以记录执行时间超过指定阈值的 SQL 语句。
1. 开启慢查询日志
配置方法:
-
临时开启(重启后失效):
-- 查看当前配置 SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'long_query_time';-- 开启慢查询日志 SET GLOBAL slow_query_log = ON; -- 设置慢查询阈值(单位:秒,这里示例为 1 秒) SET GLOBAL long_query_time = 1; -- 设置慢查询日志存储路径(可选) SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
-
永久开启(修改配置文件
my.cnf
或my.ini
):[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 # 记录未使用索引的查询(可选)
修改后需重启 MySQL 服务生效。
2. 分析慢查询日志
慢查询日志记录了符合条件的 SQL 语句及其执行信息,可通过以下工具分析:
直接查看日志文件:
日志内容包含执行时间、锁等待时间、扫描行数等关键信息,例如:
日志文件不方便我们统计查看,通常情况下我们会使用工具来辅助。
使用 mysqldumpslow
工具:
MySQL 自带的日志分析工具,可统计慢查询的频率、耗时等:
# 查看最耗时的 10 条慢查询
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log# 查看访问次数最多的 10 条慢查询
mysqldumpslow -s c -t 10 /var/log/mysql/mysql-slow.log
参数说明:-s
指定排序方式(t: 按时间,c: 按次数),-t
指定显示条数。
3.Docker安装Mysql并开启慢日志配置
环境:
centos7、mysql8.0.19
拉取 MySQL 镜像:
docker pull mysql:8.0.19
创建挂载文件目录:
# 创建本地目录(用于挂载配置、数据、日志)
mkdir -p /mysql8/{conf,data,logs}
创建配置文件和慢日志文件:
# 创建配置文件
vi /home/hl/mysql8/conf/my.cnf
# 创建慢日志
vi /home/hl/mysql8/logs/mysql-slow.log
将以下内容复制到my.cnf中:
[mysqld]
# 开启慢查询日志
slow_query_log = 1
# 慢查询日志路径(需对应容器内路径,已挂载到本地 /docker/mysql/logs)
slow_query_log_file = /var/log/mysql/mysql-slow.log
# 慢查询阈值(单位:秒,超过此值的 SQL 会被记录)
long_query_time = 1
完成上述操作后,我的目录文件如下:
创建容器:
docker run -d \
--name mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=1234 \
-v /home/hl/mysql8/conf:/etc/mysql/conf.d \
-v /home/hl/mysql8/data:/var/lib/mysql \
-v /home/hl/mysql8/logs:/var/log/mysql \
mysql:8.0.19
查看容器是否启动:
进入容器并查看慢日志是否启用:
输入以下命令进入容器,并登录进mysql:
# 进入容器
docker exec -it mysql8 bash# 登录 MySQL
mysql -u root -p# 查看慢查询配置
SHOW VARIABLES LIKE 'slow_query_log'; # 应显示 ON
SHOW VARIABLES LIKE 'long_query_time'; # 应显示 1.000000
SHOW VARIABLES LIKE 'slow_query_log_file'; # 应显示日志路径
slow_query_log为ON则开启成功:
有错误参考:
关于Mysql开启慢查询日志报错:13 - Permission denied的解决方案-CSDN博客
mysqldumpslow
测试:
为了方便测试,将my.cnf的long_query_time设置为0.001,差不多每支持一条sql都会被慢日志记录下来,然后随便执行几条sql,使用mysqldumpslow
查看:
可以清晰的查看到每条sql的执行时间、锁等待时间、扫描行数等关键信息
4.分析慢SQL
通过慢查询日志我们可以定位到慢SQL,定位到SQL后,我们就可以通过EXPLAIN 或者 DESC命令获取 MySQL 如何执行 SELECT 语句的信息:
语法:
-直接在select语句之前加上关键字 explain/desc
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件
通过 EXPLAIN
或 EXPLAIN ANALYZE
分析 MySQL 如何执行这条 SQL,重点关注 索引使用、扫描行数、连接方式 等。
4.1 关键字段解读
执行的EXPLAIN
展示SQL信息中,我们主要关注以下这些关键字即可:
字段 | 含义 | 需关注的问题 |
---|---|---|
type | 访问类型(索引使用效率) | 若为 ALL (全表扫描)、index (全索引扫描),说明未有效使用索引。 |
key | 实际使用的索引 | 若为 NULL ,说明未使用任何索引。 |
rows | 预估扫描行数 | 数值越大,效率越低(尤其远大于实际返回行数 rows_sent 时)。 |
Extra | 额外信息 | 出现 Using filesort (文件排序)、Using temporary (临时表)、Using where (全表扫描后过滤)均为性能隐患。 |
possible_keys | 可能使用的索引 | 若 possible_keys 不为空但 key 为空,说明索引失效(需排查原因)。 |
type:
这条sql的连接的类型,性能由好到差为NULL、system、const、eq_ref、ref、range、 index、all
- system:查询系统中的表
- const:根据主键查询
- eq_ref:主键索引查询或唯一索引查询
- ref:索引查询
- range:范围查询
- index:索引树扫描
- all:全盘扫描
Extra:
Extra 值 | 核心含义 | 适用场景 | 性能影响 | 典型示例 |
---|---|---|---|---|
Using where | 存储引擎读取数据后,在服务器层用WHERE 条件过滤数据(需访问表数据行)。 | 无可用索引(全表扫描),或索引无法覆盖过滤条件,需回表后再过滤。 | 效率较低(可能全表扫描或需回表) | 无索引时执行SELECT * FROM user WHERE name = 'xxx' |
Using index | 仅通过索引即可获取所有所需数据,无需回表(覆盖索引)。 | 查询的所有列都包含在索引中,且过滤条件基于索引列。 | 效率高(避免访问表数据,仅扫描索引) | 索引包含name 和age 时,执行SELECT name, age FROM user WHERE name = 'xxx' |
Using index condition | 存储引擎层利用索引先过滤部分条件(索引下推),减少回表数据量,剩余条件在服务器层判断。 | 使用二级索引,过滤条件部分可通过索引字段判断(如前缀匹配),部分需回表后判断。 | 效率中等(减少回表数据,优于Using where ) | 索引为(first_name, last_name) 时,执行SELECT * FROM user WHERE first_name LIKE 'J%' AND last_name = 'Doe' |
示例:
user_id是user表的主键,执行以下SQL语句:
主要信息如下:
4.2 分析慢SQL
通过explain展示的信息,我们可以分析该条SQL:
- 通过key和key_len检查是否命中了索引(索引本身存在是否有失效的情况)
- 通过type字段查看sql是否有进一步的优化空间,是否存在全索引扫描或全盘扫描
- 通过extra建议判断,是否出现了回表的情况,如果出现了,可以尝试添加索引或修改返回字段来修复
5.相关面试问题
5.1 mysql中,如何定位慢查询?
我们可以通过MySQL中提供的慢查询日志功能进行定位。
首先,需要在cnf配置文件中自定义慢查询阈值,默认10s,我通常设置为2s。
然后,可通过查看慢查询日志或使用mysqldumpslow工具,定位到记录的慢查询SQL。
5.2 一个SQL语句执行很慢, 如何分析?
我们通常使用EXPLAIN或DESC命令可查看到执行SQL的具体信息:
通过key和key_len检查是否命中了索引,若使用了索引但是没有名称,查看索引是否失效。
通过type查看是否存在全索引扫描(index)或全盘扫描(all),若存在则可进一步优化。
通过Extra建议判断是否存在回表情况,若存在则可通过添加索引或者修改返回字段优化。