尽管缺少列,但仍使用覆盖索引

Tom*_*Tom 8 innodb mariadb index covering-index

我有以下查询,使用 MariaDB 10 / InnoDB:

SELECT id, sender_id, receiver_id, thread_id, date_created, content 
FROM user_message 
WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20
Run Code Online (Sandbox Code Playgroud)

此查询根据给定的条件获取消息并按创建日期排序。

我有一个覆盖索引(thread_id, date_created)

运行 EXPLAIN 时,使用了正确的索引并且我得到了输出“Using where”,尽管查询使用的是不在索引中的语句中间的列。我可以为“placeholder = x”使用任何值,结果是一样的。

如果我将排序更改为使用另一列,则 EXPLAIN 会正确指示“使用位置。使用文件排序”。

我有一个令人头疼的时刻。有人可以解释一下吗?我希望看到的是需要额外的文件排序,因为由于额外的列而无法完全使用覆盖索引。

ype*_*eᵀᴹ 8

案例 A
查询:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY some_column DESC 
LIMIT 20
Run Code Online (Sandbox Code Playgroud)

指数:

(thread_id, date_created)
Run Code Online (Sandbox Code Playgroud)

计划:

Index is used
Using Where
Using filesort
Run Code Online (Sandbox Code Playgroud)

没问题吧?如果使用索引(部分匹配WHERE条件),我们仍然需要一个排序操作来对结果进行排序some_column(不在索引中)。我们还需要额外的检查(Using Where)来只保留符合第二个条件的行。好的。


案例B(问题)
查询:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20
Run Code Online (Sandbox Code Playgroud)

指数:

(thread_id, date_created)
Run Code Online (Sandbox Code Playgroud)

计划:

Index is used
Using Where
-- no "Using filesort"
Run Code Online (Sandbox Code Playgroud)

那么,为什么这里不需要排序呢?因为索引足以按查询需要进行排序。当然还有AND placeholder = FALSE索引未涵盖的额外条件 ( )的附加问题。

好的,但我们真的不需要在这里排序。索引可以为我们提供与第一个条件 ( WHERE thread_id = 12345)匹配的结果,并按所需的输出顺序排列。我们需要的唯一额外检查 - 以及计划所做的 - 是按照索引提供的顺序从表中获取行,并检查第二个条件,直到我们获得 20 个匹配项。这就是 **Using Where"" 的意思。

我们可能会在前 20 行(非常好且快速)或前 100 行(仍然可能足够快)或前 1000000 行(可能非常非常慢)中获得 20 个匹配项,或者我们可能仅从即使在从索引中读取所有匹配的行之后,表也是如此(在大表上真的很慢)。这一切都取决于数据的分布。


案例 C(甚至更好的计划)
查询:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20
Run Code Online (Sandbox Code Playgroud)

指数:

(placeholder, thread_id, date_created)
Run Code Online (Sandbox Code Playgroud)

计划:

Index is used
-- no "Using Where"
-- no "Using filesort"
Run Code Online (Sandbox Code Playgroud)

现在我们的索引同时匹配条件和排序依据。该计划非常简单:从索引中获取前* 20 个匹配项并从表中读取相应的行。不需要额外的检查(没有“使用位置”),也不需要排序(没有“使用文件排序”)。

first*:从末尾向后读取索引时的前 20 个(如我们所见ORDER BY .. DESC),但这不是问题。B-tree 索引可以以几乎相同的性能向前和向后读取。


Ric*_*mes 7

  • 使用索引表示“覆盖索引”——任何位置的所有在一个索引SELECT中的任何位置。因此,您没有“覆盖”索引。并且为您的查询创建覆盖索引是不切实际的(提到的列太多)。
  • 使用 where - 主要是噪音。
  • 使用文件排序——查询需要排序,但它可能在 RAM 或临时表中。并且可能有多种类型(例如,GROUP BY x ORDER BY b
  • 其中任何一个都可以只查看 20 行;任何其他索引都需要接触更多行,可能是整个表:

    INDEX(thread_id, placeholder, date_created)
    INDEX(placeholder, thread_id, date_created)
    
    Run Code Online (Sandbox Code Playgroud)
  • 不,在对索引中的列进行排序时,复合索引组件的基数无关紧要。

我的食谱解释了如何在给定SELECT.