上篇文章我们说了索引排序和排序注意事项,排序不要用复杂的函数,范围查找的时候,左边的列有索引效果,后面的列没有,除非指定特定值,like模糊查询时候,前面不要用%,asc desc不要混用。索引排序之所以快,因为b+树里面的双向链表和单向链表数据结构原本就是按索引从小到大排序好的,所以直接取出数据就好,不需要在磁盘和内存中排序。
用于分组
有时候我们会对一些相同的数据进行分组:SELECT name, birthday, phone, COUNT(*) FROM person_info GROUP BY name, birthday, phone;
先按name排序分组,所有name相同分为一组。2)再吧name相同值记录继续按birthday分组,看起来大分组里分了小分组。3)在吧上面数据按phone分成一个更小的分组。如果没有索引的话,这些都需要再内存中实现。
回表的代价
上面一直说回表,一直没仔细介绍过,当我们用select * from where person_info where name > ‘Anny’ and name <’barlow’;查询的步骤,先在列的叶子节点找到列name>anny的主键,在通过主键回表查询聚簇索引查询所有的数据,因为我们select * ,所有数据存储在聚簇索引的叶子节点中。
而这个过程有两个重点,顺序I/O和随机I/O:
先按列name排序,所以’Anny’和’barlow’这些因为列b+树已经按他们排序好了,所以他们是相连的,是顺序I/O,查询效率高,之后获取到他们的主键id。
用主键id查询聚簇索引的b+树,这时候的id不是相连的,所以是随机I/O,效率比顺序I/O低很多。
所以会使用两个索引,二级索引的时候顺序I/O,效率高很多,聚簇索引的时候随机I/O效率低很多。所以如果回表的主键数据太多,占百分之80以上需要回表,那还不如直接聚簇索引全表扫描查询。
我们回忆一下,mysql客户端先通过tcp/ip访问服务端,然后缓存是否存在,不存在,解码sql,之后mysql查询优化器,最后直接进入存储引擎。
那我们什么时候用全表扫描的方式,什么时候用二级索引+回表的聚簇索引方式呢?这个就是神秘的mysql查询优化器需要做的事,查询优化器会先对数据进行统计,然后根据这些数据,来判断是全表扫描还是二级索引+回表的方式,如果回表的数据比较少,就用二级索引+回表,比如SELECT * FROM person_info ORDER BY name, birthday, phone LIMIT 10; 这里加了个limit10,所以查询回表数据不是很多,会采用二级索引+回表方式。若这样SELECT * FROM person_info ORDER BY name, birthday, phone,则会全表扫描。
索引覆盖
以我们的idx_name_birthday_phone联合索引为例,当我们查询的列只有这三个索引的时候,SELECT name, birthday, phone FROM person_info ORDER BY name, birthday, phone; 大家如果认真看前面的文章就知道了,联合索引建立的b+树叶子节点存储的是什么?是name,birthday,phone和主键,这时候查询的值已经从二级索引b+树子叶查询到了,所以不需要在用主键聚簇索引去另一个b+树回表,所以即使我们需要查询其他列非索引数据时候,也不鼓励用*去查询。