彻底根除MySQL慢查询,这12个问题都不能落下

前言

日常开发中,我们经常会遇到数据库慢查询。那么导致数据慢查询都有哪些常见的原因呢?今天田螺哥就跟大家聊聊导致MySQL慢查询的12个常见原因,以及对应的解决方法。

彻底根除MySQL慢查询,这12个问题都不能落下插图亿华云

一、SQL没加索引

1、反例select * from user_info where name =dbaplus社群 ;

彻底根除MySQL慢查询,这12个问题都不能落下插图1亿华云

2、正例//添加索引

alter table user_info add index idx_name (name);

彻底根除MySQL慢查询,这12个问题都不能落下插图2亿华云

二、SQL 索引不生效

有时候我们明明加了索引了,但是索引却不生效。在哪些场景,索引会不生效呢?主要有以下十大经典场景:

彻底根除MySQL慢查询,这12个问题都不能落下插图3亿华云

1、隐式的类型转换,索引失效

我们创建一个用户user表。

CREATE TABLE user (

id int(11) NOT NULL AUTO_INCREMENT,

userId varchar(32) NOT NULL,

age varchar(16) NOT NULL,

name varchar(255) NOT NULL,

PRIMARY KEY (id),

KEY idx_userid (userId) USING BTREE

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

userId字段为字串类型,是B 树的普通索引,如果查询条件传了一个数字过去,会导致索引失效。如下:

彻底根除MySQL慢查询,这12个问题都不能落下插图4亿华云

如果给数字加上,也就是说,传的是一个字符串呢,当然是走索引,如下图:

彻底根除MySQL慢查询,这12个问题都不能落下插图5亿华云

为什么第一条语句未加单引号就不走索引了呢?这是因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL会做隐式的类型转换,把它们转换为浮点数再做比较。隐式的类型转换,索引会失效。

2、查询条件包含or,可能导致索引失效

我们还是用这个表结构:

CREATE TABLE user (

id int(11) NOT NULL AUTO_INCREMENT,

userId varchar(32) NOT NULL,

age varchar(16) NOT NULL,

name varchar(255) NOT NULL,

PRIMARY KEY (id),

KEY idx_userid (userId) USING BTREE

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

其中userId加了索引,但是age没有加索引的。我们使用了or,以下SQL是不走索引的,如下:

彻底根除MySQL慢查询,这12个问题都不能落下插图6亿华云

对于or 没有索引的age这种情况,假设它走了userId的索引,但是走到age查询条件时,它还得全表扫描,也就是需要三步过程:全表扫描 索引扫描 合并。如果它一开始就走全表扫描,直接一遍扫描就完事。Mysql优化器出于效率与成本考虑,遇到or条件,让索引失效,看起来也合情合理嘛。

注意:如果or条件的列都加了索引,索引可能会走也可能不走,大家可以自己试一试哈。但是平时大家使用的时候,还是要注意一下这个or,学会用explain分析。遇到不走索引的时候,考虑拆开两条SQL。

3、like通配符可能导致索引失效

并不是用了like通配符,索引一定会失效,而是like查询是以%开头,才会导致索引失效。

like查询以%开头,索引失效。

explain select * from user where userId like 3;

彻底根除MySQL慢查询,这12个问题都不能落下插图7亿华云

把%放后面,发现索引还是正常走的,如下:

explain select * from user where userId like 123%;

彻底根除MySQL慢查询,这12个问题都不能落下插图8亿华云

既然like查询以%开头,会导致索引失效。我们如何优化呢?

使用覆盖索

把%放后面4、查询条件不满足联合索引的最左匹配原则

MySQl建立联合索引时,会遵循最左前缀匹配的原则,即最左优先。如果你建立一个(a,b,c)的联合索引,相当于建立了(a)、(a,b)、(a,b,c)三个索引。

假设有以下表结构:

CREATE TABLE user (

id int(11) NOT NULL AUTO_INCREMENT,

user_id varchar(32) NOT NULL,

age varchar(16) NOT NULL,

name varchar(255) NOT NULL,

PRIMARY KEY (id),

KEY idx_userid_name (user_id,name) USING BTREE

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

有一个联合索引idx_userid_name,我们执行这个SQL,查询条件是name,索引是无效:

explain select * from user where name =dbaplus社群;

因为查询条件列name不是联合索引idx_userid_name中的第一个列,索引不生效

彻底根除MySQL慢查询,这12个问题都不能落下插图9亿华云

在联合索引中,查询条件满足最左匹配原则时,索引才正常生效。

彻底根除MySQL慢查询,这12个问题都不能落下插图10亿华云

5、在索引列上使用mysql的内置函数

表结构:

CREATE TABLE `user` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`userId` varchar(32) NOT NULL,

`login_time` datetime NOT NULL,

PRIMARY KEY (`id`),

KEY `idx_userId` (`userId`) USING BTREE,

KEY `idx_login_time` (`login_Time`) USING BTREE

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

虽然login_time加了索引,但是因为使用了mysql的内置函数Date_ADD(),索引直接GG,如图:

彻底根除MySQL慢查询,这12个问题都不能落下插图11亿华云

一般这种情况怎么优化呢?可以把内置函数的逻辑转移到右边,如下:

explain select * from user where login_time = DATE_ADD(2022-05-22 00:00:00,INTERVAL -1 DAY);

彻底根除MySQL慢查询,这12个问题都不能落下插图12亿华云

6、对索引进行列运算(如, 、-、*、/),索引不生效

表结构:

CREATE TABLE `user` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`userId` varchar(32) NOT NULL,

`age` int(11) DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `idx_age` (`age`) USING BTREE

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

虽然age加了索引,但是因为它进行运算,索引直接迷路了。如图:

彻底根除MySQL慢查询,这12个问题都不能落下插图13亿华云

所以不可以对索引列进行运算,可以在代码处理好,再传参进去。

7、索引字段上使用(!= 或者

THE END
Copyright © 2024 亿华云