MySQL6-Query優化

MySQL Query Optimizer

MySQL中有專門負責優化SELECT語句的優化器模塊,叫做MySQL Optimizer,其主要的功能就是通過計算分析系統中收集的各種統計信息,爲客戶端請求的Query 給出他認爲最優的執行計劃,也就是他認爲最優的數據檢索方式。

當MySQL Optimizer接收到從Query Parser(解析器)送過來的Query之後,會根據MySQL Query語句的相應語法對該Query進行分解分析的同時,還會做很多其他的計算轉化工作。如常量轉化,無效內容刪除,常量計算等等。所有這些工作都只爲了Optimizer工作的唯一目的,分析出最優的數據檢索方式,也就是我們常說的執行計劃

Query語句優化基本思路和原則

  1. 優化更需要優化的Query;
  2. 定位優化對象的性能瓶頸;
  3. 明確的優化目標;
  4. 從Explain 入手;
  5. 多使用profile
  6. 永遠用小結果集驅動大的結果集;
  7. 儘可能在索引中完成排序;
  8. 只取出自己需要的Columns;
  9. 僅僅使用最有效的過濾條件;
  10. 儘可能避免複雜的Join 和子查詢;

充分利用Explain

ID

Query Optimizer所選定的執行計劃中查詢的序列號。

Select_type

所使用的查詢類型,主要有以下這幾種查詢類型:

①DEPENDENT SUBQUERY:子查詢中內層的第一個SELECT,依賴於外部查詢的結果集;

②DEPENDENT UNION:子查詢中的UNION,且爲UNION 中從第二個SELECT 開始的後面所有SELECT,同樣依賴於外部查詢的結果集;

③PRIMARY:子查詢中的最外層查詢,注意並不是主鍵查詢;

④SIMPLE:除子查詢或者UNION之外的其他查詢;

⑤SUBQUERY:子查詢內層查詢的第一個SELECT,結果不依賴於外部查詢結果集;

⑥UNCACHEABLE SUBQUERY:結果集無法緩存的子查詢;

⑦UNION:UNION語句中第二個SELECT開始的後面所有SELECT,第一個SELECT 爲PRIMARY。

⑧UNION RESULT:UNION 中的合併結果;

Table

顯示這一步所訪問的數據庫中的表的名稱;

Type

告訴我們對錶所使用的訪問方式,主要包含如下集中類型;

  1. all:全表掃描
  2. const:讀常量,且最多隻會有一條記錄匹配,由於是常量,所以實際上只需要讀一次;
  3. eq_ref:最多隻會有一條匹配結果,一般是通過主鍵或者唯一鍵索引來訪問;
  4. fulltext:
  5. index:全索引掃描;
  6. index_merge:查詢中同時使用兩個(或更多)索引,然後對索引結果進行merge 之後再讀取表數據;
  7. index_subquery:子查詢中的返回結果字段組合是一個索引(或索引組合),但不是一個主鍵或者唯一索引;
  8. rang:索引範圍掃描;
  9. ref:Join 語句中被驅動表索引引用查詢;
  10. ref_or_null:與ref 的唯一區別就是在使用索引引用查詢之外再增加一個空值的查詢;
  11. system:系統表,表中只有一行數據;
  12. unique_subquery:子查詢中的返回結果字段組合是主鍵或者唯一約束;

Possible_keys

該查詢可以利用的索引. 如果沒有任何索引可以使用,就會顯示成null,這一項內容對於優化時候索引的調整非常重要;

Key

MySQL Query Optimizer 從possible_keys 中所選擇使用的索引;

Key_len

被選中使用索引的索引鍵長度;

Ref

列出是通過常量(const),還是某個表的某個字段(如果是join)來過濾(通過key)的;

Rows

MySQL Query Optimizer 通過系統收集到的統計信息估算出來的結果集記錄條數;

Extra

