使用索引扫描进行排序

209 阅读2分钟

这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

使用MySQL查询有两种方式能够生成有序的结果

  1. MySQL的排序操作
  2. 按照索引顺序进行扫描 后者由于只需要按照索引进行顺序扫描,速度会比前者快很多。

但是如果索引列无法满足查询结果的列,那么每次扫描一条索引记录就需要回表查询一次对应的行,由于是随机IO,因此按照“索引顺序回表查询”比“顺序全表扫描”要慢,因此尽可能在设计索引时既满足排序又可以用于查找行。

使用索引扫描进行排序的条件

如果想要索引既能满足排序,又能由于查找,需要注意以下原则

  1. 索引列的顺序和ORDER BY子句的顺序完全一致,并且排序方向相同。如下面4条查询,只有查询1和查询3的排序是完全按照索引。
    # 1: Using Index
    explain select source_system, batch_no from account_batch order by source_system, batch_no;
    # 2: Using Index; Using filesort
    explain select source_system, batch_no from account_batch order by source_system desc, batch_no desc;
    # 3: Using Index
    explain select source_system, batch_no from account_batch order by source_system desc, batch_no;
    # 4: Using Index; Using filesort
    explain select source_system, batch_no from account_batch order by batch_no, source_system
    
  2. 多表关联查询时,只有ORDER BY子句引用的字段全部为第一个表时才能使用索引做排序。如下面2条查询,查询2由于ORDER BY子句包含了第二个表的字段,因此无法按照索引排序。
    # 1: Using index
    explain select a.source_system, a.template_id from account_batch a left join account_template t on a.template_id = t.id order by source_system, template_id;
    # 2: Using filesort
    explain select a.source_system, a.template_id from account_batch a left join account_template t on a.template_id = t.id order by source_system, template_id, a.template_name;
    
  3. 前导列为常量,可以不满足最左前缀的要求。如下面的查询可以应用(source_system、template_id、batch_no)索引
    # Using where; Using index
    explain
    select source_system, batch_no
    from account_batch
    where source_system = 'JL_MANUAL_PRC'
    order by template_id, batch_no;