1. 程式人生 > >【mysql索引】之使用索引掃描做排序

【mysql索引】之使用索引掃描做排序

前言

今天看了《高效能MySQL》的索引掃描做排序章節,並且親身實踐了一下,發現有些結果與原書不一樣,個人猜測是MySQL版本不一樣造成的,下面分享一下我個人的筆記。

簡介

MySQL 有兩種方式生成有序結果:通過排序操作或者按索引順序掃描。

如果EXPLAIN出來type列的值為index,則說明MySQL使用索引掃描來做排序。(這句有疑問,很多情況下都type都不是index,卻可以進行索引排序)

掃描索引本身是很快的,因為只需從一條索引記錄移動到緊接著的下一條記錄。但如果索引不能覆蓋查詢所需的全部列,那就不得不每掃描一條索引記錄就都回表查詢一次對應的行。這基本上都是隨機I/O(有關IO我找到一篇描述生動的文章:

理解I/O:隨機和順序),因此按索引順序讀取的速度通常要比順序地全表掃描慢,尤其是在I/O密集型的工作負載時。

MySQL可以同時使用同一個索引既滿足查詢,又滿足排序。因此,如果可能,設計索引是應該儘可能
地同時滿足這兩個任務,這樣是最好的。

排序成立的條件

只有當索引的列順序和ORDER BY子句的順序完全一致時,並且所有列的排序方向(倒序或正序)都一樣時,
MySQL才能使用索引來對結果排序。如果需要關聯表來查詢,則只有當ORDER BY子句引用的欄位全部為第一個表時,才會使用索引做排序。
ORDER BY子句和查詢型查詢限制是一樣的,需要滿足索引的最左字首的要求,否則MySQL都需要執行排序操作,而無法利用索引排序。
不過有一種情況是ORDER BY子句可以不滿足索引的最左字首要求,就是前導列為常量的時候,如果WHERE子句或者JOIN子句中對這些列指定了常量,就可以彌補索引的不足(下面會有例子)

例子

下面的例子都會以例項資料庫sakila(MySQL安裝成功後自帶的一個示例資料庫)的rental表作為演示,其中表上有 rental_date  (rental_date,inventory_id,customer_id)這個多列索引,我們從下面一例中EXPLAIN中可以看出Extra裡並沒有出現檔案排序(filesort)操作(Using filesort(MySQL中無法利用索引完成的排序操作稱為“檔案排序”)當我們試圖對一個沒有索引的欄位進行排序時,就是filesoft。它跟檔案沒有任何關係,實際上是內部的一個快速排序。):
-- 即使ORDER BY子句不滿足最左字首要求,也可用於查詢排序,因為第一列被指定為一個常數
EXPLAIN SELECT rental_id, staff_id FROM rental
WHERE rental_date = '2005-05-25'
ORDER BY inventory_id, customer_id;

-- 第一列提供常量條件,使用第二列進行排序,這兩列組合在一起,形成索引最左字首,所以可以索引排序,沒有出現filesort
EXPLAIN SELECT rental_id, staff_id FROM rental
WHERE rental_date = '2005-05-25'
ORDER BY inventory_id DESC;

-- 原書:這個也沒問題,因為ORDER BY使用的兩列就是索引的最左字首 
-- 我個人實踐:我這裡試過是不行的,難道是MySQL的改良捨棄掉了某種演算法?
EXPLAIN SELECT rental_id, staff_id FROM rental
WHERE rental_date > '2005-05-25'
ORDER BY rental_date, inventory_id;


-- 原書:這個查詢使用了兩種不同的排序方向,不能進行索引排序 
-- 我個人實踐:我試過是可以的,可能MySQL改良了吧 
EXPLAIN SELECT rental_id, staff_id FROM rental
WHERE rental_date = '2005-05-25'
ORDER BY rental_date DESC, inventory_id ASC;


-- WHERE 和 ORDER BY 的列無法組成索引最做字首 
EXPLAIN SELECT rental_id, staff_id FROM rental
WHERE rental_date = '2005-05-25'
ORDER BY customer_id;
-- 索引列在第一列上是範圍條件,因此MySQL無法使用索引的其餘列 
EXPLAIN SELECT rental_id, staff_id FROM rental
WHERE rental_date > '2005-05-25'
ORDER BY inventory_id, customer_id;



-- inventory_id上有多個條件,對於排序萊說,這也屬於範圍查詢
EXPLAIN SELECT rental_id, staff_id FROM rental
WHERE rental_date = '2005-05-25' AND inventory_id IN(1,2)
ORDER BY customer_id;


/*
理論上是可以用索引進行關聯排序的,但由於優化器在優化時把film_actor表當作關聯的第二張表,
因此實際上無法使用索引
*/
EXPLAIN SELECT actor_id, title FROM film_actor
INNER JOIN film USING(film_id) ORDER BY actor_id;