Index Condition Pushdown (ICP)是 mysql 使用索引從表中檢索行數據的一種優化方式,從mysql5.6開始支持,mysql5.6以前,存儲引擎會經過遍歷索引定位基表中的行,而後返回給Server層,再去爲這些數據行進行WHERE後的條件的過濾。mysql 5.6以後支持ICP後,若是WHERE條件可使用索引,MySQL 會把這部分過濾操做放到存儲引擎層,存儲引擎經過索引過濾,把知足的行從表中讀取出。ICP能減小引擎層訪問基表的次數和 Server層訪問存儲引擎的次數。mysql
ICP的目標是減小從基表中讀取操做的數量,從而下降IO操做算法
對於InnoDB表,ICP只適用於輔助索引sql
當使用ICP優化時,執行計劃的Extra列顯示Using indexcondition提示數據庫
數據庫配置 optimizer_switch="index_condition_pushdown=on」;bash
輔助索引INDEX (a, b, c)oop
SELECT * FROM peopleWHERE a='12345' AND b LIKE '%xx%'AND c LIKE '%yy%';
若不使用ICP:則是經過二級索引中a的值去基表取出全部a='12345'的數據,而後server層再對b LIKE '%xx%'AND c LIKE '%yy%' 進行過濾性能
若使用ICP:則b LIKE '%xx%'AND c LIKE '%yy%'的過濾操做在二級索引中完成,而後再去基表取相關數據優化
mysql 5.6中只支持 MyISAM、InnoDB、NDB cluster線程
mysql 5.6中不支持分區表的ICP,從MySQL 5.7.3開始支持分區表的ICPrest
ICP的優化策略可用於range、ref、eq_ref、ref_or_null 類型的訪問數據方法
不支持主建索引的ICP(對於Innodb的彙集索引,完整的記錄已經被讀取到Innodb Buffer,此時使用ICP並不能下降IO操做)
當 SQL 使用覆蓋索引時但只檢索部分數據時,ICP 沒法使用
ICP的加速效果取決於在存儲引擎內經過ICP篩選掉的數據的比例
MRR 的全稱是 Multi-Range Read Optimization,是優化器將隨機 IO 轉化爲順序 IO 以下降查詢過程當中 IO 開銷的一種手段,這對IO-bound類型的SQL語句性能帶來極大的提高,適用於range ref eq_ref類型的查詢
MRR優化的幾個好處
使數據訪問有隨機變爲順序,查詢輔助索引是,首先把查詢結果按照主鍵進行排序,按照主鍵的順序進行書籤查找
減小緩衝池中頁被替換的次數
批量處理對鍵值的操做
第一步 先根據where條件中的輔助索引獲取輔助索引與主鍵的集合,結果集爲rest
select key_column, pk_column from tb where key_column=x order by key_column
第二步 經過第一步獲取的主鍵來獲取對應的值
for each pk_column value in rest do: select non_key_column from tb where pk_column=val
使用MRR特性時
第一步 先根據where條件中的輔助索引獲取輔助索引與主鍵的集合,結果集爲rest
select key_column, pk_column from tb where key_column = x order by key_column
第二步 將結果集rest放在buffer裏面(read_rnd_buffer_size 大小直到buffer滿了),而後對結果集rest按照pk_column排序,獲得結果集是rest_sort
第三步 利用已經排序過的結果集,訪問表中的數據,此時是順序IO.
select non_key_column fromtb where pk_column in (rest_sort)
在不使用 MRR 時,優化器須要根據二級索引返回的記錄來進行「回表」,這個過程通常會有較多的隨機IO, 使用MRR時,SQL語句的執行過程是這樣的:
優化器將二級索引查詢到的記錄放到一塊緩衝區中
若是二級索引掃描到文件的末尾或者緩衝區已滿,則使用快速排序對緩衝區中的內容按照主鍵進行排序
用戶線程調用MRR接口取cluster index,而後根據cluster index 取行數據
當根據緩衝區中的 cluster index取完數據,則繼續調用過程 2) 3),直至掃描結束
經過上述過程,優化器將二級索引隨機的 IO 進行排序,轉化爲主鍵的有序排列,從而實現了隨機 IO 到順序 IO 的轉化,提高性能
SELECT * FROM t WHERE key_part1 >= 1000 AND key_part1 < 2000AND key_part2 = 10000;
表t上有二級索引(key_part1, key_part2),索引根據key_part1,key_part2的順序排序。
若不使用MRR:此時查詢的類型爲Range,sql優化器會先將key_part1大於1000小於2000的數據取出,即便key_part2不等於10000,帶取出以後再進行過濾,會致使不少無用的數據被取出
若使用MRR:若是索引中key_part2不爲10000的元組越多,最終MRR的效果越好。優化器會將查詢條件拆分爲(1000,1000),(1001,1000),... (1999,1000)最終會根據這些條件進行過濾
當mrr=on,mrr_cost_based=on,則表示cost base的方式還選擇啓用MRR優化,當發現優化後的代價太高時就會不使用該項優化
當mrr=on,mrr_cost_based=off,則表示老是開啓MRR優化
SET @@optimizer_switch='mrr=on,mrr_cost_based=on';
參數read_rnd_buffer_size 用來控制鍵值緩衝區的大小。二級索引掃描到文件的末尾或者緩衝區已滿,則使用快速排序對緩衝區中的內容按照主鍵進行排序
Batched Key Access (BKA) 提升表 join 性能的算法。當被join的表可以使用索引時,就先排好順序,而後再去檢索被join的表,聽起來和MRR相似,實際上MRR也能夠想象成二級索引和 primary key的join
若是被Join的表上沒有索引,則使用老版本的BNL策略(BLOCK Nested-loop)
對於多表join語句,當MySQL使用索引訪問第二個join表的時候,使用一個join buffer來收集第一個操做對象生成的相關列值。BKA構建好key後,批量傳給引擎層作索引查找。key是經過MRR接口提交給引擎的(mrr目的是較爲順序)MRR使得查詢更有效率。
大體的過程以下:
BKA使用join buffer保存由join的第一個操做產生的符合條件的數據
而後BKA算法構建key來訪問被鏈接的表,並批量使用MRR接口提交keys到數據庫存儲引擎去查找查找。
提交keys以後,MRR使用最佳的方式來獲取行並反饋給BKA
BNL和BKA都是批量的提交一部分行給被join的表,從而減小訪問的次數,那麼它們有什麼區別呢?
BNL比BKA出現的早,BKA直到5.6纔出現,而NBL至少在5.1裏面就存在。
BNL主要用於當被join的表上無索引
BKA主要是指在被join表上有索引能夠利用,那麼就在行提交給被join的表以前,對這些行按照索引字段進行排序,所以減小了隨機IO,排序這纔是二者最大的區別,可是若是被join的表沒用索引呢?那就使用NBL
Using join buffer (Batched Key Access)和Using join buffer (Block Nested Loop)
BAK使用了MRR,要想使用BAK必須打開MRR功能,而MRR基於mrr_cost_based的成本估算並不能保證老是使用MRR,官方推薦設置mrr_cost_based=off來老是開啓MRR功能。打開BAK功能(BAK默認OFF):
SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
BKA使用join buffer size來肯定buffer的大小,buffer越大,訪問被join的表/內部表就越順序。
BNL默認是開啓的,設置BNL相關參數:
SET optimizer_switch=’block_nested_loop’
支持inner join, outer join, semi-join operations,including nested outer joins
BKA主要適用於join的表上有索引可利用,無索引只能使用BNL
ICP(Index Condition Pushdown)
Index Condition Pushdown是用索引去表裏取數據的一種優化,減小了引擎層訪問基表的次數和Server層訪問存儲引擎的次數,在引擎層就可以過濾掉大量的數據,減小io次數,提升查詢語句性能
MRR(Multi-Range Read)
是基於輔助/第二索引的查詢,減小隨機IO,而且將隨機IO轉化爲順序IO,提升查詢效率。
不使用MRR以前(MySQL5.6以前),先根據where條件中的輔助索引獲取輔助索引與主鍵的集合,再經過主鍵來獲取對應的值。輔助索引獲取的主鍵來訪問表中的數據會致使隨機的IO(輔助索引的存儲順序並不是與主鍵的順序一致),隨機主鍵不在同一個page裏時會致使屢次IO和隨機讀。
使用MRR優化(MySQL5.6以後),先根據where條件中的輔助索引獲取輔助索引與主鍵的集合,再將結果集放在buffer(read_rnd_buffer_size 直到buffer滿了),而後對結果集按照pk_column排序,獲得有序的結果集rest_sort。最後利用已經排序過的結果集,訪問表中的數據,此時是順序IO。即MySQL 將根據輔助索引獲取的結果集根據主鍵進行排序,將無序化爲有序,能夠用主鍵順序訪問基表,將隨機讀轉化爲順序讀,多頁數據記錄可一次性讀入或根據這次的主鍵範圍分次讀入,減小IO操做,提升查詢效率。
Nested Loop Join算法
將驅動表/外部表的結果集做爲循環基礎數據,而後循環該結果集,每次獲取一條數據做爲下一個表的過濾條件查詢數據,而後合併結果,獲取結果集返回給客戶端。Nested-Loop一次只將一行傳入內層循環, 因此外層循環(的結果集)有多少行, 內存循環便要執行多少次,效率很是差。
Block Nested-Loop Join算法
將外層循環的行/結果集存入join buffer, 內層循環的每一行與整個buffer中的記錄作比較,從而減小內層循環的次數。主要用於當被join的表上無索引。
Batched Key Access算法
當被join的表可以使用索引時,就先好順序,而後再去檢索被join的表。對這些行按照索引字段進行排序,所以減小了隨機IO。若是被Join的表上沒有索引,則使用老版本的BNL策略(BLOCK Nested-loop)。