Mysql 当索引中有一些前导通配符时,当使用;覆盖指数;?

Mysql 当索引中有一些前导通配符时,当使用;覆盖指数;?,mysql,covering-index,Mysql,Covering Index,这是“高性能MySQL 3rd”的一个示例 书中说MySQL不能执行如下操作 MySQL无法在索引中执行类似的操作。这是一个 MySQL 5.5和 早期只允许简单的比较(如平等、不平等、, 在索引操作中大于)。MySQL可以执行前缀匹配 喜欢索引中的模式,因为它可以将它们转换为简单模式 比较,但查询中的前导通配符使其不可能 用于存储引擎评估匹配。因此,MySQL服务器 它本身必须获取并匹配行的值,而不是 索引的值 之后,这本书给出了“延迟加入”的改进 mysql> EXPLAIN SELE

这是“高性能MySQL 3rd”的一个示例

书中说MySQL不能执行如下操作

MySQL无法在索引中执行类似的操作。这是一个 MySQL 5.5和 早期只允许简单的比较(如平等、不平等、, 在索引操作中大于)。MySQL可以执行前缀匹配 喜欢索引中的模式,因为它可以将它们转换为简单模式 比较,但查询中的前导通配符使其不可能 用于存储引擎评估匹配。因此,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也不能在索引中执行类似的操作


我真糊涂

这是一个围绕MySQL如何工作的技术限制而进行的优化,较少涉及逻辑。尤其是您不能使用索引直接查找前导通配符的匹配项的理解是正确的

主要的问题是,MySQL 5.5中的覆盖索引在技术上并没有完全做到您认为它可以做到的事情

要正确阅读书中引用的语句,您必须知道MySQL服务器和底层存储引擎之间存在差异。MySQL服务器接收您的查询,决定如何执行它,并返回一些行

因此,对于第一个查询,MySQL要求InnoDB提供以下数据:所有列(
select*
),使用索引查找
actor='SEAN CARREY'
。虽然这很好,而且您假设覆盖索引可以做到这一点,但不幸的是,它也不能直接消除基于
标题的行,比如“%APOLLO%”
,因为

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

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

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

在第二个查询中,MySQL服务器只需要存储引擎提供
prod\u id
(根据请求)和
title
(进行
where
比较)。这实际上已经包含在索引中了!尽管上层仍然需要对标题(如“%APOLLO%”)进行求值,但存储引擎现在不需要读取实际的表数据来满足子查询请求

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

你认为这不是覆盖指数应该做的吗?你是对的。MySQL 5.6学习了如何做到这一点:

索引条件下推(ICP)是MySQL使用索引从表中检索行的一种优化。如果没有ICP,存储引擎将遍历索引以定位基表中的行,并将它们返回给MySQL服务器,该服务器将评估行的WHERE条件。启用ICP后,如果WHERE条件的一部分可以仅使用索引中的列进行评估,则MySQL服务器会将WHERE条件的这一部分向下推送到存储引擎

[……]

MySQL可以使用索引通过
zipcode='95054'
扫描用户。第二部分(
lastname,如“%etrunia%”
)不能用于限制必须扫描的行数,因此如果没有索引条件下推,此查询必须为所有具有
zipcode='95054'
的用户检索完整的表行

按下索引条件,MySQL会在读取完整表行之前检查
lastname(如“%etrunia%”)
部分。
这避免了读取与zipcode条件匹配但与lastname条件不匹配的索引元组对应的完整行

因为它只需要解决技术问题,所以这里不再需要延迟连接(尽管您不应该忘记它,它在其他情况下可能很有用)。您的第一个查询现在应该包括

  • 使用索引条件
    (JSON属性:使用索引条件)
通过访问索引元组并首先测试它们来读取表,以确定是否读取完整的表行。通过这种方式,索引信息用于延迟(“下推”)读取完整的表行,除非有必要。参见第8.2.1.5节“索引条件下推优化”


你的问题是什么?@GordonLinoff我不知道为什么MySQL可以像第二条语句中的索引那样运行。我认为它们是一样的,都需要获取行,因为有一个前导通配符。是的,索引是一个排序的元素列表,这些元素会破坏索引。这就像在电话簿中搜索“%brook%”,没有有效的方法。然而,如果你想要“布鲁克%”,你可以从“布鲁克”页面开始向前扫描。
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);