《高性能MySQL》笔记-使用索引扫描来做排序

MySQL有两种方式可以生成有序的结果:通过排序操作;或者按索引顺序扫描;如果EXPLAIN出来的type列的值为“index“,则说明MySQL使用索引扫描来做排序(不要和Extra列的“Using index”搞混淆了)。
扫描索引本身是很快的,因为需要从一条索引记录移动到紧接着的下一条记录。但如果索引不能覆盖查询所需的全部列,那就不得不每扫描一条索引记录就都回表查询一次对应的行。这基本上都是随机I/O,因此按索引顺序读取数据的速度通常比顺序地全表扫描慢,尤其是在I/O密集型的工作负载时。
MySQL可以使用同一个索引既满足排序,又用于查找行。因此,如果可能,设计索引时应该尽可能地同时满足这两种任务,这样是最好的。
只有当索引的列顺序和ORDER BY子句的顺序完全一致,并且所有列的排序方向(倒序或正序)都一样时,MySQL才能够使用索引来对结果做排序。如果查询需要关联多张表,则只有当ORDER BY子句引用的字段全部为第一个表时,才能使用索引做排序。OEDER BY子句和查找型查询的限制是一样的:需要满足索引的最左前缀的要求;否则,MySQL都需要执行排序操作,而无法利用索引排序。
有一种情况下ORDER BY子句可以不满足索引的最左前缀的要求,就是前导列为常量的时候。如果WHERE子句或者JOIN子句中对这些列制定了常量,就可以“弥补“索引的不足。
例如,Sakila示例数据库的表rental在列(rental_date,inventory_id,customer_id)上有名为rental_date的索引。
《高性能MySQL》笔记-使用索引扫描来做排序_第1张图片
MySQL可以使用rental_date索引为下面的查询做排序,从EXPLAIN中可以看到没有出现文件排序(filesort)操作:
《高性能MySQL》笔记-使用索引扫描来做排序_第2张图片
即使OEDER BY子句不满足索引的最左前缀的要求,也可以用于查询排序,这是因为索引的第一列被指定为一个常数。
还有更多可以使用索引做排序的查询示例。下面这个查询可以利用索引排序,是因为查询为索引的第一列提供了常量条件,而使用第二列进行排序,将两列组合在一起,就形成了索引的最左前缀:
… WHERE rental_date = ‘2005-05-25’ ORDER BY inventory_id DESC;
下面这个查询也没有问题,因为ORDER BY使用的两列就是索引的最左前缀:
… WHERE rental_date > ‘2005-05-25’ ORDER BY rental_date, inventory_id;
下面是一些不能使用索引做排序的查询:

  • 下面这个查询使用了两种不同的排序方向,但是索引列都是正序排列的:
    … WHERE rental_date = ‘2005-05-25’ ORDER BY inventory_id DESC , customer_id ASC;
  • 下面这个查询的ORDER BY子句中引用了一个不在索引中的列:
    … WHERE rental_date = ‘2005-05-25’ ORDER BY inventory_id , staff_id;
  • 下面这个查询的WHERE和ORDER BY中的列无法组合成索引的最左前缀:
    … WHERE rental_date = ‘2005-05-25’ ORDER BY customer_id;
  • 下面这个查询在索引列的第一列上是范围条件,所以MySQL无法使用索引的其余列:
    … WHERE rental_date = ‘2005-05-25’ ORDER BY inventory_id , customer_id;
  • 这个查询在inventory_id列上有多个等于条件。对于排序来说,这也是一种范围查询:
    … WHERE rental_date = ‘2005-05-25’ AND inventory_id IN(1,2) ORDER BY customer_id;

下面这个例子理论上是可以使用索引进行关联排序的,但由于优化器在优化时将film_actor表当做关联的第二张表,所以实际上无法使用索引:
《高性能MySQL》笔记-使用索引扫描来做排序_第3张图片
使用索引做排序的一个最重要的用法是当查询同时有ORDER BY和LIMIT子句的时候。

你可能感兴趣的:(数据库,使用索引扫描来做排序)