1. 程式人生 > >Mysql Block Nested-Loop 與 ICP

Mysql Block Nested-Loop 與 ICP

之前轉載了 inside君 的 mysql join 原理與調優。發現 Block Nested-Loop 和 mrr 的優化重點落在了回表查詢。回顧一下,Block Nested-Loop 主要是在外表和內表的中間加了一個buffer,buffer快取外表的資料,可以減少內表的io。當查詢條件包含輔助索引,查詢結果包含查詢條件之外的欄位時,Block Nested-Loop 同樣需要回表查詢,而輔助索引不是順序的,所以回表查詢過程會包含大量的隨機io,會嚴重影響查詢效率。而針對這個問題,mysql 5.6推出了mrr新特性:

從上圖可以發現mrr通過一個額外的記憶體來對rowid進行排序,然後再順序地,批量地訪問表。這個進行rowid排序的記憶體大小由引數read_rnd_buffer_size控制,預設256K。

 

要開啟mrr還有一個比較重的引數是在變數optimizer_switch中的mrr和mrr_cost_based選項。mrr選項預設為on,mrr_cost_based選項預設為off。mrr_cost_based選項表示通過基於成本的演算法來確定是否需要開啟mrr特性。然而,在MySQL當前版本中,基於成本的演算法過於保守,導致大部分情況下優化器都不會選擇mrr特性。為了確保優化器使用mrr特性,請執行下面的SQL語句: mysql>set optimizer_switch='mrr=on,mrr_cost_based=off';
請看如下例子 mysql中按序執行如下命令 set @@optimizer_switch = "index_condition_pushdown=off"

explain select * from t_user ,t_user_ref where t_user.id = t_user_ref.userId AND t_user.age > 22

set @@optimizer_switch = "index_condition_pushdown=on"

結果如下

首先,結合之前的文章,
mysql會自動選取資料少的為驅動表,顯然上面查詢中 t_user_ref 為1 ,可以得出 t_user_ref 為被驅動表,t_user 為驅動表,也就是外表。
外表驅動表type 為All,說明驅動表沒有走索引,而是全表掃(age上有索引)。可能這裡存在一個問題,就是age欄位有索引,為什麼執行計劃顯示索引實效呢? 這裡是由於查詢的數量是大表的大部分,應該是30%以上時,索引實效。嘗試執行 explain select * from t_user ,t_user_ref wheret_user.id = t_user_ref.userId AND t_user.age > 30 結果如下:
如圖,驗證  查詢的數量是大表的大部分,應該是30%以上時,索引實效。 附 索引實效情況總結:http://blog.sina.com.cn/s/blog_6e322ce7010101i7.html  http://www.jianshu.com/p/932bcdf2c89f
根據執行引擎的extra欄位 using index condition 是指前面說的回表查詢。那麼這個extra欄位究竟代表什麼意思呢? extra 具體資訊 參見 https://my.oschina.net/zimingforever/blog/60233 這裡主要區分一下Using index ,using where ,Using index condition 。  首先Using where 效能是最差的,表示優化器需要通過索引回表查詢資料,也就是上文說的需要隨機io的情況。 using index 是效能較好的,表示直接訪問索引就足夠獲取到所需要的資料,不需要通過索引回表。  那麼 using index condition 是什麼意思呢? Using index condition 是在5.6版本後加入的新特性(Index Condition Pushdown);簡稱ICP。 顧名思義,他是在using where 基礎上做了一些優化,參照mysq  官網文件:https://dev.mysql.com/doc/refman/5.6/en/index-condition-pushdown-optimization.html Index Condition Pushdown (ICP) is an optimization for the case where MySQL retrieves rows from a table using an index. Without ICP, the storage engine traverses the index to locate rows in the base table and returns them to the MySQL server which evaluates the WHERE condition for the rows. With ICP enabled, and if parts of the WHERE condition can be evaluated by using only columns from the index, the MySQL server pushes this part of the WHERE condition down to the storage engine. The storage engine then evaluates the pushed index condition by using the index entry and only if this is satisfied is the row read from the table. ICP can reduce the number of times the storage engine must access the base table and the number of times the MySQL server must access the storage engine.
那麼簡單翻譯就是說: 首先 mysql server 和 storage engine 是兩個元件, server 負責 sql的parse, 執行; storage engine 去真正的 做 資料/index的 讀取/寫入. 以前是這樣: server 命令 storage engine 按 index 把相應的 資料 從 資料表讀出, 傳給server, server來按 where條件 做選擇; 現在 ICP則是在 可能的情況下, 讓storage engine 根據index 做判斷, 如果不符合 條件 則無須 讀 資料表. 這樣 節省了disk IO.
因此ICP會在回表查詢的時候節省資料庫IO 提高效能。如圖:

Using index condition 會先條件過濾索引,過濾完索引後找到所有符合索引條件的資料行,隨後用 WHERE 子句中的其他條件去過濾這些資料行


未完待續...