MySQL慢查询定位

MySQL慢查询定位

引言

网站访问缓慢很多根源是执行时间过长的SQL。开启MySQL慢查询日志,可以捕获执行超时SQL,找到需要优化的语句,是数据库性能排查的核心手段。

慢查询基础配置

慢查询日志会记录执行时长超过指定阈值的SQL语句。

-- 查看慢查询是否开启
SHOW VARIABLES LIKE 'slow_query_log';

-- 设置阈值,单位秒,超过1秒就记录
SET GLOBAL long_query_time =1;

常用分析工具

  1. mysqldumpslow:mysql自带工具,统计慢日志
    mysqldumpslow -s t /var/lib/mysql/slow.log
  2. pt-query-digest:percona工具,分析效果更强,适合大量日志。

优化排查流程

  1. 开启慢查询,收集慢SQL;
  2. 使用explain查看执行计划,判断是否缺少索引;
  3. 优化where条件,添加合适索引,改写SQL逻辑;
  4. 优化完成后,持续观察慢日志确认效果。

示例

EXPLAIN SELECT * FROM article WHERE title like '%PHP%';

看type字段,ALL代表全表扫描,是需要优化的信号。

总结

慢查询日志用来抓取低效SQL,但是开启会少量增加服务器IO压力,生产环境可以按需开启。定位到慢SQL后优先检查索引,再调整SQL写法,解决数据库卡顿问题。


emer 发布于  2026-10-18 07:15 

MySQL索引设计原则

MySQL索引设计原则

引言

MySQL索引相当于书籍目录,快速定位数据,避免全表扫描。不合理的索引不仅无法提升查询速度,还会降低写入性能。索引设计是数据库优化最基础、最高效的手段,建站、接口开发都需要掌握。

索引核心原理

InnoDB引擎索引基于B+树实现,主键索引叶子节点存储完整行数据;二级索引叶子节点存储主键值,查询会触发回表。索引字段越大,占用磁盘越多,内存缓存能存放的索引越少,查询越慢。

索引设计核心原则

  1. 选择性优先:优先给区分度高的字段建索引,如手机号、ID;性别、状态这类只有少量枚举值字段不适合单独建索引。
  2. 联合索引遵循最左匹配原则,字段顺序:等值查询在前,范围查询放后面。
  3. 避免在索引字段做函数运算,where YEAR(create_time)=2026会直接失效索引。
  4. 不要过度建索引,每次新增、更新、删除数据,所有索引都需要同步更新,拖累写入。

示例SQL

-- 联合索引示例,适合 where uid=100 and create_time>xxx
CREATE INDEX idx_uid_ct ON user(uid,create_time);

-- 查看sql是否命中索引
EXPLAIN SELECT * FROM user WHERE uid=100;

常见踩坑

  • 字符串字段不加引号,隐式类型转换,索引失效;
  • 模糊查询%关键词,前置百分号不走索引;
  • 联合索引跳过左侧字段,无法触发索引。

总结

索引不是越多越好,按需创建。建索引前先用explain分析执行计划,优先优化高频查询SQL,控制索引数量,平衡查询与写入性能。


emer 发布于  2026-10-18 07:10 

MySQL慢查询排查,explain分析SQL执行计划

前言

网站突然变慢,数据库CPU100%?多半是慢查询搞的鬼。本文讲怎么开启慢查询日志、用explain分析SQL执行计划,找到慢的原因。

一、开启慢查询日志

临时开启

-- 查看慢查询是否开启
SHOW VARIABLES LIKE 'slow_query_log';

-- 开启慢查询
SET GLOBAL slow_query_log = ON;

-- 超过1秒就算慢查询
SET GLOBAL long_query_time = 1;

永久开启

编辑my.cnf:

vi /etc/my.cnf

加:

slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1

重启MySQL:

systemctl restart mysqld

看慢查询日志

tail -f /var/log/mysql/slow.log

二、explain是什么

explain用来分析SQL是怎么执行的,看有没有用索引、扫了多少行。

用法

EXPLAIN SELECT * FROM users WHERE name = '张三';

三、explain字段解释

id

