mysql有兩種方式可以生成有序的結果,通過排序操作或者按照索引順序掃描,如果explain的type列的值為index,則說明mysql使用了索引掃描來做排序(不要和extra列的Using index搞混了,那個是使用了覆蓋索引查詢)。掃描索引本身是很快的,因為只需要從一條索引記錄移動到緊接着的下一條記錄,但如果索引不能覆蓋查詢所需的全部列,那就不得不掃描一條索引記錄就回表查詢一次對應的整行,這基本上都是隨機IO,因此按索引順序讀取數據的速度通常要比順序地全表掃描慢,尤其是在IO密集型的工作負載時。
mysql可以使用同一個索引既滿足排序,又用於查找行,因此,如果可能,設計索引時應該盡可能地同時滿足這兩種任務,這樣是最好的。只有當索引的列順序和order by子句的順序完全一致,並且所有列的排序方向(倒序或升序,創建索引時可以指定ASC或DESC)都一樣時,mysql才能使用索引來對結果做排序,如果查詢需要關聯多張表,則只有當order by子句引用的字段全部為第一個表時,才能使用索引做排序,order by子句和查找型查詢的限制是一樣的,需要滿足索引的最左前綴的要求,否則mysql都需要執行排序操作,而無法使用索引排序。
示例:
有表rental,表結構如下:

