0、环境
mysql 5.7
table1 300w数据
table2 2000w数据
1、问题描述
在开发过程中发现,列表数据分页查询的时候,偏移量为0或1时查询的语句执行很慢(约2s),而偏移量为2时查询语句都相对保持很快的速度(约80ms),性能差别达到了二十倍。附上sql如下:
SELECT a.* FROM table1 a
LEFT JOIN table2 b ON a.f_user_id = b.f_id
WHERE b.mobile='130xxxxxxxx'
ORDER BY a.f_time DESC LIMIT 0 , 10;
2、排查过程
2.1、explain
惯例,在数据库中做性能分析,对两种情况的sql分别explain,情况如下:
2.2、分析
type表示MySQL在表中找到所需行的方式,又称“访问类型”,常用的类型有: ALL, index, range, ref, eq_ref, const, system, NULL(从左到右,性能从差到好)
| type | 描述 |
|---|---|
| ALL | Full Table Scan, MySQL将遍历全表以找到匹配的行 |
| index | Full Index Scan,index与ALL区别为index类型只遍历索引树 |
| range | 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值 |
| ref | 只检索给定范围的行,使用一个索引来选择行 |
| eq_ref | 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件 |
| const、system | 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system |
| NULL | MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。 |
粗略的可以看出,较慢的语句使用index遍历了全索引树,而较快的语句使用ref。并且在偏移量从1改为2的时候,mysql优化器的决定发生了突变,索引由原先的idx_issue_ts改变为了idx_one_id。这就导致了整个查询语句的性能发生了改变。
2.3、解决办法
在能够预估业务数据量的情况下,可以人为地指定mysql优化器使用的索引,方法如下:
SELECT a.* FROM table1 a force index(idx_user_id)
LEFT JOIN table2 b ON a.f_user_id = b.f_id
WHERE b.mobile='130xxxxxxxx'
ORDER BY a.f_time DESC LIMIT 0 , 10;
2.4、原因探究
知道了索引的选择不同导致了查询语句的性能差异,那么为什么MYSQL在偏移量仅有微小差别的情况下会选择不同的索引呢?并且查询语句在执行时的执行顺序是怎样的?