这是“高性能MySQL 3rd”的示例。
mysql> EXPLAIN SELECT * FROM products WHERE actor='SEAN CARREY' AND title like '%APOLLO%';
这本书说MySQL无法执行以下LIKE。
MySQL无法在索引中执行LIKE操作。这是一个 低级存储引擎API的限制,在MySQL 5.5和更高版本中 较早的版本仅允许进行简单的比较(例如相等,不平等, 以及大于)。 MySQL可以执行前缀匹配 索引中的LIKE模式,因为它可以将它们转换为简单模式 比较,但是查询中的前导通配符使它不可能 供存储引擎评估匹配结果。因此,MySQL服务器 本身必须获取并匹配该行的值,而不是 索引的值。
此后,该书进行了“延迟加入”改进。
mysql> EXPLAIN SELECT * FROM products
-> JOIN (
-> SELECT prod_id FROM products WHERE actor='SEAN CARREY' AND title LIKE '%APOLLO%'
-> ) AS t1 ON (t1.prod_id=products.prod_id);
即使(actor,title,prod_id)是一个“覆盖索引”,MySQL也无法在该索引中执行LIKE。
我很困惑!
答案 0 :(得分:0)
这是一项优化,它围绕有关MySQL工作方式的技术限制而不是逻辑方面的限制进行工作。特别是,您不能使用索引直接查找前导通配符的匹配的理解是正确的。
主要问题是,MySQL 5.5中的覆盖索引在技术上并没有完全按照您认为的做(并且可以做)。
要正确阅读本书中引用的语句,您必须知道 MySQL服务器与基础的存储引擎之间存在差异。 MySQL服务器接受您的查询,决定如何执行它,sends a request to the (InnoDB) storage engine via an api,并返回一些行。
因此,对于您的第一个查询,MySQL要求InnoDB提供以下数据:所有列(select *
),使用索引查找actor='SEAN CARREY'
。尽管这很好,并且您假设覆盖索引可以做到这一点,但不幸的是,它也不能直接消除基于title like '%APOLLO%'
的行,因为
这是低级存储引擎API的局限性,在MySQL 5.5及更早版本中,它仅允许索引操作中的简单比较(例如相等,不相等和大于)。
自从您请求*
以来,它会从InnoDB引擎中检索具有正确的actor(使用索引)的所有行的所有列,这需要查询表数据,然后再过滤掉这些列,因为
MySQL服务器本身必须获取并匹配行的值,而不是索引的值。
在第二个查询中,MySQL服务器仅需要来自存储引擎的prod_id
(根据请求)和title
(进行where
比较)。 这实际上已经被索引覆盖了!尽管上层仍然需要对title like '%APOLLO%'
进行评估,但是存储引擎现在不需要读取实际的表数据即可满足请求。子查询。
MySQL服务器现在可以评估它收到的数据,并将另一个请求发送到存储引擎,以检索满足prod_id
条件的where
的所有列。在极端情况下,这可能根本无法过滤(例如,使用actor='SEAN CARREY'
的每一行也可以满足title like '%APOLLO%'
),然后,由于您要进行更多的工作,因此延迟的连接可能会变慢。
您认为这不是覆盖指数应该做什么?你是对的。 MySQL 5.6学会了如何做more properly:
索引条件下推(ICP)是MySQL使用索引从表中检索行的情况的优化。如果不使用ICP,则存储引擎将遍历索引以在基表中定位行,并将其返回给MySQL服务器,后者将评估这些行的WHERE条件。启用ICP后,如果仅可以使用索引中的列来评估WHERE条件的一部分,则MySQL服务器会将WHERE条件的这一部分向下推送到存储引擎。
[...]
MySQL可以使用索引来扫描具有
zipcode='95054'
的人员。第二部分(lastname LIKE '%etrunia%'
不能用于限制必须扫描的行数,因此,在没有“索引条件下推”的情况下,该查询必须为所有拥有zipcode='95054'
的人检索完整的表行。>通过索引条件下推,MySQL在读取完整表行之前检查
lastname LIKE '%etrunia%'
部分。这样可以避免读取与匹配邮政编码条件但不匹配姓氏条件的索引元组对应的完整行。 。
由于只需要解决技术问题,因此您不再需要此处的延迟加入(尽管您不应该忘记它,在其他情况下它也很有用)。您的第一个查询的explain output现在应该包含
Using index condition
(JSON属性:using_index_condition)通过访问索引元组并首先对其进行测试以确定是否读取完整的表行来读取表。这样,除非有必要,否则索引信息将用于延迟(“下推”)读取整个表行。请参见第8.2.1.5节“索引条件下推优化”。