SELECT的标识符,越大越先执行。

select_type

查询类型:

  • SIMPLE:简单查询
  • PRIMARY:主查询
  • SUBQUERY:子查询

table

查的哪张表。

type(最重要)

访问类型,从好到差:

类型 含义
system 表只有一行
const 主键或唯一索引匹配,只查一行
eq_ref 主键或唯一索引匹配
ref 普通索引匹配
range 索引范围查询(between、>、<)
index 扫整个索引树
ALL 全表扫描(最坏!)

重点看: 出现ALL就是全表扫描,要加索引。

possible_keys

可能用到的索引。

key

实际用到的索引。

key是NULL就是没用到索引。

rows

预估要扫描多少行。

越大越慢。

Extra

额外信息:

值 含义
Using where 服务器在存储引擎返回后再过滤
Using index 用了覆盖索引,不错
Using temporary 用了临时表,不好
Using filesort 用了文件排序,不好

四、常见慢查询原因

原因1:没加索引

SELECT * FROM users WHERE name = '张三';

name字段没索引 → 全表扫描 → 慢。

解决: 加索引

ALTER TABLE users ADD INDEX idx_name(name);

原因2:索引失效

虽然加了索引,但SQL写法不对,索引用不上。

索引失效的情况:

  1. 对索引字段用函数:

    SELECT * FROM users WHERE LEFT(name, 3) = '张三'; -- 索引失效
  2. 索引字段用运算:

    SELECT * FROM users WHERE age + 1 = 20; -- 索引失效
  3. 最左前缀原则不满足:

    -- 联合索引(a, b, c)
    SELECT * FROM t WHERE b = 1; -- 索引失效
  4. like以%开头:

    SELECT * FROM users WHERE name LIKE '%张三%'; -- 索引失效
  5. 类型不匹配:

    -- phone是varchar
    SELECT * FROM users WHERE phone = 13800138000; -- 索引失效

原因3:SELECT *

查了不需要的字段,占内存。

解决: 只查需要的字段

SELECT id, name FROM users WHERE ...

原因4:大表分页越查越慢

SELECT * FROM users ORDER BY id LIMIT 100000, 10;

慢的原因: 要先扫10万行再跳过。

解决:

SELECT u.* FROM users u
INNER JOIN (SELECT id FROM users ORDER BY id LIMIT 100000, 10) t
ON u.id = t.id;

五、实战案例

慢SQL

SELECT * FROM orders WHERE user_id = 123 AND status = 1 ORDER BY create_time DESC LIMIT 10;

explain分析

EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = 1 ORDER BY create_time DESC LIMIT 10;

发现: type是ALL,key是NULL → 全表扫描。

加索引

ALTER TABLE orders ADD INDEX idx_user_status_time(user_id, status, create_time);

再explain: type变成ref,key是idx_user_status_time → 用上索引了。

六、优化原则

  1. WHERE条件加索引
  2. ORDER BY字段加索引
  3. 联合索引遵守最左前缀
  4. **不要SELECT ***
  5. 避免在索引字段上用函数
  6. 大分页用子查询优化

七、常见坑

坑1:加了索引但没用

原因: SQL写法导致索引失效。

排查: explain看key字段。

坑2:索引加太多

索引不是越多越好,写的时候要维护索引,会变慢。

建议: 单表索引不超过5个。

坑3:区分度低的字段不加索引

比如gender(男/女),加索引没用。

原则: 区分度高的字段加索引。

总结

记住:

  1. 开启慢查询日志找慢SQL
  2. 用explain分析SQL执行计划
  3. type=ALL就是全表扫描,要加索引
  4. key=NULL就是没用到索引
  5. rows越大越慢
  6. 索引失效要避免

最常用:

EXPLAIN SELECT * FROM users WHERE ...

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:49 

MySQL优化查询速度,explain分析执行计划

前言

SQL查得慢,不知道为什么慢。用EXPLAIN看MySQL怎么执行这条SQL,就能找到问题在哪。

一、EXPLAIN怎么用

EXPLAIN SELECT * FROM users WHERE name = '张三';

