1. 程式人生 > >Mysql索引掃描排序

Mysql索引掃描排序

使用索引掃描來做排序

生成有序結果

Mysql有兩種操作可以用來生成有序結果:
+ 排序操作: 將查找出來的結果使用排序演算法進行排序
+ 按索引順序掃描ORDER BY語句後跟著一個被索引的列,如此一來索引的順序就是索引對應記錄的順序,這樣直接順著索引一直往下讀取記錄即可得到有序的結果。

建立用於排序掃描的索引

按照索引順序掃描的好處是不言而喻的,因為查找出來的結果就是有序結果而無需執行額外的排序操作,這樣執行的速度就會相對較快。但是,不是什麼時候按照索引掃描的執行速都會是最快的。雖然掃描索引的速度是非常快的,但是如果索引不能覆蓋到查詢所需要的所有資料列的話,這種情況下每掃描一個索引就必須相對應地回表一次,這樣的IO幾乎是隨機IO,如此一來雖然索引掃描無需執行一次排序演算法,但是隨機IO操作會大大拖慢執行速度,導致按照索引掃描的執行速度反而要比排序操作要慢。

因此,在考慮使用按照索引掃描的方式去獲得有序結果,那麼設計索引時必須要考慮索引覆蓋的情況

什麼時候Mysql會使用索引排序?

  • 索引列的順序與 ORDER BY字句的順序一致時,並且所有列的排列順序一樣。
    因為多列索引的索引值是綜合了多個列計算而得的,且計算的結果與列的順序有關。例如下面就是一個例子:

    //虛擬碼
    index_t hash(string x, string y, string z){
       return x.append(y).append(z);
    }

    當執行ORDER BY x, y, z時,實際上就是ORDER BY hash(x, y, z),這樣必須保證x, y, z

    的順序與建立索引時一致,索引順序對應的記錄順序才與排序的順序一致,這樣才可以使用索引掃描進行排序。

  • 當查詢關聯多張表時,則只有當ORDER BY中引用的欄位全為第一張表中的欄位,才可能使用索引排序。
  • 有一種例外,ORDER BY後跟的欄位可以不滿足最左字首原則:當前導量為常量的時候

幾個例子

  1. 有一張表rental如下所示:
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), ... };

執行語句:

EXPLAIN SELECT rental_id, staff_id FROM sakila.rental 
-> WHERE rental_date = '2005-05-5'
-> ORDER BY inventory_id, customer_id\G

輸出為:

possible_keys: rental_date
          key: rental_date
         rows: 1
        extra: using where

這裡看到extra: using where而不是extra: using filesort表明此次查詢並沒有排序操作,而是直接通過掃描索引獲得有序結果。因為這在這裡,雖然ORDER BY語句的欄位並沒有滿足最左字首的原則,但是由於前導列只定了一個常量,所以可以使用索引掃描進行排序。

  1. 將上述查詢改為以下語句也沒問題:
EXPLAIN SELECT rental_id, staff_id FROM sakila.rental 
-> WHERE rental_date > '2005-05-5'
-> ORDER BY rental_date, inventory_id, customer_id\G

因為這裡雖然沒有將前導列指定為常量,但是這裡ORDER BY的語句滿足了最左字首原則。

  1. 下列語句不能使用掃描索引得到有序結果:
    • ...WHERE rental_date > '2005-05-5' ORDER BY rental_date DESC, inventory_id ASC
      因為排序方向不一致。
    • ...WHERE rental_date = '2005-05-5' ORDER BY rental_date , staff_id
      因為使用了一個不在索引中的列。
    • ...WHERE rental_date > '2005-05-5' ORDER BY inventory_id
      因為不滿足最左字首索引