-1

这是“高性能 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。

我感到很困惑!

4

1 回答 1

1

这是一项针对 MySQL 工作方式的技术限制而不是关于逻辑的优化。特别是您理解不能使用索引直接查找前导通配符的匹配项是正确的。

主要问题是 MySQL 5.5 中的覆盖索引在技术上并没有完全按照您的假设(并且可以做到)。

要正确阅读书中引用的语句,您必须知道MySQL 服务器和底层存储引擎之间存在差异。MySQL 服务器接受您的查询,决定如何执行它,通过 api 向 (InnoDB) 存储引擎发送请求,并取回一些行。

因此,对于您的第一个查询,MySQL 要求 InnoDB 为其提供以下数据:所有列 ( select *),使用索引来查找actor='SEAN CARREY'. 虽然它会很好并且您假设覆盖索引会这样做,但不幸的是,它也不能直接消除基于 的行title like '%APOLLO%',因为

这是低级存储引擎 API 的限制,在 MySQL 5.5 及更早版本中,它只允许在索引操作中进行简单的比较(例如相等、不等和大于)。

由于您要求*,它会从 InnoDB 引擎检索所有具有正确参与者(使用索引)的行的所有列,这需要查看表数据,然后过滤它们,因为

MySQL 服务器本身必须获取并匹配行的值,而不是索引的值。

在第二个查询中,MySQL 服务器只需要prod_id(根据请求)和title(进行where比较)来自存储引擎。这现在实际上被索引覆盖了!虽然上层还需要对 进行求值title like '%APOLLO%',但存储引擎现在不需要读取实际的表数据来完成子查询的请求。

MySQL 服务器现在可以评估它收到的数据并向存储引擎发送另一个请求,以检索prod_id满足 - 条件的所有列where。在极端情况下,这可能根本不会过滤(例如,每一行actor='SEAN CARREY'也可以完成title like '%APOLLO%'),然后延迟连接可能会慢一些,因为您总体上做了更多的工作。

你认为这不是覆盖索引应该做的吗?你说的对。MySQL 5.6 学会了如何更正确地做到这一点:

索引条件下推 (ICP) 是针对 MySQL 使用索引从表中检索行的情况的优化。如果没有 ICP,存储引擎会遍历索引以定位基表中的行,并将它们返回给 MySQL 服务器,由 MySQL 服务器评估行的 WHERE 条件。在启用 ICP 的情况下,如果 WHERE 条件的一部分可以通过仅使用索引中的列来评估,MySQL 服务器会将这部分 WHERE 条件下推到存储引擎。

[...]

MySQL 可以使用索引来扫描具有zipcode='95054'. 第二部分 ( lastname LIKE '%etrunia%') 不能用于限制必须扫描的行数,因此如果没有索引条件下推,此查询必须检索所有拥有zipcode='95054'.

使用索引条件下推,MySQLlastname LIKE '%etrunia%'在读取整个表行之前检查该部分。这避免了读取与匹配邮政编码条件但不匹配姓氏条件的索引元组相对应的完整行。

由于它只需要解决技术问题,因此您不再需要延迟加入(尽管您不应该忘记它,它在其他情况下可能很有用)。您的第一个查询的解释输出现在应该包括

  • Using index condition(JSON 属性:using_index_condition)

通过访问索引元组并首先对其进行测试以确定是否读取完整的表行来读取表。这样,除非有必要,否则索引信息用于延迟(“下推”)读取全表行。请参阅第 8.2.1.5 节,“索引条件下推优化”。

于 2019-05-29T08:39:30.017 回答