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>
相关推荐
kura_tsuki5 小时前
[Oracle数据库] Oracle 常用函数
数据库·oracle
YA3336 小时前
java基础(十)sql的mvcc
数据库
weixin_3077791310 小时前
VS Code配置MinGW64编译SQLite3库
开发语言·数据库·c++·vscode·算法
SelectDB10 小时前
Apache Doris 4.0 AI 能力揭秘(一):AI 函数之 LLM 函数介绍
数据库·人工智能·数据分析
我是哈哈hh11 小时前
【MySQL】在UBuntu环境安装以及免密码登录入门
linux·数据库·mysql·ubuntu
喪彪11 小时前
MySQL新手教学
数据库·mysql·adb
丘大梨14 小时前
QT 基础聊天应用项目文档
运维·数据库·系统架构
HMBBLOVEPDX14 小时前
MySQL的多版本并发控制(MVCC):
数据库·mysql·mvcc
.用户昵称已存在.14 小时前
MongoDB 从入门到精通:安装配置与基础操作指令详解
数据库·mongodb