一、简介
MySQL 慢查询日志是定位数据库性能瓶颈的核心工具,主要用于追踪执行效率低下的 SQL 语句。通过慢查询日志,可以查找出哪些查询语句的执行效率低,以便进行优化。慢查询日志默认不开启。
二、作用
慢查询日志用于记录执行时间超过设定阈值的 SQL 语句,帮助开发者或 DBA 发现 "拖慢" 数据库的低效查询。其核心价值在于:
-
精准定位耗时较长的 SQL(如全表扫描、复杂关联查询等);
-
分析查询的执行频率、锁等待时间、扫描行数等关键指标;
-
为 SQL 优化、索引调整、表结构设计提供数据依据。
三、关键配置参数及开启方式
1、关键配置参数
| 参数名 | 含义 | 默认值 |
|---|---|---|
| slow_query_log | 是否开启慢查询日志(1开启,0关闭) | 0 |
| slow_query_log_file | 慢查询日志文件路径 | 取决于系统,如 /var/log/mysql/slow.log |
| long_query_time | 慢查询阈值(单位:秒),执行时间 ≥ 该值 的 SQL 会被记录 | 10 |
| log_queries_not_using_indexes | 是否记录未使用索引或是全表扫描的查询(即使执行时间未超过 long_query_time) | 0 |
| log_slow_rate_limit | 查询多少次才记录,MariaDB 特有配置项 | 1 |
2、临时开启
通过 SET GLOBAL 命令修改参数,当前会话生效,重启失效
sql
-- 开启慢查询日志
SET GLOBAL slow_query_log = 1;
-- 设置日志文件路径(需提前创建目录并授权)
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
-- 设置慢查询阈值(如 10 秒,支持小数,如 0.5 表示 500 毫秒)
SET GLOBAL long_query_time = 10;
-- 记录未使用索引的查询(可选,用于发现索引缺失问题)
SET GLOBAL log_queries_not_using_indexes = 1;
3、永久开启
修改 MySQL 配置文件(通常为 /etc/my.cnf 或 /etc/mysql/my.cnf),在 mysqld 段添加相关配置参数
bash
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 10 # 阈值 10 秒
log_queries_not_using_indexes = 1 # 记录未用索引的查询
修改后重启 MySQL 服务生效
四、慢查询日志分析
1、模拟慢查询
临时开启慢查询日志,设置阈值为1秒,设置日志输出为 /var/log/mysql/slow.log

执行慢查询动作,生成日志

查看日志

2、日志关键字段解析
-
Query_time:SQL 执行时间(秒),此处为 3 秒,超过阈值 1 秒;
-
Lock_time:锁等待时间(秒),此处 0 秒,若过大可能存在锁竞争;
-
Rows_sent:返回给客户端的行数(1行);
-
Rows_examined:扫描的行数(1 行)
3、定位问题的步骤
筛选关键慢查询:优先关注 Query_time 大、执行频率高的 SQL(可通过 mysqldumpslow 工具统计,如 mysqldumpslow -s t -t 10 /var/log/mysql/slow.log 按时间取前 10 条)。
分析执行计划:对慢查询使用 EXPLAIN 工具查看执行计划,重点关注:
-
type:访问类型(ALL 表示全表扫描,性能最差;ref/range 表示使用索引,较优);
-
key:实际使用的索引(NULL 表示未使用索引);
-
rows:预估扫描行数(值越大,效率越低);
-
Extra:额外信息(如 Using filesort 表示需要排序,Using temporary 表示使用临时表,均需优化)。

定位根因:结合执行计划判断问题类型,例如:
-
若 type=ALL 且 key=NULL:未创建合适的索引;
-
若 rows 远大于 Rows_sent:索引过滤性差(如区分度低的字段);
-
若 Lock_time 大:存在行锁 / 表锁竞争(如高并发更新同一行)。
五、常见的慢查询优化
针对慢查询日志定位的问题,常见优化手段可分为索引优化、SQL 语句优化、表结构优化和配置优化四类:
1、索引优化
-
添加合适的索引:为查询条件(WHERE)、关联字段(JOIN ON)、排序字段(ORDER BY)创建索引;
-
避免索引失效:不使用函数或表达式处理索引字段(如 WHERE SUBSTR(phone,1,3)='138' 会导致 phone 索引失效);避免 !=、NOT IN、IS NOT NULL 等操作(索引难以高效过滤);
-
避免隐式类型转换:字符串查询加引号(如 WHERE name='123' 而非 WHERE name=123);
-
删除冗余索引:同一字段的重复索引会增加写入开销。
2、SQL 语句优化
-
避免 SELECT *:只查询需要的字段,减少 IO 开销;
-
拆分复杂查询:将一次性执行的大查询拆分为多个小查询(如分页查询用 LIMIT 限制行数,避免全表扫描后过滤);
-
优化关联查询:JOIN 表数量控制在 3 张以内,关联字段必须建索引,避免 LEFT JOIN 时右表条件导致全表扫描;避免笛卡尔积;
-
替换子查询:子查询可能产生临时表,改用 JOIN 优化(如 SELECT * FROM t1 WHERE id IN (SELECT id FROM t2) 改为 SELECT t1.* FROM t1 JOIN t2 ON t1.id = t2.id)。
3、表结构优化
-
分表分库:大表(千万级以上)按时间(如订单表按月份分表)或哈希(如用户表按 user_id 分表)拆分,减少单表数据量。
-
选择合适的数据类型:如用 INT 代替 VARCHAR 存储数字,DATE/DATETIME 代替字符串存储日期,减少存储空间和比较开销。
-
避免大字段:TEXT/BLOB 等大字段单独存表,避免查询时不必要的 IO 消耗。
4、配置优化
-
调整缓存:增大 innodb_buffer_pool_size(InnoDB 缓存池,建议占物理内存的 50%-70%),减少磁盘 IO;
-
优化连接:调整 max_connections 避免连接数不足,wait_timeout 释放闲置连接;
-
日志优化:非必要时关闭 log_queries_not_using_indexes(避免日志过大),定期轮转慢查询日志。