Index

儘管缺少列,但仍使用覆蓋索引

  • June 18, 2019

我有以下查詢,使用 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

此查詢根據給定條件獲取消息並按創建日期排序。

我有一個覆蓋指數超過(thread_id, date_created).

執行 EXPLAIN 時,使用了正確的索引並且我得到了輸出“Using where”,儘管查詢正在使用不在索引中的語句中間的列。我可以為“placeholder = x”使用任何值,結果是一樣的。

如果我將排序更改為使用另一列,則 EXPLAIN 正確指示“使用位置。使用文件排序”。

我有一個令人頭疼的時刻。任何人都可以闡明這一點嗎?我希望看到的是需要額外的文件排序,因為覆蓋索引由於額外的列而無法完全使用。

案例A

查詢:

WHERE thread_id = 12345 
 AND placeholder = FALSE
ORDER BY some_column DESC 
LIMIT 20

指數:

(thread_id, date_created)

計劃:

Index is used
Using Where
Using filesort

那裡沒有問題,對吧?如果使用索引(部分匹配WHERE條件),我們仍然需要一個排序操作來對結果進行排序some_column(它不在索引中)。我們還需要一個額外的檢查(使用 Where)來只保留與第二個條件匹配的行。好的。


案例B(問題)

查詢:

WHERE thread_id = 12345 
 AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

指數:

(thread_id, date_created)

計劃:

Index is used
Using Where
-- no "Using filesort"

那麼,為什麼這裡不需要排序呢?因為索引足以按照查詢的需要進行排序。當然還有一個額外的問題,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

指數:

(placeholder, thread_id, date_created)

計劃:

Index is used
-- no "Using Where"
-- no "Using filesort"

現在我們的索引匹配條件和排序依據。該計劃非常簡單:從索引中獲取前* 20 個匹配項並從表中讀取相應的行。不需要額外的檢查(沒有“Using Where”)和排序(沒有“Using filesort”)。

first*:從末尾向後讀取索引時的前 20 個(正如我們所擁有ORDER BY .. DESC的那樣),但這不是問題。B-tree 索引可以以幾乎相同的性能向前和向後讀取。

引用自:https://dba.stackexchange.com/questions/240742