建表
create table staffs(
id int primary key auto_increment,
name varchar(24) not null default '' comment '姓名',
age int not null default 0 comment '年龄',
pos varchar (20) not null default '' comment '职位',
add_time timestamp not null default current_timestamp comment '入职时间'
)charset utf8 comment '员工记录表';
//插入数据
insert into staffs (name,age,pos,add_time) values('z3','22','manager',now());
insert into staffs (name,age,pos,add_time) values('july','23','dev',now());
insert into staffs (name,age,pos,add_time) values('2000','23','dev',now());
//建立索引
mysql> create index idx_staffs_nap on staffs(name,age,pos);
Query OK, 0 rows affected (0.16 sec)
Records: 0 Duplicates: 0 Warnings: 0
案例一
全值匹配我最爱…
mysql> explain select * from staffs where name = 'july' and age = 23 and pos = 'dev';
+----+-------------+--------+------+----------------+----------------+---------+-------------------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+----------------+---------+-------------------+------+-----------------------+
| 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 140 | const,const,const | 1 | Using index condition |
+----+-------------+--------+------+----------------+----------------+---------+-------------------+------+-----------------------+
1 row in set (0.00 sec)
我们发现type ref 并且使用到了索引 且ref为常量 效果不错
案例二 重点 最左前缀法则
mysql> explain select * from staffs where age = 23 and pos = 'dev';
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
type居然变成了all 全表扫描 且没有用到索引
我们在试一下
mysql> explain select * from staffs where name = 'july';
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 74 | const | 1 | Using index condition |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)
结论: 违背了最左前缀法则 如果我们索引了多列.要遵守最左前缀法则.指的是查询从索引最左前列开始并且不跳过索引中的列
也就是说我们建立的索引顺序是 name age pos 可以把第一个索引也就是name看成是火车头 没有火车头的话是跑步起来的,否则就会出现索引失效的情况.
如何理解不跳过索引中的列
mysql> explain select * from staffs where name='july' and pos = 'dev';
+----+-------------+--------+------+----------------+----------------+---------+-------+------+---------------------
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
+----+-------------+--------+------+----------------+----------------+---------+-------+------+---------------------
| 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 74 | const | 1 | Using index conditio
+----+-------------+--------+------+----------------+----------------+---------+-------+------+---------------------
1 row in set (0.00 sec)
结论: 我们可以看到 使用name 和pos 且跳过age索引,发现使用了索引但是仔细观察我们会发现ref 只用到了一个常量且 key_len长度也并没有增加也就说明了 pos并没有被使用到就好像 火车只可能是火车头和第二节车厢以此类推,绝对不可能出现火车头后面就是第三节车厢
案例三
不在索引列上做任何操作(计算 函数 (自动或手动)类型转换 ),会导致索引失效而转向全表扫描
mysql> select * from staffs where left(name,4)='july';
+----+------+-----+-----+---------------------+
| id | name | age | pos | add_time |
+----+------+-----+-----+---------------------+
| 2 | july | 23 | dev | 2019-01-02 10:25:49 |
+----+------+-----+-----+---------------------+
1 row in set (0.00 sec)
//首先我们看这条sql语句是可以查到的并没有出现什么问题,我们在看一下性能
mysql> explain select * from staffs where left(name,4)='july';
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
//发现我们使用了全表扫描且并没有使用的索引不要在索引列上做计算
案例四
存储引擎不能使用索引中范围条件右边的列
mysql> explain select * from staffs where name = 'july' and age > 11 and pos = 'dev';
+----+-------------+--------+-------+----------------+----------------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+----------------+----------------+---------+------+------+-----------------------+
| 1 | SIMPLE | staffs | range | idx_staffs_nap | idx_staffs_nap | 78 | NULL | 1 | Using index condition |
+----+-------------+--------+-------+----------------+----------------+---------+------+------+-----------------------+
1 row in set (0.00 sec)
我们可以根据长度来分析出来 只用到了两个索引,且age只用到了排序并没有搜索
案例五
尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *
mysql> explain select name,age,pos from staffs where name = 'july' and age = 11 and pos = 'dev';
+----+-------------+--------+------+----------------+----------------+---------+-------------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+----------------+---------+-------------------+------+--------------------------+
| 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 140 | const,const,const | 1 | Using where; Using index |
+----+-------------+--------+------+----------------+----------------+---------+-------------------+------+--------------------------+
1 row in set (0.00 sec)
可以看到extra 使用l USING index 这样就表现的更好了
在通过这个方法 看看案例四的情况
mysql> explain select name,age,pos from staffs where name = 'july' and age > 11 and pos = 'dev';
+----+-------------+--------+------+----------------+----------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+--------------------------+
| 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 74 | const | 1 | Using where; Using index |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)
虽然我们使用到了 > 范围条件可我们的type还是ref也就是说明我们并没有使用age,这样也就没有索引失效啦
案例六
在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
mysql> explain select * from staffs where name != 'july';
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | idx_staffs_nap | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
案例七
is null is not null 也无法使用个索引
mysql> explain select * from staffs where name is null;
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
1 row in set (0.02 sec)
mysql> explain select * from staffs where name is not null;
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | idx_staffs_nap | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
案例八 重点
like %如何使用
mysql> explain select * from staffs where name like '%july%';
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from staffs where name like '%july';
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from staffs where name like 'july%';
+----+-------------+--------+-------+----------------+----------------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+----------------+----------------+---------+------+------+-----------------------+
| 1 | SIMPLE | staffs | range | idx_staffs_nap | idx_staffs_nap | 74 | NULL | 1 | Using index condition |
+----+-------------+--------+-------+----------------+----------------+---------+------+------+-----------------------+
1 row in set (0.00 sec)
我们发现只有百分号在右边的时候才不会出现全表扫描,但是我们如果必须要使用两个百分号才能查出结果该怎么办呢,
大招索引覆盖
mysql> explain select name from staffs where name like '%july%';
+----+-------------+--------+-------+---------------+----------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+----------------+---------+------+------+--------------------------+
| 1 | SIMPLE | staffs | index | NULL | idx_staffs_nap | 140 | NULL | 3 | Using where; Using index |
+----+-------------+--------+-------+---------------+----------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
如果要使用两个百分号,还要不导致索引失效,则结果集内容是复合索引中的内容
案例九 低级错误一定要注意
字符串不加单引号索引失效
mysql> explain select * from staffs where name=2000;
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | idx_staffs_nap | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
1 row in set (0.02 sec)
mysql> explain select * from staffs where name='2000';
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 74 | const | 1 | Using index condition |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)
案例十
少用or 用它连接时会索引失效
mysql> explain select * from staffs where name='2000' or name='z3';
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | idx_staffs_nap | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
1 row in set (0.02 sec)
总结
假设index(a,b,c)
where语句 | 索引是否被使用 |
---|---|
where a=3 | Y 使用到a |
where a=3 and b=5 | Y使用到a b |
where a=3 and b=5 and c=4 | Y 使用到 a b c |
where b=3 或者where b=3 and c=4 或者 where c=4 | N 没有使用到 看案例二 |
where a=3 and c=5 | Y 使用到了a 看案例二 |
where a=3 abd b>4 and c=5 | Y 使用到了a 和b看案例四 |
where a=3 and b like ‘kk%’ and c=4 | Y 使用到了a b |