输出很多列,重点看这几个:id、select_type、table、type、key、rows、Extra。

二、重点列详解

1. type(访问类型)

最重要的一列,看SQL查得快不快。

从好到差:

type 说明 速度
system 表只有一行 最快
const 主键或唯一索引查一次 很快
eq_ref 联表时主键关联 很快
ref 非唯一索引查 快
range 索引范围扫描 中
index 扫整个索引树 慢
ALL 全表扫描 最慢

目标: 至少达到range,最好是ref以上。

如果type是ALL,就是全表扫描,必须优化。

2. key(实际用到的索引)

key: NULL

说明没用索引。

key: idx_name

说明用到了idx_name索引。

3. rows(扫描行数)

MySQL预估要扫描多少行。

rows越大,越慢。

4. Extra(额外信息)

常见的:

  • Using where:WHERE过滤
  • Using index:覆盖索引,不用回表,好
  • Using filesort:需要额外排序,不好
  • Using temporary:用了临时表,不好

三、常见问题

问题1:type=ALL,全表扫描

原因: 没加索引,或者索引失效。

解决: 给WHERE条件的字段加索引。

问题2:key=NULL,没用索引

原因:

  1. 没建索引
  2. 索引失效了(函数、类型转换、前导模糊查询等)

解决: 建正确的索引,避免索引失效写法。

问题3:Using filesort

原因: ORDER BY的字段没走索引。

解决: 给ORDER BY的字段加索引。

问题4:Using temporary

原因: GROUP BY或DISTINCT用了临时表。

解决: 给GROUP BY的字段加索引。

四、实战案例

慢SQL

SELECT * FROM orders WHERE user_id = 100 ORDER BY create_time DESC LIMIT 10;

EXPLAIN结果:

id select_type table type key rows Extra
1 SIMPLE orders ALL NULL 100000 Using where; Using filesort

问题:

  1. type=ALL,全表扫描
  2. key=NULL,没用索引
  3. rows=100000,扫了10万行
  4. Using filesort,还要额外排序

优化

加联合索引:

ALTER TABLE orders ADD INDEX idx_userid_createtime (user_id, create_time);

再EXPLAIN:

id select_type table type key rows Extra
1 SIMPLE orders ref idx_userid_createtime 10 Using where

优化效果:

  1. type变成ref
  2. 用到了索引
  3. rows从10万变成10
  4. 没有filesort了

五、常用优化技巧

1. 避免SELECT *

只查需要的列,能用上覆盖索引。

2. WHERE条件加索引

经常查询的字段加索引。

3. 联合索引最左前缀

联合索引 (a,b,c),查询条件要从最左列开始。

4. 避免索引失效

  • 不要对索引字段用函数
  • 不要隐式类型转换
  • 不要前导模糊查询 like '%xxx'

总结

EXPLAIN看这三点:

  1. type:是不是全表扫描
  2. key:有没有用到索引
  3. rows:扫描了多少行

记住: 慢SQL先EXPLAIN,找到问题再优化。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 07:47 

MySQL explain执行计划详解,分析SQL性能必备

前言

做后端开发的同学肯定都遇到过这种情况:写了一条SQL,查询特别慢,但是不知道为什么慢。是没走索引?还是全表扫描?还是数据量太大?

这时候就需要用MySQL的explain命令了。explain可以告诉你MySQL是怎么执行这条SQL的,走了哪个索引,扫了多少行,是不是全表扫描。搞懂了explain,你就能快速定位SQL慢的原因,然后针对性优化。

很多新手同学不知道explain怎么用,也不知道每一列是什么意思。这篇文章就把explain的知识点从头到尾讲清楚,看完之后你就能看懂执行计划了。

一、explain是什么

explain就是MySQL提供的一个分析工具。你在SELECT语句前面加上EXPLAIN,MySQL就不会真的执行这条SQL,而是告诉你它准备怎么执行这条SQL。

explain能告诉你什么

  • 这条SQL有没有用到索引
  • 扫了多少行数据
  • 有没有全表扫描
  • 用的是哪个索引
  • 表连接的顺序是什么

二、怎么用explain

