持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
在上篇文章里,我们介绍了覆盖索引
但是
索引覆盖查询还是有很多陷阱可能导致无法实现优化,MySQL查询优化器会在执行查询前判断是否有一个索引能进行覆盖,假设索引覆盖了where条件中的字段,但不是整个查询涉及的字段。MySQL5.5或者更早的版本还是会回表获取数据行,尽管这一行可能最终还是会被过滤掉。
我们举个栗子:
SELECT * FROM products where actor = 'SEAM CARREY'
AND title like '%APOLLO%';
像上面这个查询索引是无法覆盖的,有两个原因:
- 没有任何索引能够覆盖这个查询。因为查询的结果字段是包括了所有的列,没有任何一个索引是可以覆盖所有的列的。当然,理论上MySQL还是有一个捷径可以利用,就是where语句中的条件是有索引覆盖的。
- MySQL不能在索引中执行like操作。这是底层存储引擎API的限制。MySQL5.5和更早的版本只支持在索引中做简单比较操作(例如等于,不等于,大于)。mysql能在索引中做最左前缀匹配的like比较,因为该操作可以转换为简单的比较操作,但是如果是通配符开头的like查询,存储引擎就无法做比较匹配。
也有办法是可以解决我上面说的这两个问题的,那就是需要重写查询并巧妙地设计索引。首先将索引扩展到覆盖是三个数据列(actor,title,prod_id),然后按照如下的方式重写查询。
select *
from products
join (
select prod_id
from products
where actor = 'SEAM CARREY' AND title like '%APOLLO%';
) AS t1 ON (t1.prod_id=products.prod_id);
我们把这种方式叫做延迟关联,因为延迟了对列的访问。在查询的第一阶段MySQL可以使用覆盖索引,在from子句的子查询中找到匹配的prod_id,然后根据这些prod_id 值在外层查询匹配获取需要的所有列值,虽然无法使用索引覆盖整个查询,但总算比完全无法利用索引覆盖的好。而如果查询第一阶段在非索引覆盖的场景下,延时关联失效,上下两种SQL的执行速度没有多少区别。
延时关联,即通过使用覆盖索引查询返回需要的主键,再根据主键关联原表获得需要的数据,尤其在大分页查询的场景下,可以提高查询效率。