如何在使用索引上避免翻车~

这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战

索引翻车~索引翻车啦~~~

我们先提前准备一个数据表,其中id为主键,user_id 为唯一索引,name 为普通索引,address 为普通索引:

CREATE TABLE `t_user` (\
  `id` int NOT NULL AUTO_INCREMENT,\
  `name` varchar(255) DEFAULT NULL,\
  `age` int DEFAULT NULL,\
  `address` varchar(255) DEFAULT NULL,\
  `user_id` int DEFAULT NULL,\
  PRIMARY KEY (`id`),\
  UNIQUE KEY `index_user_id` (`user_id`) USING BTREE,\
  KEY `index_name` (`name`) USING BTREE,\
  KEY `index_address` (`address`) USING BTREE\
) ENGINE=InnoDB;
复制代码

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

SELECT * FROM t_user WHERE user_id = 123456;
复制代码

image.png

根据执行计划,我们很容易看出来,使用了user_id这个索引。

这时我们给sql多加一个or的条件,如下所示

SELECT * FROM t_user WHERE user_id = 123456 or age = 18;
复制代码

image.png

从执行计划上,我们很容易看出并没有使用上索引,因为key的那一栏为null。

  • 对于条件中出现 or 的情况,user_id 列加了索引,age 列是没有加索引的,假设 MySQL 一定要走索引,可能需要三步:索引扫描+全表扫描+合并;
  • 如果它一开始就走全表扫描,直接一遍扫描就完事了;
  • mysql是有优化器的,处于效率与成本,遇到 or条件,索引可能失效,看起来也合情合理。

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

SELECT * FROM t_user WHERE name = '%budong%';

复制代码

image.png 看执行计划,我们发现没用到索引,但是如果我们把前面的%删除,我们再看下执行计划如下

SELECT * FROM t_user WHERE name = 'budong%';

复制代码

image.png 很明显,又用上了索引。

但是如果此时我们不查询所有的字段,而是只查询id和name,如下所示

SELECT id,name FROM t_user WHERE name = 'budong%';

复制代码

image.png

like查询以%开头,会导致索引失效。可以有两种方式优化:

  • 使用覆盖索引
  • 把%放后面

索引包含所有满足查询需要的数据的索引,称为覆盖索引(Covering Index)。

3、字符串列使用 where 一定用引号括起来,否则索引失效

SELECT * FROM t_user WHERE name = 123;
复制代码

第一眼我也以为该sql会走索引,但是细看发现有问题,name后面的123没有用引号包住,这么一来,MySQL会做隐式的类型转换,把它们转换为浮点数再做比较,因为当使用where不加单引号的时候,是字符串跟数字的比较。

image.png 可以看到没用上索引,但是当我们给123加上引号后,就会用上name索引了。

4、在索引列上使用mysql的内置函数,索引失效

SELECT * FROM t_user WHERE UPPER(name) = 'budong';
复制代码

image.png

我们从上面的执行计划可以看到,索引直接罢工了,直接全表扫描。

5、索引字段上使用is null或is not null,可能导致索引失效

SELECT * FROM t_user WHERE name is not null;
复制代码

image.png 可以看到用上了索引,address不为空也是如此,执行计划如下。

SELECT * FROM t_user WHERE address is not null;
复制代码

image.png

但是当我们查找name不为空或者address不为空会有什么样的结果呢?

SELECT * FROM t_user WHERE name is not null or address is not null;
复制代码

image.png

6.对索引列运算索引失效

对索引列进行四则运算(如,+、-、*、/),索引会失效。

user_id 是索引列,在查询的时候进行+1

SELECT * FROM t_user WHERE user_id+1= 456789;
复制代码

索引翻车啦~

image.png

7.联合索引问题导致索引失效

我们创建一个联合索引,由name和age组成,name在前,age在后

CREATE TABLE `t_user` (\
  `id` int NOT NULL AUTO_INCREMENT,\
  `name` varchar(255) DEFAULT NULL,\
  `age` int DEFAULT NULL,\
  `address` varchar(255) DEFAULT NULL,\
  `user_id` int DEFAULT NULL,\
  PRIMARY KEY (`id`),\
  KEY `index_name_age` (`name`,`age`) USING BTREE\
) ENGINE=InnoDB;

复制代码

我们执行如下sql

SELECT * FROM t_user WHERE name = 'budong' AND age = 18;
复制代码

image.png 毫无疑问,走了联合索引

where 条件只有 name 列呢?

SELECT * FROM t_user WHERE name = 'budong';
复制代码

image.png

依旧走的是联合索引。

但是当where 条件只有 age 列呢?

SELECT * FROM t_user WHERE age = 18;
复制代码

image.png

索引又翻车啦~

  • 当我们创建一个联合索引的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,这就是最左匹配原则
  • 联合索引不满足最左原则,索引一般会失效。感兴趣的同学可以看下这篇文章 -> 一起来探索联合索引吧~

猜你喜欢

转载自juejin.im/post/7034907021212123143