用法很简单,就在你的SELECT语句前面加上EXPLAIN就行:

EXPLAIN SELECT * FROM users WHERE name = '张三';

执行完之后,MySQL会返回一行结果,每一列都代表不同的信息。

三、explain的各列详解

explain返回的结果有很多列,我们重点讲几个常用的。

1. id

这一列表示SELECT的序号。如果有多个SELECT(比如子查询),id越大的越先执行。

2. select_type

这一列表示查询的类型,常见的有:

  • SIMPLE:简单查询,没有子查询或者UNION
  • PRIMARY:最外层的查询
  • SUBQUERY:子查询里的第一个SELECT
  • DERIVED:派生表(FROM子句里的子查询)
  • UNION:UNION里的第二个及以后的SELECT

3. table

这一列表示当前这一行是在查哪张表。

4. type

这一列非常重要,表示MySQL是怎么找到数据的,也就是访问类型。从好到差依次是:

  • system:表里只有一行数据,最好的情况
  • const:通过索引一次就找到了,比如WHERE id=1,id是主键
  • eq_ref:联表查询的时候,用主键或者唯一索引关联,最多匹配一行
  • ref:用普通索引关联,可能匹配多行
  • range:索引范围扫描,比如WHERE id BETWEEN 1 AND 10
  • index:扫描整个索引树,比全表扫描好一点
  • ALL:全表扫描,最差的情况,一定要优化

重点关注: 如果type是ALL或者index,说明这条SQL性能很差,要赶紧优化。

5. possible_keys

这一列表示理论上可能用到的索引。如果是NULL,说明没有用到索引。

6. key

这一列表示实际用到的索引。如果是NULL,说明真的没用到索引。

重点关注: 如果possible_keys有值,但是key是NULL,说明MySQL没有选择用索引,可能是数据量太小,或者索引失效了。

7. key_len

这一列表示用了索引的长度。可以用来判断用了联合索引的前几个字段。

8. ref

这一列表示用了哪个常量或者列来和索引做比较。

9. rows

这一列表示MySQL估计要扫多少行才能找到需要的数据。这个值越大,说明要扫的数据越多,性能越差。

10. Extra

这一列非常重要,包含了很多额外的信息,常见的有:

  • Using where:服务器在存储引擎返回数据之后,又做了一层WHERE过滤
  • Using index:用了覆盖索引,不需要回表,性能很好
  • Using temporary:用了临时表,一般是GROUP BY或者ORDER BY的时候没有用到索引
  • Using filesort:用了文件排序,ORDER BY没有用到索引,性能差
  • Using join buffer:联表的时候用了缓冲,说明联表的字段没有索引
  • Impossible WHERE:WHERE条件永远是false,根本查不到数据

重点关注: 如果Extra里出现了Using temporary或者Using filesort,说明SQL性能有问题,要优化。

四、重点看哪几列

很多同学看explain结果的时候,这么多列不知道重点看什么。其实重点看这几个:

  1. type:有没有全表扫描(ALL)
  2. key:有没有用到索引
  3. rows:扫了多少行
  4. Extra:有没有Using filesort或者Using temporary

只要这几个都没问题,这条SQL的性能一般就不会太差。

五、举个例子

比如我们执行这条SQL:

EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND status = 1;

执行完之后看到:

+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table  | type | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
|  1 | SIMPLE      | orders | ref  | user_id       | user_id | 4       | const |  100 | Using where |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+

我们来分析一下:

  • type是ref:说明用了普通索引,还不错
  • possible_keys是user_id:理论上可以用user_id索引
  • key是user_id:实际用了user_id索引
  • rows是100:估计要扫100行
  • Extra是Using where:说明返回之后又做了一层过滤

从这个结果来看,这条SQL用了索引,性能还可以。但是status字段没有用到索引,如果status过滤性好的话,可以考虑建一个(user_id, status)的联合索引。

六、explain的扩展用法

除了基本的explain,还有几个扩展的用法:

EXPLAIN PARTITIONS

看这条SQL会不会命中分区表:

EXPLAIN PARTITIONS SELECT * FROM orders WHERE ...;

