儘管缺少列,但仍使用覆蓋索引
我有以下查詢,使用 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 索引可以以幾乎相同的性能向前和向後讀取。