这是我参与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;
复制代码
根据执行计划,我们很容易看出来,使用了user_id这个索引。
这时我们给sql多加一个or的条件,如下所示
SELECT * FROM t_user WHERE user_id = 123456 or age = 18;
复制代码
从执行计划上,我们很容易看出并没有使用上索引,因为key的那一栏为null。
- 对于条件中出现 or 的情况,user_id 列加了索引,age 列是没有加索引的,假设 MySQL 一定要走索引,可能需要三步:索引扫描+全表扫描+合并;
- 如果它一开始就走全表扫描,直接一遍扫描就完事了;
- mysql是有优化器的,处于效率与成本,遇到 or条件,索引
可能
失效,看起来也合情合理。
2、like通配符可能导致索引失效
SELECT * FROM t_user WHERE name = '%budong%';
复制代码
看执行计划,我们发现没用到索引,但是如果我们把前面的%删除,我们再看下执行计划如下
SELECT * FROM t_user WHERE name = 'budong%';
复制代码
很明显,又用上了索引。
但是如果此时我们不查询所有的字段,而是只查询id和name,如下所示
SELECT id,name FROM t_user WHERE name = 'budong%';
复制代码
like查询以%开头,会导致索引失效。可以有两种方式优化:
- 使用覆盖索引
- 把%放后面
索引包含所有满足查询需要的数据的索引,称为覆盖索引
(Covering Index)。
3、字符串列使用 where 一定用引号括起来,否则索引失效
SELECT * FROM t_user WHERE name = 123;
复制代码
第一眼我也以为该sql会走索引,但是细看发现有问题,name后面的123没有用引号包住,这么一来,MySQL会做隐式的类型转换,把它们转换为浮点数再做比较,因为当使用where不加单引号的时候,是字符串跟数字的比较。
可以看到没用上索引,但是当我们给123加上引号后,就会用上name索引了。
4、在索引列上使用mysql的内置函数,索引失效
SELECT * FROM t_user WHERE UPPER(name) = 'budong';
复制代码
我们从上面的执行计划可以看到,索引直接罢工了,直接全表扫描。
5、索引字段上使用is null或is not null,可能导致索引失效
SELECT * FROM t_user WHERE name is not null;
复制代码
可以看到用上了索引,address不为空也是如此,执行计划如下。
SELECT * FROM t_user WHERE address is not null;
复制代码
但是当我们查找name不为空或者address不为空会有什么样的结果呢?
SELECT * FROM t_user WHERE name is not null or address is not null;
复制代码
6.对索引列运算索引失效
对索引列进行四则运算(如,+、-、*、/),索引会失效。
user_id 是索引列,在查询的时候进行+1
SELECT * FROM t_user WHERE user_id+1= 456789;
复制代码
索引翻车啦~
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;
复制代码
毫无疑问,走了联合索引
where 条件只有 name 列呢?
SELECT * FROM t_user WHERE name = 'budong';
复制代码
依旧走的是联合索引。
但是当where 条件只有 age 列呢?
SELECT * FROM t_user WHERE age = 18;
复制代码
索引又翻车啦~
- 当我们创建一个联合索引的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,这就是
最左匹配原则
。 - 联合索引不满足最左原则,索引一般会失效。感兴趣的同学可以看下这篇文章 -> 一起来探索联合索引吧~