EXPLAIN FORMAT=JSON

输出JSON格式的执行计划,信息更详细:

EXPLAIN FORMAT=JSON SELECT * FROM users WHERE ...;

常见坑

坑1:看到type是const就以为没问题

很多同学看到type是const就以为这条SQL性能很好。但是如果rows很大,或者Extra里有Using filesort,性能还是可能有问题。不能只看type,要综合看。

坑2:以为possible_keys有值就一定会用索引

很多同学看到possible_keys有值,就以为这条SQL一定会用到索引。其实不是,possible_keys只是说理论上可能用到,实际用不用要看key列。如果key是NULL,说明MySQL最后没有用这个索引。

坑3:rows是精确值

很多同学以为rows列是精确的行数,其实不是。rows是MySQL估计出来的行数,不是精确值。但是大致能反映出扫描的数据量大小。

坑4:以为Using filesort就是用了文件排序

很多同学看到Extra里有Using filesort就以为是真的用磁盘文件排序。其实不是,Using filesort只是说明MySQL做了额外的排序操作,不一定是用磁盘,数据量小的时候也可能在内存里排。

坑5:只看单表的explain,不看联表的

很多同学优化SQL的时候,只看单表的执行计划,不看联表的。其实联表的时候,表的连接顺序对性能影响很大。explain会按表的连接顺序一行一行列出来,要按顺序看。

总结

MySQL explain执行计划的核心知识点:

  1. explain的作用:看MySQL是怎么执行SQL的,不用真的执行
  2. type列最重要:从好到差是system > const > eq_ref > ref > range > index > ALL,出现ALL就是全表扫描
  3. key列:实际用到的索引,NULL就是没用到索引
  4. rows列:估计要扫多少行,越大越慢
  5. Extra列:重点关注Using filesort和Using temporary,出现这两个就要优化
  6. 重点看type、key、rows、Extra这四列,其他的了解就行
  7. rows是估计值,不是精确的,但是能反映大致的数据量

explain是分析SQL性能的必备工具,不管是面试还是实际开发,都是必须掌握的。以后遇到SQL慢,先explain一下,看看是哪里出了问题。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 08:54 

MySQL索引失效常见场景,避免踩坑

前言

做数据库开发的同学都知道,索引是提升查询性能最有效的手段。很多时候一张几百万行的大表,加个合适的索引查询速度就能从几秒变成几毫秒。

但是很多同学不知道的是,就算你加了索引,如果SQL语句写得不对,索引也可能会失效,结果还是全表扫描,查询慢得要命。这篇文章就把MySQL索引失效的常见场景都整理出来,帮你避免踩坑。

常见索引失效场景

1. 对索引列使用函数或运算

这是最常见的索引失效场景。如果你在索引列上用了函数或者做了运算,MySQL就没法用到这个索引了。

失效的写法:

-- 对索引列使用函数
SELECT * FROM users WHERE DATE(create_time) = '2026-10-05';

-- 对索引列做运算
SELECT * FROM orders WHERE amount + 100 > 500;

正确的写法:

-- 改成范围查询
SELECT * FROM users 
WHERE create_time >= '2026-10-05 00:00:00' 
AND create_time < '2026-10-06 00:00:00';

-- 把运算移到右边
SELECT * FROM orders WHERE amount > 400;

2. 不满足最左前缀原则

如果你建的是联合索引(比如 index(a, b, c)),查询的时候必须从最左边的列开始用,否则索引就会失效。

联合索引:index(name, age, city)

失效的写法:

-- 没有用到最左列name
SELECT * FROM users WHERE age = 25;

-- 跳过了中间的列
SELECT * FROM users WHERE name = '张三' AND city = '北京';

正确的写法:

-- 从最左列开始
SELECT * FROM users WHERE name = '张三';

-- 按顺序用
SELECT * FROM users WHERE name = '张三' AND age = 25;

3. 使用like以%开头

如果你的查询条件是 like '%xxx' 或者 like '%xxx%',那索引就会失效,因为MySQL不知道从哪里开始找。

失效的写法:

-- %在前面
SELECT * FROM users WHERE name LIKE '%张%';

