索引是数据库设计中特殊的数据存储结构,它能使我们的查询效率加倍,合理的使用索引让我们的性能得到质的提升,但是开发过程中,难免各种各样的业务需求可能会导致我们不意间写的SQL语句索引失效,这里整理了一些让索引失效的SQL操作有哪些。
下面是User表结构,主键只有一个id,数据量一共是800w条,根据不同测试条件后续会修改索引。
CREATE TABLE `csdn`.`无标题` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`mobile` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`open_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`region_id` int(10) NULL DEFAULT NULL,
`addr` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`sex` tinyint(2) NOT NULL DEFAULT 0 COMMENT '0女1男',
`state` tinyint(2) NOT NULL DEFAULT 1 COMMENT '0冻结1正常',
`created_date` datetime(0) NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `name`(`name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8003859 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
1.Like语句可能会导致失效(注:可能)
前置条件说明 主键:id (B+tree)、name(B+tree)
百分号放后面,显然是会走索引的。那我们吧百分号放前面试试:
果然,百分号通配符放前面会导致索引失效,但是所有情况均如此吗?我们继续
我们把查询列改为name列,这时会走索引,这是因为覆盖索引
的缘故,当我们加了索引的列查询的时候刚好也只查询这个列,就不需要回表
操作所以这个情况是会走索引的。别急,还有一种情况:
当我们把查询列改为id和name时,发现也会走索引,而且和上面这种结果一样,这是因为innodb引擎普通索引在命中的时候并不是直接返回结果,而是得到数据行的id,再根据id去主键索引中拿到数据,所以普通索引中会持有主键的id。
2.or关键字可能会导致失效(注:可能)
前置条件说明 主键:id (B+tree)、name(B+tree)
此时全表扫描没有走索引,因为mobile列没有加索引,or关键字会索引失效。别急,继续往下看:
此时走了索引,这是因为如果or关键字连接的列都加了索引的话,查询还是会走索引的。
3.索引列进行函数或者运算符操作可能导致失效(注:可能)
前置条件说明 主键:id (B+tree)、name(B+tree)
我对id列进行了三角函数和加法运算,此时发现索引失效,当我们采用覆盖索引
试试:
4.索引列使用!= <> 可能会导致失效(注:可能)
前置条件说明 主键:id (B+tree)、name(B+tree)
可以看见!=操作是会导致索引失效(这里也是可能出现,这个和数据的重复比率相关,只是一般情况下普通索引用!=会失效,特殊情况还是会走),如果采用覆盖索引
呢,试试:
果然,覆盖索引
的情况下还是会走索引的。别急,我们看看这一个情况:
奇怪的事情发生了,如果是主键使用!= 筛选还是会走索引,其实不光是主键索引会有这个情况,唯一索引也会走的,具体原因我后期专门写一个索引博客解释。
5.索引列使用is not null可能导致失效(注:可能)
前置条件说明 主键:id (B+tree)、name(B+tree)
name采用覆盖索引还是会走的:
6.索引是字符串类型的列查询不带引号可能会导致失效(注:可能)
前置条件说明 主键:id (B+tree)、name(B+tree)
我们将它改回来:
此时当然走索引,当然,如果采用覆盖索引
其实还是会走索引的
7.联合索引不遵循最左匹配原则会导致索引失效
前置条件说明 主键:id (B+tree)、name(B+tree)、(region_id,sex)(B+tree)
这当然会走索引,即便调换where条件顺序也会走索引的:
但是如果只有region_id呢?
答案是会走索引的,别急,我们再看一个情况:
为什么region_id会走索引,而sex不走呢?答案是region_id和sex的联合索引必须要满足最左匹配原则
,也就是说这个(region_id,sex)的联合索引其实可以解释为两个索引的组合:region_id,(region_id,sex),所以如果不满足最左匹配原则就会索引失效。(A,B,C)类型的符合索引,where条件后面如果是A,AB,ABC都可以命中索引。
8.试用not in可能导致失效(注:可能)
前置条件说明 主键:id (B+tree)、name(B+tree)
如果采用覆盖索引:
其实这里和!=情况是类似的,如果是主键或者唯一索引试用not in还是会走索引的
9.外联查询字符集不同意可能导致失效或索引利用率降低
前置条件说明 主键:id (B+tree)、name(B+tree)、mobile(B+tree)
user表mobile是varchar utf8mb utf8mb4_bin而mobile_des表的mobile是int类型,我们修改一下mobile_des的mobile的编码为utf8和utf8_general_ci
我们看到索引的利用率是不一样的。
10.优化器认为全表扫描比使用索引更有效率是会失效
这类情况几乎是不大可能发生,但是从逻辑上讲是存在的。当一个列的值存在大量重复时,比如性别,各种状态,当一部分值的数据量几乎碾压性的大于另一个值,优化器认为扫描索引和回表所带来的消耗和全表扫描相差不大时可能出现,但是此类情况和数据库的设计一般不会出现。