查詢中每一步實現的額外細節信息,主要可能會是以下內容:

  1. Distinct:查找distinct 值,所以當mysql 找到了第一條匹配的結果後,將停止該值的查詢而轉爲後面其他值的查詢;
  2. Full scan on NULL key:子查詢中的一種優化方式,主要在遇到無法通過索引訪問null值的使用使用;
  3. Impossible WHERE noticed after reading const tables:MySQL Query Optimizer 通過收集到的統計信息判斷出不可能存在結果;
  4. No tables:Query 語句中使用FROM DUAL 或者不包含任何FROM 子句;
  5. Not exists:在某些左連接中MySQL Query Optimizer 所通過改變原有Query 的組成而使用的優化方法,可以部分減少數據訪問次數;
  6. Range checked for each record (index map: N):通過MySQL 官方手冊的描述,當MySQL Query Optimizer 沒有發現好的可以使用的索引的時候,如果發現如果來自前面的表的列值已知,可能部分索引可以使用。對前面的表的每個行組合,MySQL 檢查是否可以使用range 或index_merge 訪問方法來索取行。
  7. Select tables optimized away:當我們使用某些聚合函數來訪問存在索引的某個字段的時候,MySQL Query Optimizer 會通過索引而直接一次定位到所需的數據行完成整個查詢。當然,前提是在Query 中不能有GROUP BY 操作。如使用MIN()或者MAX()的時候;
  8. Using filesort:當我們的Query 中包含ORDER BY 操作,而且無法利用索引完成排序操作的時候,MySQL Query Optimizer 不得不選擇相應的排序算法來實現。
  9. Using index:所需要的數據只需要在Index 即可全部獲得而不需要再到表中取數據;
  10. Using index for group-by:數據訪問和Using index 一樣,所需數據只需要讀取索引即可,而當Query 中使用了GROUP BY 或者DISTINCT 子句的時候,如果分組字段也在索引中,Extra 中的信息就會是Using index for group-by;
  11. Using temporary:當MySQL 在某些操作中必須使用臨時表的時候,在Extra 信息中就會出現Using temporary 。主要常見於GROUP BY 和ORDER BY 等操作中。
  12. Using where:如果我們不是讀取表的所有數據,或者不是僅僅通過索引就可以獲取所有需要的數據,則會出現Using where 信息;
  13. Using where with pushed condition:這是一個僅僅在NDBCluster 存儲引擎中才會出現的信息,而且還需要通過打開Condition Pushdown 優化功能纔可能會被使用。控制參數爲engine_condition_pushdown。

充分利用Profiling

要想優化一條Query,我們就需要清楚的知道這條Query 的性能瓶頸到底在哪裏,是消耗的CPU計算太多,還是需要的的IO 操作太多?要想能夠清楚的瞭解這些信息,在MySQL 5.0 和MySQL 5.1正式版中已經可以非常容易做到了,那就是通過Query Profiler 功能。

MySQL 的Query Profiler 是一個使用非常方便的Query 診斷分析工具,通過該工具可以獲取一條Query 在整個執行過程中多種資源的消耗情況,如CPU,IO,IPC,SWAP 等,以及發生的PAGE FAULTS,CONTEXT SWITCHE 等等,同時還能得到該Query 執行過程中MySQL 所調用的各個函數在源文件中的位置。

①通過執行“set profiling”命令,可以開啓關閉Query Profiler 功能。

②執行Query。

③show profiles獲取系統中保存的所有Query 的profile 概要信息

④在獲取到概要信息之後,我們就可以根據概要信息中的Query_ID 來獲取某個Query 在執行過程中詳細的profile 信息

Join的實現原理及優化思路

在MySQL中,只有一種Join 算法,就是大名鼎鼎的Nested Loop Join,他沒有其他很多數據庫所提供的Hash Join,也沒有Sort Merge Join。顧名思義,Nested Loop Join 實際上就是通過驅動表的結果集作爲循環基礎數據,然後一條一條的通過該結果集中的數據作爲過濾條件到下一個表中查詢數據,然後合併結果。如果還有第三個參與Join,則再通過前兩個表的Join 結果集作爲循環基礎數據,再一次通過循環查詢條件到第三個表中查詢數據,如此往復。

儘可能減少Join 語句中的Nested Loop 的循環總次數

如何減少Nested Loop 的循環總次數?最有效的辦法只有一個,那就是讓驅動表的結果集儘可能的小,這也正是優化基本原則之一“永遠用小結果集驅動大的結果集”。

優先優化Nested Loop 的內層循環

不僅僅是在數據庫的Join 中應該做的,實際上在我們優化程序語言的時候也有類似的優化原則。內層循環是循環中執行次數最多的,每次循環節約很小的資源,在整個循環中就能節約很大的資源。

保證Join 語句中被驅動表上Join 條件字段已經被索引

保證被驅動表上Join 條件字段已經被索引的目的,正是針對上面兩點的考慮,只有讓被驅動表的Join 條件字段被索引了,才能保證循環中每次查詢都能夠消耗較少的資源,這也正是優化內層循環的實際優化方法。

當無法保證被驅動表的Join 條件字段被索引且內存資源充足的前提下,不要太吝惜JoinBuffer 的設置

當在某些特殊的環境中,我們的Join 必須是All,Index,range 或者是index_merge 類型的時候,Join Buffer 就會派上用場了。在這種情況下,Join Buffer 的大小將對整個Join 語句的消耗起到非常關鍵的作用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章