-- %在前后都有
SELECT * FROM users WHERE name LIKE '%张';

正确的写法:

-- %在后面,这样可以用到索引
SELECT * FROM users WHERE name LIKE '张%';

如果一定要用前后模糊匹配的话,可以考虑全文索引或者ES搜索引擎。

4. 索引列使用 != 或 <>

如果查询条件用了 != 或者 <>,MySQL优化器会觉得大部分数据都要查,还不如直接全表扫描,所以索引就失效了。

失效的写法:

SELECT * FROM users WHERE status != 1;
SELECT * FROM users WHERE status <> 1;

正确的写法:

-- 改成in或者or
SELECT * FROM users WHERE status IN (0, 2, 3);

5. 使用or连接条件

如果or两边的列不是都有索引,那整个查询就会全表扫描。

失效的写法:

-- name有索引,但是age没有索引
SELECT * FROM users WHERE name = '张三' OR age = 25;

正确的写法:

-- 用union all代替or
SELECT * FROM users WHERE name = '张三'
UNION ALL
SELECT * FROM users WHERE age = 25;

或者给age也加上索引,这样两边都能用索引。

6. 隐式类型转换

这是最坑的一个场景。如果你的索引列是字符串类型,但是查询的时候传了数字,MySQL会自动做类型转换,结果索引就失效了。

失效的写法:

-- 索引列phone是varchar类型,但是传了数字
SELECT * FROM users WHERE phone = 13800138000;

正确的写法:

-- 加引号,传字符串
SELECT * FROM users WHERE phone = '13800138000';

7. 使用not in、not exists

not in 和 not exists 也很容易导致索引失效。因为MySQL优化器对这类反查询的优化做得不太好。

失效的写法:

SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM orders);

正确的写法:

-- 改成left join
SELECT u.* FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.user_id IS NULL;

8. 优化器认为全表扫描更快

还有一种情况是,虽然你写的SQL完全符合索引使用规则,但是MySQL优化器自己判断说"这张表数据太少了,全表扫描比走索引还快",那它就会选择全表扫描。

这种情况是正常的,不用纠结。一般小表全表扫描确实更快。

如何检查索引是否生效

那怎么知道自己写的SQL有没有用到索引呢?很简单,用EXPLAIN关键字看执行计划就行。

EXPLAIN SELECT * FROM users WHERE name = '张三';

重点看这几个字段:

  • type:如果是 ALL 就是全表扫描,说明索引失效了;如果是 ref、eq_ref、const 就说明用到索引了
  • key:这里显示实际用到的索引名,如果是 NULL 说明没用到索引
  • rows:扫描的行数,越大说明效率越低

常见坑

坑1:加了索引但是没生效,以为索引没用

很多同学加了索引之后发现查询还是慢,就觉得索引没用。其实大概率是你的SQL写法有问题,导致索引失效了。一定要先用EXPLAIN看一下执行计划,确认索引有没有被用到。

坑2:联合索引建了不用最左列

很多同学建了联合索引,但是查询的时候没用到最左边的列,结果索引白建了。一定要记住最左前缀原则。

坑3:隐式类型转换导致索引失效

这个最坑,因为写SQL的时候完全看不出来。比如手机号、身份证号这些字符串类型的字段,查询的时候一定要加引号,不然索引就失效了。

总结

MySQL索引失效的常见场景总结一下:

  1. 对索引列使用函数或运算
  2. 不满足最左前缀原则
  3. like以%开头
  4. 使用 != 或 <>
  5. 使用or连接条件
  6. 隐式类型转换
  7. 使用not in、not exists
  8. 表太小,优化器选择全表扫描

写SQL的时候一定要注意避开这些坑,写完之后用EXPLAIN检查一下索引有没有生效。这样你的数据库查询性能才能真正提上去。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 08:27 

MySQL慢查询开启,定位慢SQL语句方法

前言

做网站开发的同学肯定遇到过这种情况:网站打开越来越慢,数据库CPU占用很高,但就是不知道哪条SQL语句拖慢了整个系统。这时候就需要用到MySQL的慢查询日志功能,把执行慢的SQL语句记录下来,然后逐个优化。

