(1)针对问题解析:
为什么在这个案例中MySQL默认会选择对主键聚簇索引进行扫描?
为什么没使用index_category这个二级索引进行扫描?
即使用了聚簇索引,为什么这个SQL以前没有问题,现在突然就有问题了?
(2)问题分析:
select from products where category=’xx’ and sub_category=’xx’ order by id desc limit xx,xx
这个SQL语句,MySQL为什么选择对聚簇索引进行扫描?其实关于这个逻辑说起来并不复杂,因为都知道这个表是一个亿级数据量的大表,那么对于他来说,index_category这个二级索引
也是比较大的,所以此时对于MySQL来说,有这么一个判断,他觉得如果要是从index_category二级索引里来查找符合where条件的一批数据,接着还得回表回到聚簇索引里去,因为SQL语句是要
select 的,所以这里必然涉及到一次回表操作,回到聚簇索引里去把所有字段的完整数据都查出来,但是在回表之前,他必然要做完order by id desc limit xx,xx 这个操作。
举个例子,比如 where category=’xx’ and sub_category=’xx’ , 从 index_category 二级索引里查找出了一大批数据。
比如从二级索引里查出几万条数据,接着因为二级索引里包含主键id值的,所以此时他得按照order by id desc 这个排序语法,对着几万条数据基于临时磁盘文件进行filesort磁盘排序,排序
完之后,再按照limit xx,xx 语法,把指定位置的几条数据拿出来,假设是limit 0,10,那么就是把10条数据拿出来。
拿出10条数据之后,再回到聚簇索引里去根据id查找,把这10条数据的完整字段都查出来,这就是MySQL认为如果你使用index_category的话,可能发生的一个情况。所以如果根据
where category=’xx’ and sub_category=’xx’ , 从index_category二级索引里查出来的数据太多,还得在临时磁盘里排序,可能性能会很差,因此MySQL就把这种方式判定为一种不太好的方式, 因此MySQL选择换一种方式,也就是说,直接扫描主键的聚簇索引,因为聚簇索引都是按照id值有序的,所以扫描的时候,直接按order by id desc 这个倒序顺序扫描就可以了,然后因为是limit 0,10 也就知道仅仅只拿10条数据就可以了。
所以MySQL在按顺序扫描聚簇索引的时候,就会对每一条数据都采用Using where的方式,跟where category='xx' and sub_category='xx' 条件进行比对,符合条件的直接放入结果集,
最多就是放10条数据进去就可以返回了。此时MySQL认为,按顺序扫描聚簇索引,拿到10条符合where条件的数据,速度应该是很快的,很可能比使用index_category二级索引那个方案更快,因此此时他 就采用扫描聚簇索引的这种方式。
考虑一个问题:
这个SQL语句实际之前线上系统运行一直没问题,也就说,之前线上系统而言,即采用聚簇索引的方案,其实这个SQL语句一般运行都不慢,最起码不会超过1s,那么为什么
在某一天晚上突然报大量的慢查询? 原因很简单,其实在之前的时候, where category=’xx’ and syb_category=’xx’ 这个条件通常是有返回值的,就是根据条件里的取值,扫描聚簇索引的时候,
通常都是很快就能找到符合条件的值以及返回的,所以性能没什么大问题。
但是后来可能商品系统的运营人员,在商品管理的时候加了几种商品分类和子类,但是这几种分类的组合其实没有对应的商品。
也就是说,那一天晚上,很多用户使用分类和子类去筛选商品,where category=’新分类’ and sub_category=’新子类’ 这个条件实际上是查不到数据的。
所以,底层在扫描聚簇索引的时候,扫描不到符合条件的where条件的结果,一下子把聚簇索引全部扫描了一遍,等于是上亿条数据全表扫描了一遍,都没找到符合where category=’新分类’
and sub_category=’新子类’这个条件的数据。
也正因如此,才导致这个SQL语句频繁的出现几十秒的慢查询,进而导致MySQL连接资源打满,商品系统崩溃。
这个案例中,因为从聚簇索引查每一行的数据做where条件的筛选,然后一直找不到数据,导致相当于全表扫描一遍。这个表上没创建任何索引全表扫描查询的效果是一样的。
换种SQL写法,
select * from products where id in (select id from products where category=’xx’ and sub_category=’xx’) order by id desc limit xx,xx
先从二级索引里查出这些数据并排序,以实际为准仅是提供一种思路。
(3)总结:
到这问题就分析清楚了,核心就是能看懂SQL的执行计划,理解他为什么那么慢,只要理解了,就是想办法解决。
问题1:哪几种extra执行速度很慢?
解答:Using temporary block nested loop join (join 操作被驱动表关联条件没有索引)
知识点:inner join下关联条件on 和 where 条件等价, left join 或right join以主表为主 on条件不对主表筛选。