mysql可以使用rental_date索引為下面的SQL查詢做排序,從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 ***************************
id: 1
select_type: SIMPLE
table: rental
type: ref
possible_keys: rental_date
key: rental_date
key_len: 5
ref: const
rows: 1
Extra: Using where
1 row in set (0.01 sec)
注意:where條件列可以不按照索引定義的順序出現,不管按照什么順序出現索引列,只要出現的索引列在索引定義順序的列上能連起來就行,但是order by列不同,出現順序一定得按照索引定義的順序,否則無法使用索引進行排序,如,把inventory_id和costomer_id交換一下,就會出現filesort,因為索引是按照定義時的順序排序,order by列打亂這個排序順序就無法使用索引進行排序了:
mysql > explain select rental_id,staff_id from sakila.rental where rental_date = '2005-05-25' order by customer_id,inventory_id\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: rental
type: ref
possible_keys: rental_date
key: rental_date
key_len: 5
ref: const
rows: 1
Extra: Using where; Using filesort
1 row in set (0.00 sec)
從上面示例中可以看到,order by子句不滿足索引的最左前綴要求(rental_date列沒有出現,只出現了索引的后邊兩列),不能用rental_date來排序,但可以用於rental_date列查詢,這是因為索引的第一個列被指定為常數。
還有一些可以使用索引排序的查詢示例:
下面這個索引第一列在where上,第二列在order by上:
mysql > explain select rental_id,staff_id from sakila.rental where rental_date='2005-05-05' order by inventory_id desc\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: rental
type: ref
possible_keys: rental_date
key: rental_date
key_len: 5
ref: const
rows: 1
Extra: Using where
1 row in set (0.00 sec)
下面這個查詢因為order by使用的兩列就是索引的最左前綴:
mysql > explain select rental_id,staff_id from sakila.rental where rental_date='2005-05-05' order by rental_date,inventory_id\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: rental
type: ref
possible_keys: rental_date
key: rental_date
key_len: 5
ref: const
rows: 1
Extra: Using where
1 row in set (0.00 sec)
下面是一些不能使用索引做排序的查詢:
下面這個查詢使用了兩種不同的排序方向,但是索引列最左前綴是符合的:
mysql > explain select rental_id,staff_id from sakila.rental where rental_date='2005-05--25' rder by inventory_id desc,customer_id asc\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: rental
type: ref
possible_keys: rental_date
key: rental_date
key_len: 5
ref: const
rows: 1
Extra: Using where; Using filesort
1 row in set (0.00 sec)
下面這個查詢的order by子句中使用了一個不在索引中的列:
mysql > explain select rental_id,staff_id from sakila.rental where rental_date='2005-05--25' order by inventory_id,staff_id\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: rental
type: ref
possible_keys: rental_date
key: rental_date
key_len: 5
ref: const
rows: 1
Extra: Using where; Using filesort
1 row in set (0.00 sec)
下面這個查詢的where和order by中的列無法組合成索引的最左前綴,中間缺失了inventory_id列:
mysql > explain select rental_id,staff_id from sakila.rental where rental_date='2005-05-25' order by customer_id\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: rental
type: ref
possible_keys: rental_date
key: rental_date
key_len: 5
ref: const
rows: 1
Extra: Using where; Using filesort
1 row in set (0.00 sec)
下面這個查詢在索引列的第一列上是范圍查詢,所以mysql無法使用索引的其余列,這個范圍條件后邊的無論是查詢條件列還是排序列都無法使用到索引:
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 ***************************
id: 1
select_type: SIMPLE
table: rental
type: ALL
possible_keys: rental_date
key: NULL
key_len: NULL
ref: NULL
rows: 16005
Extra: Using where; Using filesort
1 row in set (0.00 sec)
下面這個查詢在inventory_id列上有多個等於條件,對於排序來說,這也是一種范圍查詢,inventory_id條件列后面的無論是查詢還是排序都無法使用索引:
mysql > explain select rental_id,staff_id from sakila.rental where rental_date=2005-05-25 and inventory_id in (1,2) order by customer_id\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: rental
type: ref
possible_keys: rental_date,idx_fk_inventory_id
key: rental_date
key_len: 5
ref: const
rows: 1
Extra: Using index condition; Using where; Using filesort
1 row in set, 5 warnings (0.00 sec)
下面這個查詢理論上是可以使用索引進行關聯排序的,但是由於優化器在優化時將film_actor表當作關聯的第二個表,所以實際上無法使用索引,即排序列不是驅動表的列就無法使用索引排序:
mysql > explain select actor_id,title from sakila.film_actor join sakila.film using(film_id) order by actor_id\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: film
type: index
possible_keys: PRIMARY
key: idx_title
key_len: 767
ref: NULL
rows: 1000
Extra: Using index; Using temporary; Using filesort
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: film_actor
type: ref
possible_keys: idx_fk_film_id
key: idx_fk_film_id
key_len: 2
ref: sakila.film.film_id
rows: 2
Extra: Using index
2 rows in set (0.00 sec)
可以使用straight_join語句指定關聯表的順序:
mysql > explain select actor_id,title from sakila.film_actor as a straight_join sakila.film as b on a.film_id=b.film_id order by actor_id\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: a
type: index
possible_keys: idx_fk_film_id
key: PRIMARY
key_len: 4
ref: NULL
rows: 5462
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: b
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 2
ref: sakila.a.film_id
rows: 1
Extra: NULL
2 rows in set (0.00 sec)
要注意:straight_join屬於非標准的語法,在mysql優化器能做出正確選擇的時候就盡量不要使用,只有在mysql優化器做出錯誤的選擇時才使用它,straight_join是一種hit提示關鍵字,使用straight_join關鍵字時,如果只有兩個表關聯要就只使用straight_join就可以了,不需要再去指定join,left join,right join等關鍵字,否則會報1066表或表別名重復的錯誤,另外,使用straight_join后指定關聯表的關聯字段時發現使用using(xx)報語法錯,改使用on a.xx=b.xx形式指定就不報錯,不知道是不是使用straight_join關鍵字時不支持using指定關聯字段
關於straight_join提示:
這個提示可放置在select關鍵字之后,也可以放置在任何兩個關聯表的表名之前,第一個用法是讓查詢中所有的表按照在語句中出現的順序進行關聯,第二個用法則是固定其前后兩個表的關聯順序。當mysql沒正確選擇關聯順序的時候,或者由於可能的順序太多導致mysql無法評估所有的關聯順序的時候,straight_join都會很有用,如果關聯表可能的順序太多,可能導致mysql花費大量時間在statistics狀態。可以使用explain語句來查看關聯順序,然后加上這個提示再用explain查看有沒有變化。