这篇文章就详细讲讲如何开启MySQL慢查询日志,以及怎么用它来定位和优化慢SQL语句。

操作步骤

1. 先查看当前慢查询日志的状态

登录MySQL之后,先看看慢查询日志有没有开启:

SHOW VARIABLES LIKE '%slow_query_log%';

执行完之后会看到类似下面的输出:

+---------------------+-----------------------------------------------+
| Variable_name       | Value                                         |
+---------------------+-----------------------------------------------+
| slow_query_log      | OFF                                           |
| slow_query_log_file | /var/lib/mysql/izuf6w7x1x2x3x4x5x6x-slow.log  |
+---------------------+-----------------------------------------------+

从这里可以看到,slow_query_log 是 OFF 状态,说明慢查询日志没有开启。

2. 查看当前慢查询时间阈值

慢查询的默认时间阈值是10秒,也就是说执行时间超过10秒的SQL才会被记录。这个阈值对生产环境来说太大了,一般我们会改成1秒或者2秒。

查看当前阈值:

SHOW VARIABLES LIKE 'long_query_time';

3. 临时开启慢查询日志(重启后失效)

如果只是想临时排查问题,可以直接在MySQL命令行里开启,不需要重启服务:

-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';

-- 设置慢查询阈值为1秒
SET GLOBAL long_query_time = 1;

这样设置之后,执行时间超过1秒的SQL语句就会被记录到慢查询日志文件里了。

4. 永久开启慢查询日志(修改配置文件)

临时开启的方式在MySQL重启之后就会失效,如果想长期开启,需要修改MySQL的配置文件。

找到MySQL的配置文件,一般是 /etc/my.cnf 或者 /etc/mysql/my.cnf,在 [mysqld] 下面添加以下配置:

[mysqld]
# 开启慢查询日志
slow_query_log = 1
# 慢查询日志文件位置
slow_query_log_file = /var/log/mysql/slow.log
# 慢查询阈值,单位秒
long_query_time = 1
# 没有用到索引的SQL也记录下来
log_queries_not_using_indexes = 1

修改完配置文件之后,重启MySQL服务:

systemctl restart mysqld

5. 查看慢查询日志内容

慢查询日志开启之后,执行慢的SQL语句就会被记录到日志文件里。我们可以直接查看日志文件:

cat /var/log/mysql/slow.log

不过慢查询日志的格式比较复杂,直接看日志文件不太直观。我们可以用 mysqldumpslow 工具来分析:

# 查看最慢的10条SQL
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

# 查看访问次数最多的10条SQL
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log

6. 使用EXPLAIN分析慢SQL

找到慢SQL之后,下一步就是分析这条SQL为什么慢。我们可以用 EXPLAIN 关键字来查看SQL的执行计划:

EXPLAIN SELECT * FROM users WHERE name = '张三';

执行完之后会输出很多字段,我们重点关注这几个:

  • type:访问类型,最好是 const、eq_ref,最差是 ALL(全表扫描)
  • key:实际用到的索引,如果是 NULL 说明没用到索引
  • rows:扫描的行数,这个值越大越慢
  • Extra:额外信息,如果看到 Using filesort 或者 Using temporary 就说明需要优化了

常见坑

坑1:开启了慢查询但日志文件里什么都没有

很多同学开启了慢查询日志,但是去看日志文件却发现什么都没有。这通常是因为以下几个原因:

  1. 慢查询时间阈值设置得太大了,你的SQL执行时间还没超过阈值
  2. 日志文件的路径不对,你看的不是正确的日志文件
  3. MySQL没有权限写入日志文件,需要检查文件权限

坑2:long_query_time设置了但不生效

有时候你设置了 long_query_time = 1,但是发现执行时间0.5秒的SQL也被记录下来了。这是因为当前已经建立的MySQL连接还是用的旧的阈值,需要重新连接MySQL才能生效。

坑3:慢查询日志文件太大占满磁盘

如果你的网站访问量很大,慢查询日志可能会增长得非常快,几天就把磁盘占满了。所以一定要配置日志切割,或者定期清理慢查询日志文件。

