Mysql索引:索引失效场景

索引失效

  • 索引列存在类型转换
  • 索引列使用了函数
  • 索引列参与了运算
  • 模糊查询最前面的为不确定匹配字符
  • 索引列使用IS NULL或者IS NOT NULL
  • 联合索引不满足最左匹配原则
  • 使用了select *
  • 使用OR条件,但不是所有的条件都有索引
  • 使用不等于(!= 或者 <>)操作符
  • 使用order by时
  • 列上的普通索引 not in和not exists

数据准备

mysql 复制代码
CREATE TABLE `t_userinfo` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `id_no` varchar(18) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '身份编号',
  `username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `union_idx` (`id_no`,`username`,`age`),
  KEY `create_time_idx` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;


INSERT INTO `t_userinfo` (`id`, `id_no`, `username`, `age`, `create_time`) VALUES (null, '1001', 'Tom1', 11, '2022-02-27 09:04:23');
INSERT INTO `t_userinfo` (`id`, `id_no`, `username`, `age`, `create_time`) VALUES (null, '1002', 'Tom2', 12, '2022-02-26 09:04:23');
INSERT INTO `t_userinfo` (`id`, `id_no`, `username`, `age`, `create_time`) VALUES (null, '1003', 'Tom3', 13, '2022-02-25 09:04:23');
INSERT INTO `t_userinfo` (`id`, `id_no`, `username`, `age`, `create_time`) VALUES (null, '1004', 'Tom4', 14, '2023-02-25 09:04:23');


-- 删除历史存储过程
DROP PROCEDURE IF EXISTS `insert_t_user`
 
-- 创建存储过程
delimiter $
 
CREATE PROCEDURE insert_t_user(IN limit_num int)
BEGIN
 DECLARE i INT DEFAULT 10;
    DECLARE id_no varchar(18) ;
    DECLARE username varchar(32) ;
    DECLARE age TINYINT DEFAULT 1;
    WHILE i < limit_num DO
        SET id_no = CONCAT("NO", i);
        SET username = CONCAT("Tom",i);
        SET age = FLOOR(10 + RAND()*2);
        INSERT INTO `t_userinfo` VALUES (NULL, id_no, username, age, NOW());
        SET i = i + 1;
    END WHILE;
 
END $
-- 调用存储过程
call insert_t_user(100);

索引失效场景

1、索引列存在类型转换

id_no 数据库的类型为varchar,但是查询添加的参数类型为整数。在查询过程中发生了隐式类型转换,导致索引失效。

mysql 复制代码
mysql> explain select * from t_userinfo where id_no = 1002;
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_userinfo | NULL       | ALL  | union_idx     | NULL | NULL    | NULL |   94 |    10.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 3 warnings (0.01 sec)

mysql>

这种情况还有一个特例,如果字段类型为int类型,而查询条件添加了单引号或双引号,则Mysql会参数转化为int类型,虽然使用了单引号或双引号。这种情况还是会走索引的。

mysql 复制代码
mysql> explain select * from t_userinfo where id = '2';
+----+-------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table      | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t_userinfo | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql>

2、索引列使用了函数

mysql 复制代码
mysql> explain select * from t_userinfo where SUBSTR(id_no , 1, 3) = '100';
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_userinfo | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   94 |   100.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql>

类似的函数还有很多,如:concat等。

3、索引列参与了运算

mysql 复制代码
# id列参与了计算
mysql> explain select * from t_userinfo where id + 1 = 2;
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_userinfo | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   94 |   100.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql>

# 在参数侧计算,id列没有参与计算
mysql> explain select * from t_userinfo where id = 2 - 1;
+----+-------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table      | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t_userinfo | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql>

4、模糊查询最前面的为不确定匹配字符

mysql 复制代码
mysql> explain select * from t_userinfo where id_no like '%3';
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_userinfo | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   94 |    11.11 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql>

模糊匹配的占位符位于条件的首部 导致索引失效

5、索引列使用IS NULL或者IS NOT NULL

mysql 复制代码
mysql> explain select * from t_userinfo where id_no is not null;
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_userinfo | NULL       | ALL  | union_idx     | NULL | NULL    | NULL |   94 |   100.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql>

6、联合索引不满足最左匹配原则

使用到的联合索引KEY union_idx (id_no,username,age)

  • 最左边的字段为id_no,只要保证id_no出现在查询条件中,则会走该联合索引。
mysql 复制代码
mysql> explain select * from t_userinfo where id_no = '1001';
+----+-------------+------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| id | select_type | table      | partitions | type | possible_keys | key       | key_len | ref   | rows | filtered | Extra |
+----+-------------+------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t_userinfo | NULL       | ref  | union_idx     | union_idx | 75      | const |    1 |   100.00 | NULL  |
+----+-------------+------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql>

mysql> explain select * from t_userinfo where id_no = '1001' and username = 'Tom1';
+----+-------------+------------+------------+------+---------------+-----------+---------+-------------+------+----------+-------+
| id | select_type | table      | partitions | type | possible_keys | key       | key_len | ref         | rows | filtered | Extra |
+----+-------------+------------+------------+------+---------------+-----------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | t_userinfo | NULL       | ref  | union_idx     | union_idx | 206     | const,const |    1 |   100.00 | NULL  |
+----+-------------+------------+------------+------+---------------+-----------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from t_userinfo where username = 'Tom1' and id_no = '1001';
+----+-------------+------------+------------+------+---------------+-----------+---------+-------------+------+----------+-------+
| id | select_type | table      | partitions | type | possible_keys | key       | key_len | ref         | rows | filtered | Extra |
+----+-------------+------------+------------+------+---------------+-----------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | t_userinfo | NULL       | ref  | union_idx     | union_idx | 206     | const,const |    1 |   100.00 | NULL  |
+----+-------------+------------+------------+------+---------------+-----------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql>
  • 不满足最左匹配原则,失效的情况
mysql 复制代码
mysql> explain select * from t_userinfo where username = 'Tom1';
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_userinfo | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   94 |    10.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql>
mysql> explain select * from t_userinfo where age = 11;
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_userinfo | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   94 |    10.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql>

7、使用了select *

在上面的联合索引中,如果查询条件是age或username,当使用了select * ,肯定是不会走索引的

mysql 复制代码
mysql> explain select * from t_userinfo where age = 11;
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_userinfo | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   94 |    10.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)


mysql>

# 查询列明确指定需要查询的列
mysql> explain select id,id_no,username,age from t_userinfo where age = 11;
+----+-------------+------------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
| id | select_type | table      | partitions | type  | possible_keys | key       | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+------------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | t_userinfo | NULL       | index | union_idx     | union_idx | 211     | NULL |   94 |    10.00 | Using where; Using index |
+----+-------------+------------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select id,id_no,username,age from t_userinfo where username = 'Tom1';
+----+-------------+------------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
| id | select_type | table      | partitions | type  | possible_keys | key       | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+------------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | t_userinfo | NULL       | index | union_idx     | union_idx | 211     | NULL |   94 |    10.00 | Using where; Using index |
+----+-------------+------------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

mysql>

8、使用OR条件,但不是所有的条件都有索引

mysql 复制代码
# 虽然id列有索引,但是username列不满足左匹配原则,使用条件OR,不满足所有添加都有索引
mysql> explain select * from t_userinfo where id = '2' or username = 'Tom1';
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_userinfo | NULL       | ALL  | PRIMARY       | NULL | NULL    | NULL |   94 |    19.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

# 两个列都满足有索引,即使是使用条件OR,也会走索引
mysql> explain select * from t_userinfo where id = '2' or id_no = '1002';
+----+-------------+------------+------------+-------------+-------------------+-------------------+---------+------+------+----------+--------------------------------------------------+
| id | select_type | table      | partitions | type        | possible_keys     | key               | key_len | ref  | rows | filtered | Extra                                            |
+----+-------------+------------+------------+-------------+-------------------+-------------------+---------+------+------+----------+--------------------------------------------------+
|  1 | SIMPLE      | t_userinfo | NULL       | index_merge | PRIMARY,union_idx | union_idx,PRIMARY | 75,4    | NULL |    2 |   100.00 | Using sort_union(union_idx,PRIMARY); Using where |
+----+-------------+------------+------------+-------------+-------------------+-------------------+---------+------+------+----------+--------------------------------------------------+
1 row in set, 1 warning (0.01 sec)

mysql>

9、使用不等于(!= 或者 <>)操作符

当查询条件为字符串时,使用"<>"或"!="作为条件查询,不走索引

mysql> explain select * from t_userinfo where id_no <> '1002';
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_userinfo | NULL       | ALL  | union_idx     | NULL | NULL    | NULL |   94 |    98.94 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql>

但是如何字段类型为int的,还是可以走索引的。

mysql> explain select * from t_userinfo where id <> '1002';
+----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_userinfo | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |   95 |   100.00 | Using where |
+----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql>

10、使用order by时

mysql> explain select * from t_userinfo order by id_no asc;
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra          |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+----------------+
|  1 | SIMPLE      | t_userinfo | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   94 |   100.00 | Using filesort |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set, 1 warning (0.00 sec)

mysql>

11、列上的普通索引 not in和not exists

因为认为 not in 时结果集会比较大,而 in 的时候结果集会比较小。

但是not in 查询的是主键字段,还是会走索引的,普通索引就会失效。

mysql 复制代码
mysql> explain select * from t_userinfo where id_no not in('1002');
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_userinfo | NULL       | ALL  | union_idx     | NULL | NULL    | NULL |   94 |    98.94 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from t_userinfo where id_no in('1002');
+----+-------------+------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| id | select_type | table      | partitions | type | possible_keys | key       | key_len | ref   | rows | filtered | Extra |
+----+-------------+------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t_userinfo | NULL       | ref  | union_idx     | union_idx | 75      | const |    1 |   100.00 | NULL  |
+----+-------------+------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql>
相关推荐
mmsx1 小时前
android sqlite 数据库简单封装示例(java)
android·java·数据库
zpjing~.~2 小时前
Mongo 分页判断是否有下一页
数据库
2401_857600952 小时前
技术与教育的融合:构建现代成绩管理系统
数据库·oracle
秋恬意2 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
潇湘秦3 小时前
一文了解Oracle数据库如何连接(1)
数据库·oracle
雅冰石3 小时前
oracle怎样使用logmnr恢复误删除的数据
数据库·oracle
web前端神器3 小时前
mongodb给不同的库设置不同的密码进行连接
数据库·mongodb
从以前3 小时前
Berlandesk 注册系统算法实现与解析
数据库·oracle
Muko_0x7d23 小时前
Mongodb
数据库·mongodb
Ren_xixi3 小时前
redis和mysql的区别
数据库·redis·mysql