2014-06-08 39 views
0

我通過「高性能MySQL」書籍學習MySQL索引詳細信息,但我無法理解一件事。使用MySQL索引掃描進行排序

正如書上說的,(第124頁使用索引掃描的排序)

MySQL有兩種方法可以產生有序的結果:它可以使用文件排序, 或者也可以以掃描的索引。

僅當索引的順序爲 與ORDER BY子句完全相同,並且所有列按相同方向(升序或降序)排列在 中時,纔會按索引排序結果。

ORDER BY子句也具有與查找查詢相同的限制:它需要形成索引的最左邊的前綴。在其他所有情況下,MySQL使用文件夾 。

進一步,作者給出了使用MySQL的Sakila的例子數據庫 [http://dev.mysql.com/doc/sakila/en/][1]

第一個例子能正常工作的一些例子:

標準的Sakila示例數據庫租金錶上 (rental_date指數, inventory_id,CUSTOMER_ID):

CREATE TABLE rental (
... 
PRIMARY KEY (rental_id), 
UNIQUE KEY rental_date (rental_date,inventory_id,customer_id), 
KEY idx_fk_inventory_id (inventory_id), 
KEY idx_fk_customer_id (customer_id), 
KEY idx_fk_staff_id (staff_id), 
... 
); 

MySQL使用rental_date索引對下面的查詢,如您 可以從缺少文件排序的看到EXPLAIN:

> mysql> EXPLAIN SELECT 
> rental_id, staff_id FROM sakila.rental 
> -> WHERE rental_date = '2005-05-25' 
> -> ORDER BY inventory_id, customer_id\G 
> *************************** 1. row *************************** 
> type: ref 
> possible_keys: rental_date 
> key: rental_date 
> rows: 1 
> Extra: Using where 

這工作,即使ORDER BY子句本身不是 索引的最左邊的前綴,因爲我們爲索引中的第一列指定了相等的 條件。

重要的是要注意:它們在where子句中使用索引列,但在SELECT查詢中使用不同的列。

第二個例子示於短的方式:

以下查詢也適用,因爲在ORDER兩列BY 是最左邊的前綴索引:

.. 。WHERE rental_date>'2005-05-25'ORDER BY rental_date,inventory_id;

但在這裏你可以得到不同的結果,而你的選擇列內容:

第一種情形,文件排序時:

EXPLAIN 
SELECT `rental_id`, `staff_id` FROM `sakila`.`rental` 
WHERE `rental_date` > '2005-05-25' 
ORDER BY `rental_date`, `inventory_id`; 

類型:所有 possible_key:rental_date
鍵:NULL 額外:使用where;使用文件排序

第二種情況下,索引用於:

EXPLAIN 
SELECT `rental_id`, `rental_date`, `inventory_id` FROM `sakila`.`rental` 
WHERE `rental_date` > '2005-05-25' 
ORDER BY `rental_date`, `inventory_id`; 

類型:範圍 possible_key:rental_date 鍵:rental_date 額外:使用其中;使用索引

爲什麼它以這種奇怪的方式工作?如前所示,即使在SELECT子句中包含與WHERE子句不同的列,第一個示例也使用索引排序。

回答

0

在第二查詢:

SELECT `rental_id`, `rental_date`, `inventory_id` FROM `sakila`.`rental` 
WHERE `rental_date` > '2005-05-25' 
ORDER BY `rental_date`, `inventory_id`; 

的MySQL直接從索引中檢索數據,並且不參照該表格在所有。
請看看索引定義,並將其與查詢所引用的列比較:

UNIQUE KEY rental_date (rental_date,inventory_id,customer_id) 

該指數包含了除rental_id查詢所引用的所有colums,但是rental_id是主鍵,此外每個索引在其定義中明確給出的列總是包含主鍵值。
這是一個覆蓋指數此查詢,在這裏看到:http://en.wikipedia.org/wiki/Index_%28database%29#Covering_index


在第一個查詢

但是:

SELECT `rental_id`, `staff_id` FROM `sakila`.`rental` 
WHERE `rental_date` > '2005-05-25' 
ORDER BY `rental_date`, `inventory_id`; 

staff_id列,未存儲在索引中。
在這種情況下,MySql必須首先檢索與WHERE條件匹配的索引條目,然後對於每個條目必須從表中獲取整條記錄以獲取該條目缺少的staff_id值。


現在,請對您的數據庫運行此查詢並檢查它的結果:

select count(*) As total, 
      sum(case when `rental_date` > '2005-05-25' then 1 else 0 end) As x1, 
      sum(case when `rental_date` = '2005-05-25' then 1 else 0 end) As x0 
from rental 
; 

在我的sakila database該查詢返回複製以下:

+ ---------- + ------- + ------- + 
| total  | x1  | x0  | 
+ ---------- + ------- + ------- + 
| 16044  | 16036 | 0  | 
+ ---------- + ------- + ------- + 

正如你看到的,幾乎所有表中的記錄 - 99.9% - 都大於2005-05-25。 在這種情況下,MySql決定不使用索引從表中檢索行,但傾向於將表的全部內容加載到內存中,並在此處對其進行排序 - 表格相對較小,僅包含16k條記錄。
但是如果還原的條件時,MySQL更喜歡指數ACCES方法:

EXPLAIN 
SELECT `rental_id`, `staff_id` FROM `sakila`.`rental` 
WHERE `rental_date` < '2005-05-25' 
ORDER BY `rental_date`, `inventory_id`; 
+ ------- + ---------------- + ---------- + --------- + ------------------ + -------- + ------------ + -------- + --------- + ---------- + 
| id  | select_type  | table  | type  | possible_keys  | key  | key_len  | ref  | rows  | Extra  | 
+ ------- + ---------------- + ---------- + --------- + ------------------ + -------- + ------------ + -------- + --------- + ---------- + 
| 1  | SIMPLE   | rental  | range  | rental_date  | rental_date | 5   |   | 8   | Using index condition | 
+ ------- + ---------------- + ---------- + --------- + ------------------ + -------- + ------------ + -------- + --------- + ---------- + 

爲什麼不能在第一種情況下使用索引?因爲使用索引條目從表格中檢索記錄通常是最昂貴的方法 - 真的:)
只有在表格的很小部分必須檢索的情況下,索引纔是好的 - 百分之幾,也許是< 10%。對於從索引檢索的每個索引記錄,MySql必須使用主鍵獲取一條記錄 - 這是一個隨機表訪問,比順序訪問慢幾倍。
若要從id表中只獲取一條記錄,MySql必須獲取包含多條記錄的整個數據頁(塊)。使用排序後的索引,我們必須從表中的不同位置逐個獲取記錄,因此當我們想使用索引獲取90%的表時,多次檢索相同的數據塊。在這種情況下,按順序只讀一次並在存儲器中對它們進行排序會更容易和更便宜。