1. 程式人生 > 資料庫 >MySQL中(JOIN/ORDER BY)語句的查詢過程及優化方法

MySQL中(JOIN/ORDER BY)語句的查詢過程及優化方法

在MySQL查詢語句過程和EXPLAIN語句基本概念及其優化中介紹了EXPLAIN語句,並舉了一個慢查詢例子:

可以看到上述的查詢需要檢查1萬多記錄,並且使用了臨時表和filesort排序,這樣的查詢在使用者數快速增長後將成為噩夢。

在優化這個語句之前,我們先了解下SQL查詢的基本執行過程:

1.應用通過MySQL API把查詢命令傳送給MySQL伺服器,然後被解析

2.檢查許可權、MySQL optimizer進行優化,經過解析和優化後的查詢命令被編譯為CPU可執行的二進位制形式的查詢計劃(query plan),並可以被快取

3.如果存在索引,那麼先掃描索引,如果資料被索引覆蓋,那麼不需要額外的查詢,如果不是,根據索引查詢和讀取對應的記錄

4.如果有關聯查詢,查詢次序是掃描第一張表找到滿足條件的記錄,按照第一張表和第二張表的關聯鍵值,掃描第二張表查詢滿足條件的記錄,按此順序迴圈

5.輸出查詢結果,並記錄binary logs

顯然合適的索引將大大簡化和加速查詢。再看一下上面那條查詢語句,除了條件查詢外,還有關聯查詢以及ORDER BY即排序操作,

那麼讓我們進一步瞭解下關聯查詢(JOIN)和ORDER BY是怎麼工作的,MySQL有三種方式來處理關聯查詢和資料排序:

第一種方法是基於索引,第二種是對第一個非常量表進行filesort(quicksort),還有一種是把聯合查詢的結果放入臨時表,然後進行filesort。

注1:關於什麼是非常量表,請參考閱讀MySQL開發手冊:Consts and Constant Tables,

注2:什麼是filesort呢,這不是字面意思的檔案排序,filesort有兩種模式:
1、模式1:排序後的元素涵蓋了要輸出的資料。排序結果是一串有序序列元素組,不再需要額外的記錄讀取;
2、模式2:排序結果是<sort_key,row_id>鍵值對序列,通過這些row_ids再去讀取記錄(隨機讀取,效率低下);
注3:關於什麼是臨時表,請參考閱讀MySQL開發手冊:How MySQL Uses Internal Temporary Tables

第一種方法用於第一個非常量表中存在ORDER BY所依賴的列的索引,那就可直接使用已經有序的索引來查詢關聯表的資料,這種方式是效能最優的,因為不需要額外的排序動作:

第二種方式用於ORDER BY所依賴的列全部屬於第一張查詢表且沒有索引,那麼我們可以先對第一張表的記錄進行filesort(模式可能是模式1也可能是模式2),得到有序行索引,然後再做關聯查詢,filesort的結果可能是在記憶體中,也可能在硬碟上,這取決於系統變數sort_buffer_size(一般為2M左右):

第三種方法用於當ORDER BY的元素不屬於第一張表時,需要把關聯查詢的結果放入臨時表,最後對臨時表進行filesort:

第三種方法中的臨時表,可能是在記憶體中(in-memory table),也可能是在硬碟上,一般是下面兩種情況會使用硬碟(on-disk table):

(1)使用了BLOB,TEXT型別的資料

(2)記憶體表佔用超過了系統變數tmp_table_size/max_heap_table_size的限定(一般為16M左右),只能放在硬碟上

從上面的查詢執行過程和方式,我們應該可以清楚的知道為什麼Using filesort,Using temporary會嚴重的影響查詢效能,因為如果資料型別或者欄位設計有問題,

在需要查詢的表以及結果中存在大資料的欄位,而沒有合適的索引可用時,都可能會導致產生大量的IO操作,這就是查詢效能緩慢的根源所在。

回到文章開頭所舉的查詢例項,它顯然是使用了效率最低的第三種方法,我們需要做和嘗試的優化手段有:

1、為users.fl_no新增索引,為select和where所使用的欄位建立索引

2、把users.fl_no轉移到或者作為冗餘欄位新增到表user_profile中

3、去除TEXT型別的欄位,TEXT可以替換為VARCHAR(65535)或對於中文而言VARCHAR(20000)

4、如果實在無法消除Using filesort,那麼提高sort_buffer_size,以減少IO操作負擔

5、儘量使用第一張表所覆蓋的索引進行排序,實在不行,可以把排序邏輯從MySQL中移到PHP/Java程式中執行

實施1、2、3的優化方法後,EXPLAIN結果如下:

備註:編寫簡單的PHP應用,用siege測試,查詢效率提高>3倍。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。