一、索引对查询效率的提升
这是一个拥有300万行数据的表格:
表的大概结构:
查询id:
用了很短的执行时间,如果查询title字段:
用了4.6秒的时间,查询效率比较低,原因是id字段是主键,有主键索引,而title字段没有索引,所以查询效率比较低。
要解决这个问题,可以对title字段建立索引。
create index idx_item_title on tb_item(title);
创建成功:
大概花费了一分钟的时间。
再次查询:
查询效率大大提升了。
二、避免索引失效
创建一张新的表:
对这个表的三个字段创建联合索引:
create index idx_seller_name_sta_addr on tb_seller(name,status,address);
1.全值匹配
对索引中所有列都指定具体值。
三个查询的值都为联合索引中的字段。
这时候用explain来查看它的执行计划:
走了索引。
2.最左前缀法则
创建索引的时候的语句是:
create index idx_seller_name_sta_addr on tb_seller(name,status,address);
这个联合索引对name、status、address三个字段建立了索引,按照最左前缀法则,查询的时候,如果想使用这个索引,查询条件中要包含name字段,并且不能够跳过status字段,也就是如果想使用这个索引查询name和address,那么在查询条件中,也必须包含status字段,不能跳过它。
-
只查询name字段:
走了索引。
-
查询name和status字段:
也走了索引。
-
查询name、status和address字段:
可以看到走了索引。
-
只查询status和address字段:
没有走索引。
-
查询address字段:
没有走索引。
-
如果查询条件中包含name、status和address字段,但是打乱顺序:
可以看到也走了索引。
所以只要查询条件中包含索引的最左字段(name),不跳过字段,那么这个索引就没有失效,可以使用。
-
如果查询的是name字段和address字段,也就是中间跳过了status字段:
可以看到还是走了索引,但是索引的长度是403,和只查询name字段时的索引长度一样,所以可以推断出,name字段走了索引,而address字段没有走索引。
3.范围查询右边的列,不能使用索引 。
通过一组对比:
第二次查询的status字段是范围查找,可以看到两次查找索引的长度发生了变化,也就是第一个和第二个字段使用了索引,范围查询右边的列的索引失效了。
4.在索引列上进行运算操作, 索引将失效
可以看到,进行的是全表扫描,索引失效了。
5.字符串不加单引号,造成索引失效
如果是字段是varchar类型,不加单引号也可以查询出来:
加不加单引号索引长度发生了变化,没加单引号的时候,只有name字段走了索引,因为数据库检测到status是varchar类型,会自动进行隐式类型转换,这时候索引就失效了。
6.尽量使用覆盖索引,避免select *
覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。
也就是说,如果要查询的字段是name,status和address,(select name,status,address from ....)这时候就尽量不使用select * from
:
可以看到如果是select * from
也使用了索引,但是extra给出的信息不同,在select * from
中,using index condition
是指:查找使用了索引,但是需要回表查询数据,所以select * from
相对于select name,status,address from ....
查询效率还是慢了的。
- using index :使用覆盖索引的时候就会出现
- using where:在查找使用索引的情况下,需要回表去查询所需的数据
- using index condition:查找使用了索引,但是需要回表查询数据
-
using index ; using where:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据
如果查询的字段有不包含在联合索引中的,需要回表查询:
7.用or分割开的条件
如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
索引失效,走的是全表扫描:
如果换成and:
8.以%开头的Like模糊查询,索引失效。
以%开头,索引失效:
要解决这个问题可以使用覆盖索引(sellerid有索引):
password没有索引:
9.如果MySQL评估使用索引比全表更慢,则不使用索引。
对address字段创建单列索引:
这时候查询,查询北京市没有走索引,而查询西安市走了索引:
查看一下表:
在表中,共有12条数据,11条都包含了北京市,查询的又是select * from,需要回表,所以走索引还不如全表扫描快,所以索引就失效了。
10.is NULL , is NOT NULL 有时索引失效
在表中,address字段所有的行都是非空的,所以is null的情况没有或基本没有,这时候就会走索引。
而每个字段都是is not null的,所以就不走索引了。
同理(name字段有索引):
11.in 走索引, not in 索引失效。
12.单列索引和复合索引
尽量使用复合索引,而少使用单列索引 。
create index idx_name_sta_address on tb_seller(name, status, address);
创建一个这样的复合索引相当于创建了三个索引:
name
name + status
name + status + address
如果创建单列索引:
create index idx_seller_name on tb_seller(name);
create index idx_seller_status on tb_seller(status);
create index idx_seller_address on tb_seller(address);
如果数据库中只有这三个单列索引,那么对name、status、address字段进行联合查询的时候,数据库只会选这三个单列索引中的一个最优索引(辨识度最高的索引),不会使用全部索引。
使用复合索引:
使用三个单列索引,只是用了一个索引:
三、查看索引的使用情况
show status like 'Handler_read%';
show global status like 'Handler_read%';
当前会话的索引使用情况:
全局的索引使用情况:
参数含义:
- Handler_read_first:索引中第一条被读的次数。如果较高,表示服务器正执行大量全索引扫描(这个值越低越好)。
- Handler_read_key:如果索引正在工作,这个值代表一个行被索引值读的次数,如果值越低,表示索引得到的性能改善不高,因为索引不经常使用(这个值越高越好)。
- Handler_read_next :按照键顺序读下一行的请求数。如果你用范围约束或如果执行索引扫描来查询索引列,该值增加。
- Handler_read_prev:按照键顺序读前一行的请求数。该读方法主要用于优化ORDER BY ... DESC。
- Handler_read_rnd :根据固定位置读一行的请求数。如果你正执行大量查询并需要对结果进行排序该值较高。你可能使用了大量需要MySQL扫描整个表的查询或你的连接没有正确使用键。这个值较高,意味着运行效率低,应该建立索引来补救。
- Handler_read_rnd_next:在数据文件中读下一行的请求数。如果你正进行大量的表扫描,该值较高。通常说明你的表索引不正确或写入的查询没有利用索引。