索引失效(应尽量避免)
1 全值匹配我最爱
2 最佳左前缀法则(如果索引了多列,要遵守最左前缀法则,查询从索引的最左侧列开始并且不跳过索引中的列,,比如建的索引为ABC,使用的时候AC,那么C用不到索引查询)
3 不再索引列上做任务操作(计算,函数,(自动 or 手动)类型转换),会导致索引失效而转向全表扫描
4 存储引擎不能使用索引中范围条件右边的列
5 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*
6 mysql在使用不等于(!= 或 <>)时无法使用索引导致全表扫描
7 is null,is not null 也无法使用索引
8 like 以通配符开头(’%abc…’)mysql索引失效会变成全表扫描操作,解决like '%字符串%'时索引不被使用的方法
9 字符串不加单引号索引失效
10 少用or,用它来连接时索引失效
最佳左前缀案例
#建表
create table staffs(
id int primary key auto_increment,
name varchar(25) not null default '',
age int not null default 0,
pos varchar(20) not null comment '职位',
add_time timestamp not null comment '入职时间'
);
#插入数据
insert into staffs(name,age,pos,add_time) values('z3',22,'manager',now());
insert into staffs(name,age,pos,add_time) values('lili',23,'dev',now());
insert into staffs(name,age,pos,add_time) values('xiaowagn',23,'dev',now());
insert into staffs(name,age,pos,add_time) values('1500',23,'dev',now());
#查询
mysql> select * from staffs;
+----+----------+-----+---------+---------------------+
| id | name | age | pos | add_time |
+----+----------+-----+---------+---------------------+
| 1 | z3 | 22 | manager | 2020-08-15 10:01:21 |
| 2 | lili | 23 | dev | 2020-08-15 10:01:21 |
| 3 | xiaowagn | 23 | dev | 2020-08-15 10:01:21 |
| 4 | 1500 | 23 | dev | 2020-08-15 13:25:02 |
+----+----------+-----+---------+---------------------+
#建立联合索引
alter table staffs add index idx_staffs_nameAgePos(name,age,pos);
#查看sql执行计划 查询name使用到了索引,并且type为ref
mysql> explain select * from staffs where name = 'z3';
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | staffs | NULL | ref | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 77 | const | 1 | 100.00 | NULL |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
#查询 name age 同样用到了索引 type为ref,ref列看到两个const,说明两个索引都用到了
mysql> explain select * from staffs where name = 'z3' and age = 22;
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------+------+----------+-------+
| 1 | SIMPLE | staffs | NULL | ref | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 81 | const,const | 1 | 100.00 | NULL |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------+------+----------+-------+
#查询name age pos 用到了索引 type为ref,ref列看到3个const,说明3个索引都用到了
mysql> explain select * from staffs where name = 'z3' and age = 22 and pos = 'manager';
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
| 1 | SIMPLE | staffs | NULL | ref | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 143 | const,const,const | 1 | 100.00 | NULL |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
#仅查询age,会发现type为ALL,为全表扫描
mysql> explain select * from staffs where age = 22 ;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
# 查询 pos ,会发现type为ALL,为全表扫描
mysql> explain select * from staffs where pos = 'manager';
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
# 查询 age pos ,可以看到type为ALL,没有用到索引
mysql> explain select * from staffs where age = 22 and pos = 'manager';
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
explain select * from staffs where name = 'z3' and pos = 'manager';
#查询name,pos,可以看到type为ref,ref列看到一个const,说明只用到了一个索引,由于跳过一个索引列,因此pos索引无法使用
mysql> explain select * from staffs where name = 'z3' and pos = 'manager';
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+
| 1 | SIMPLE | staffs | NULL | ref | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 77 | const | 1 | 33.33 | Using index condition |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+
对索引列进行操作会导致索引失效
#对索引字段name不做修改,可以正常使用到索引
mysql> explain select * from staffs where name = 'z3';
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | staffs | NULL | ref | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 77 | const | 1 | 100.00 | NULL |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
#对索引列使用函数导致索引失效,变成了全表扫描
mysql> explain select * from staffs where trim(name) = 'z3';
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
存储引擎不能使用索引中范围条件右边的列
#
mysql> explain select * from staffs where name = 'z3' and age = 21 and pos = 'manager';
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
| 1 | SIMPLE | staffs | NULL | ref | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 143 | const,const,const | 1 | 100.00 | NULL |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
# 将age改为 >21,会发现type变成range,并且使用到的索引长度由之前的额143变成81,说明没有使用到pos索引
mysql> explain select * from staffs where name = 'z3' and age > 21 and pos = 'manager';
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | staffs | NULL | range | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 81 | NULL | 1 | 33.33 | Using index condition |
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
尽量使用覆盖索引
#使用了select *
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
| 1 | SIMPLE | staffs | NULL | ref | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 143 | const,const,const | 1 | 100.00 | NULL |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
#查询的所有字段都在建立的索引中,可以看到在extra中使用了using index,相对select *性能更好
mysql> explain select name,age,pos from staffs where name = 'z3' and age = 22 and pos = 'manager';
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ref | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 143 | const,const,const | 1 | 100.00 | Using index |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------------+
mysql在使用不等于(!= 或 <>)时无法使用索引导致全表扫描
#使用 = 时可以用到索引
mysql> explain select * from staffs where name = 'z3';
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | staffs | NULL | ref | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 77 | const | 1 | 100.00 | NULL |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
#使用 != 时,索引失效,变成全表扫描
mysql> explain select * from staffs where name != 'z3';
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ALL | idx_staffs_nameAgePos | NULL | NULL | NULL | 3 | 100.00 | Using where |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
使用 <> 时,索引失效,变成全表扫描
mysql> explain select * from staffs where name <> 'z3';
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ALL | idx_staffs_nameAgePos | NULL | NULL | NULL | 3 | 100.00 | Using where |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
is null,is not null 也无法使用索引
#查询字段使用 is null,由于name字段设置了not null ,因此查找is null是不可能有结果的,就出现了Impossible WHERE,如果该字段允许为null,就会发现该字段索引用不上,变成了全表扫描
mysql> explain select * from staffs where name is null;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
#使用 is not null 同样会使得索引失效,变成了全表扫描
mysql> explain select * from staffs where name is not null;
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | staffs | NULL | ALL | idx_staffs_nameAgePos | NULL | NULL | NULL | 3 | 66.67 | Using where |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
like 以通配符开头(’%abc…’)mysql索引失效会变成全表扫描操作,
解决like '%字符串%'时索引不被使用的方法
#建表
create table tbl_user(
id int primary key auto_increment,
name varchar(20) not null,
age int,
email varchar(20)
);
#插入数据
insert into tbl_user(name,age,email) values('1aa1',21,'b@163.com');
insert into tbl_user(name,age,email) values('2aa2',222,'a@163.com');
insert into tbl_user(name,age,email) values('3aa3',265,'c@163.com');
insert into tbl_user(name,age,email) values('4aa4',21,'d@163.com');
insert into tbl_user(name,age,email) values('aa',121,'e@163.com');
#查询
mysql> select * from tbl_user;
+----+------+------+-----------+
| id | name | age | email |
+----+------+------+-----------+
| 1 | 1aa1 | 21 | b@163.com |
| 2 | 2aa2 | 222 | a@163.com |
| 3 | 3aa3 | 265 | c@163.com |
| 4 | 4aa4 | 21 | d@163.com |
| 5 | aa | 121 | e@163.com |
+----+------+------+-----------+
#没有建索引时,不管查一个字段还是多个字段基本都是全表扫描
#现在建一个联合索引
create index idx_tbl_nameAge on tbl_user(name,age);
#like中 % 加在两边 索引失效 全表扫描
mysql> explain select * from tbl_user where name like '%aa%';
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tbl_user | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 20.00 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
#like中 % 加在左边 索引失效 全表扫描
mysql> explain select * from tbl_user where name like '%aa';
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tbl_user | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 20.00 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
#like中 % 加在右边 索引没有失效 type为range,使用到了索引
mysql> explain select * from tbl_user where name like 'aa%';
+----+-------------+----------+------------+-------+-----------------+-----------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+-----------------+-----------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | tbl_user | NULL | range | idx_tbl_nameAge | idx_tbl_nameAge | 62 | NULL | 1 | 100.00 | Using index condition |
+----+-------------+----------+------------+-------+-----------------+-----------------+---------+------+------+----------+-----------------------+
但是如果%一定要加在两边,否则查询出的数据有误,又要使用索引,解决办法,尽量使用覆盖索引
#这里不使用select * 而是select 建立索引的字段,在使用覆盖索引的情况下,like '%字符串%' 索引就不会失效
mysql> explain select id,name,age from tbl_user where name like '%aa%';
+----+-------------+----------+------------+-------+---------------+-----------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+---------------+-----------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | tbl_user | NULL | index | NULL | idx_tbl_nameAge | 67 | NULL | 5 | 20.00 | Using where; Using index |
+----+-------------+----------+------------+-------+---------------+-----------------+---------+------+------+----------+--------------------------+
#select 索引 + 未建索引字段时,like '%aa%'会导致索引失效,转为全表扫描
mysql> explain select id,name,age,email from tbl_user where name like '%aa%';
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tbl_user | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 20.00 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
字符串不加单引号索引失效
# name字段为varchar类型,但是这里查询时,2000为int类型,mysql查询时会自动将int的2000隐式转换为varchar类型的'2000',就像上面说的,对索引列进行的任何操作都会导致索引失效,因此这里的name索引失效,导致全表扫描
mysql> explain select * from tbl_user where name = 2000;
+----+-------------+----------+------------+------+-----------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+-----------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tbl_user | NULL | ALL | idx_tbl_nameAge | NULL | NULL | NULL | 5 | 20.00 | Using where |
+----+-------------+----------+------------+------+-----------------+------+---------+------+------+----------+-------------+
#这里name的值3
mysql> explain select * from staffs where name = '2000';
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | staffs | NULL | ref | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 77 | const | 1 | 100.00 | NULL |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+