总结

MySQL慢查询日志是定位数据库性能问题的必备工具,核心就三步:

  1. 开启慢查询日志,设置合理的时间阈值(一般1秒)
  2. 用mysqldumpslow工具分析慢查询日志,找到最慢的SQL
  3. 用EXPLAIN分析慢SQL的执行计划,针对性优化(加索引、改写SQL等)

掌握了慢查询日志的使用方法,网站数据库性能问题就能快速定位和解决了。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 08:23 

MySQL 索引优化实战:从 Explain 分析到查询性能翻倍

前言

数据库查询慢是 Web 开发中最常见的性能问题之一。而绝大多数慢查询都可以通过合理使用索引来解决。本文将从 Explain 执行计划入手,带你一步步掌握 MySQL 索引优化的实战技巧。

一、认识 Explain 执行计划

要优化查询,首先得知道 MySQL 是怎么执行你的 SQL 的。EXPLAIN 命令可以展示 MySQL 执行查询的详细步骤:

EXPLAIN SELECT * FROM orders WHERE user_id = 10086 AND status = 'paid';

重点关注以下几个字段:

  • type:访问类型,从好到差依次为 const > eq_ref > ref > range > index > ALL。出现 ALL 就是全表扫描,必须优化。
  • key:实际使用的索引。如果为 NULL,说明没有用到索引。
  • rows:预估需要扫描的行数。这个值越大越慢。
  • Extra:额外信息。Using filesort 和 Using temporary 都是危险信号。

二、最左前缀原则

联合索引 (a, b, c) 的匹配规则:

-- 用到索引 a,b,c
SELECT * FROM t WHERE a = 1 AND b = 2 AND c = 3;

-- 用到索引 a,b
SELECT * FROM t WHERE a = 1 AND b = 2;

-- 只用到索引 a(中间断了)
SELECT * FROM t WHERE a = 1 AND c = 3;

-- 完全用不到索引
SELECT * FROM t WHERE b = 2 AND c = 3;

实战要点:把等值查询的列放在前面,范围查询的列放在后面。

三、常见索引失效场景

1. 对索引列使用函数或运算

-- 失效:对索引列用了函数
SELECT * FROM users WHERE DATE(created_at) = '2026-01-01';

-- 有效:改成范围查询
SELECT * FROM users WHERE created_at >= '2026-01-01' AND created_at < '2026-01-02';

2. 隐式类型转换

-- 失效:phone 是 varchar,用数字查
SELECT * FROM users WHERE phone = 13800138000;

-- 有效:加引号
SELECT * FROM users WHERE phone = '13800138000';

3. LIKE 以通配符开头

-- 失效
SELECT * FROM users WHERE name LIKE '%张%';

-- 有效
SELECT * FROM users WHERE name LIKE '张%';

四、实战优化案例

问题 SQL(扫描 10 万行,耗时 1.2 秒):

SELECT id, title, created_at FROM articles
WHERE category_id = 5 AND status = 'published'
ORDER BY created_at DESC LIMIT 20;

Explain 结果:type=index,rows=120000,Using filesort。

优化方案:创建联合索引

ALTER TABLE articles ADD INDEX idx_cat_status_time (category_id, status, created_at DESC);

优化后:type=ref,rows=20,Extra 无 filesort。查询时间从 1.2 秒降到 0.005 秒。

五、索引不是越多越好

索引虽然能加速查询,但也有代价:

  1. 写入变慢:每次 INSERT/UPDATE 都要维护索引
  2. 占用空间:索引也是要存磁盘的
  3. 优化器选错:索引太多可能让优化器犯迷糊

建议:单表索引数量控制在 5 个以内,避免重复索引和冗余索引。

总结

MySQL 索引优化的核心思路:

  1. 用 EXPLAIN 看执行计划,找到慢的原因
  2. 遵循最左前缀原则设计联合索引
  3. 避免常见的索引失效写法
  4. 定期清理无用索引

掌握这些技巧,你就能解决 80% 以上的慢查询问题。


emer 发布于  2026-10-4 09:52 
'); })();