1. 程式人生 > >淺析MySQL中的Index Condition Pushdown (ICP 索引條件下推)和Multi-Range Read(MRR 索引多範圍查詢)查詢優化

淺析MySQL中的Index Condition Pushdown (ICP 索引條件下推)和Multi-Range Read(MRR 索引多範圍查詢)查詢優化

一、ICP優化原理

Index Condition Pushdown (ICP),也稱為索引條件下推,體現在執行計劃的上是會出現Using index condition(Extra列,當然Extra列的資訊太多了,只能做簡單分析)
ICP原理通俗講就是,查詢過程中,直接在查詢引擎層的API獲取資料的時候實現"非直接索引"過濾條件的篩選,而不是查詢引擎層查詢出來之後在Server層篩選。
換句話說就是ICP在獲取資料的同時實現了where的次選條件中無法直接使用索引的情況下的篩選,避免了沒有ICP優化的時候分兩個步驟的實現(獲取資料的過程沒有做次選條件的過濾)
如果是非ICP優化查詢的話,是兩步,第一步是獲取資料,第二步是獲取的資料進行條件篩選。
顯然,相比後者,前者可以一步實現索引的查詢Seek+filter,效率上更高。

適應的場景:
ICP的優化策略可用於range、ref、eq_ref、ref_or_null 型別的訪問資料方法

 

其實沒有例項不太好理解這種優化策略,還是舉兩個實際列子吧。

 

ICP優化例項

第一個例子在網上非常多,也非常容易理解.具體表結構見上文(http://www.cnblogs.com/wy123/p/7366486.html

下面用到的test_orderdetail表的索引為:create index idx_orderid_productname on test_orderdetail(order_id,product_name);
查詢語句為:select * from test_orderdetail where order_id = 10900 and product_name like '%00163e0496af%';
顯然,order_id = 10900是可以直接進行索引查詢的,雖然product_name也包含在複合索引中,但是product_name like '%00163e0496af%'是無法使用索引的
觀察其執行計劃,發現Extra中是Using index condition。

ICP在這裡的優化原理就是,
在利用第一個條件 order_id = 10900 進行索引查詢的過程中,同時使用product_name like '%00163e0496af%'這個無法直接使用索引查詢的條件進行過濾。
最終一步就可以篩選出來結果。

set optimizer_switch='index_condition_pushdown=off或者on'

  對比關閉ICP優化的情況
  如果關閉ICP優化,執行計劃的Extra顯示為Using where,
  意味著使用order_id = 10900進行索引查詢之後,再對結果集進行product_name like '%00163e0496af%'的篩選

  

  第二個例子是後面自己想的,為了驗證ICP的出現場景,以及確實優於非ICP優化的情況

這一次使用的表是test_order,test_order上的索引為create index idx_userid_order_id_createdate on test_order(user_id,order_id,create_date);
查詢語句為:select * from test_order where user_id = 500 and create_date > '2015-1-1';
與上面的例子一樣,第二個篩選條件是無法直接使用索引的

     首先看兩者的執行計劃在ICP優化上的區別

  關閉ICP之後的執行計劃

  然後分別執在開啟與關閉ICP的情況下,觀察其執行過程中的profile資訊

  檢視兩個sql執行的詳細資訊,也即分別在開啟與關閉ICP優化的情況下,如下,在stage/sql/Sending data環節有超過一個數量級的差異。
  也就意味著通過ICP機制的優化,server 層和 engine 層之間資料互動的次數減少。

  

  引用MySQL · 特性分析 · Index Condition Pushdown (ICP)中的一句話
  在二級索引是複合索引且前面的條件過濾性較低的情況下,開啟 ICP 可以有效的降低 server 層和 engine 層之間互動的次數,從而有效的降低在執行時間。

 

  最後,再思考一個問題,
  對於select * from test_orderdetail where order_id = 10900 and product_name like '%00163e0496af%';這個查詢,
  如果order_id 包含在一個二級索引中,但是product_name 沒有包含在這個二級索引中,MySQL會不會採用ICP的方式進行優化?
  答案是否定的。
  因為ICP的前提兩個查詢條件包被索引覆蓋,但是次選條件無法直接使用索引查詢,如果次選條件沒有被索引覆蓋,是無法得知次選條件的值的,也就無從 索引條件下推優化了。

  

 

  

 

二、Multi-Range Read(MRR)

非MRR優化下存在的問題:
首先了解一點背景知識:MySQL的Innodb表都是聚集索引表,沒有顯式指定聚集索引的情況下,會自動生成一個聚集索引。
在使用二級索引(或者說是非聚集索引)進行範圍查詢的條件下,二級索引會根據其B樹結構的葉子節點儲存的聚集索引進行資料的查詢(回表操作),
但是符合條件的資料(二級索引超找的資料)有可能是隨機分佈在聚集索引B樹的任何一個部分,這樣就可能存在表上過多的隨機IO。
當表非常大的時候,每一行的查詢過程都需要在磁碟上隨機進行,可能會對效能造成影響。

舉個例子,
如下圖,參考藍線的移動軌跡,二級索引查詢到的目標資料行的物理位置為1,2,3,4(主要的是以何種順序去獲取這四個位置的資料,可以隨機的方式獲取,也可以順序的方式獲取,講究就在這一點)
在查詢這四個位置的資料的時候,如果直接按照二級索引對應的聚集索引的順序查詢,
由於二級索引排序的情況下,其對應的聚集索引的順序可能是隨機的,那麼其對應的資料的物理位置也就是隨機的了
如果按照二級索引的順去回表超找對應的資料行,那麼這個過程就需要隨機IO查詢。
這種查詢方式的缺點,可以理解為在查詢這四行資料的過程中,在物理位置差異較大的情況下,需要磁頭來回擺臂來實現(隨機IO讀取)。

MRR多範圍讀取優化的目的是通過對記錄的讀取請求進行排序,然後再讀取資料行的時候以順序IO的方式進行,避免隨機IO
究竟是對哪個欄位排序?個人認為可以理解成二級索引範圍查詢到的對應的聚集索引的key值進行排序。
有序掃描的過程可以認為是:

(1)通過非聚集索引找到目標資料的聚集索引的key值
(2)對通過二級索引找到的目標資料的聚集索引的key值排序,此時聚集索引與物理位置一一對應。
(3)(回表的過程)通過二級索引對應的有序的聚集索引,執行一個有序的磁碟掃描來獲取資料,從而來加快讀取資料的速度。

順序讀磁碟通常會更快,當然也不是說這種方式的效率總是較高的,凡事有利必有弊,也有例外的情況

1,如果掃描的是一個較小的資料範圍,並且目標資料已經在磁碟的快取當中,MRR的唯一影響是為了緩衝/排序額外的增加了一些CPU開銷。
2,order by *** LIMIT n查詢,當n值比較小的時候,可能會變的更慢,
   原因是 MRR試圖通過順序讀盤的方式(來或取資料),可能一開始讀取到的資料並非總是排在(order by ***)符合前N條的。
3,MRR是一個實現過程,個人理解,極端情況下,如果MySQL不知道目標資料的行數,
   如果僅僅只有一行,依然要進行排序操作,然後回表讀取資料行,這種情況下也是得不償失的。

  開啟MRR優化
  set global optimizer_switch = 'mrr=on,mrr_cost_based=off';

  啟用MRR優化的前提是要進行書籤超找,也即要回表,如果不需要回表的話,二級索引本身就可以查詢出來需要的欄位了,沒有隨機IO的機會的所謂了。

  如下截圖,如果去掉order_status,也就意味著無需回表查詢,那麼就不會出現MRR優化了。

  同時,一旦出現MRR優化,查詢出來的結果的順序,必然是按照聚集索引來排序的,這個原理應該是不難理解的。

  

 

  當然MRR優化也有在表關聯情況下的優化措施,原理大同小異。

 

總結:

    Index Condition Pushdown(索引條件下推)和Multi-Range Read(多範圍讀)都是MySQL為了提高查詢優化而備用的選項,屬於MySQL5.6裡面的新特性。
    無奈樓主接觸MySQL不久,見識不夠,很是覺得新鮮,高手勿噴。
    兩者的共同的特點都是在使用索引超找(或者索引範圍掃描)的過程中的一些優化措施。
    這些優化措施可以在二級索引查詢(索引範圍掃描)的過程中優化查詢動作的行為,
    當然這些優化措施並非總是萬能的,允許使用者顯式開啟或者關閉,給使用者充分的自由,然而自由也並非完全沒有問題,這也要求使用者在做相關優化的時候需要進行充分的權衡和考慮。

 

參考:

    https://mariadb.com/kb/en/mariadb/multi-range-read-optimization/
    http://blog.itpub.net/22664653/viewspace-1673682/
    http://blog.itpub.net/22664653/viewspace-1678779/
       http://mysql.taobao.org/monthly/2015/12/08/

     以及各種網上搜索……

 

本文出處:http://www.cnblogs.com/wy123/p/7374078.html 

最後,mariadb官方這幾張圖非常贊,對理解問題很有幫助,先盜下來,備用(無恥一笑,O(∩_∩)O~),

突然又想到做人了,為什麼一定要直來直去呢,很多時候是欲速則不達,迂迴一下,暫時停下來,好好計劃計劃再出發,未必是壞事。

Index Condition Pushdown (ICP),也稱為索引條件下推,體現在執行計劃的上是會出現Using index condition(Extra列,當然Extra列的資訊太多了,只能做簡單分析)
ICP原理通俗講就是,查詢過程中,直接在查詢引擎層的API獲取資料的時候實現"非直接索引"過濾條件的篩選,而不是查詢引擎層查詢出來之後在Server層篩選。
換句話說就是ICP在獲取資料的同時實現了where的次選條件中無法直接使用索引的情況下的篩選,避免了沒有ICP優化的時候分兩個步驟的實現(獲取資料的過程沒有做次選條件的過濾)
如果是非ICP優化查詢的話,是兩步,第一步是獲取資料,第二步是獲取的資料進行條件篩選。
顯然,相比後者,前者可以一步實現索引的查詢Seek+filter,效率上更高。

適應的場景:
ICP的優化策略可用於range、ref、eq_ref、ref_or_null 型別的訪問資料方法

 

其實沒有例項不太好理解這種優化策略,還是舉兩個實際列子吧。

 

ICP優化例項

第一個例子在網上非常多,也非常容易理解.具體表結構見上文(http://www.cnblogs.com/wy123/p/7366486.html

下面用到的test_orderdetail表的索引為:create index idx_orderid_productname on test_orderdetail(order_id,product_name);
查詢語句為:select * from test_orderdetail where order_id = 10900 and product_name like '%00163e0496af%';
顯然,order_id = 10900是可以直接進行索引查詢的,雖然product_name也包含在複合索引中,但是product_name like '%00163e0496af%'是無法使用索引的
觀察其執行計劃,發現Extra中是Using index condition。

ICP在這裡的優化原理就是,
在利用第一個條件 order_id = 10900 進行索引查詢的過程中,同時使用product_name like '%00163e0496af%'這個無法直接使用索引查詢的條件進行過濾。
最終一步就可以篩選出來結果。

set optimizer_switch='index_condition_pushdown=off或者on'

  對比關閉ICP優化的情況
  如果關閉ICP優化,執行計劃的Extra顯示為Using where,
  意味著使用order_id = 10900進行索引查詢之後,再對結果集進行product_name like '%00163e0496af%'的篩選

  

  第二個例子是後面自己想的,為了驗證ICP的出現場景,以及確實優於非ICP優化的情況

這一次使用的表是test_order,test_order上的索引為create index idx_userid_order_id_createdate on test_order(user_id,order_id,create_date);
查詢語句為:select * from test_order where user_id = 500 and create_date > '2015-1-1';
與上面的例子一樣,第二個篩選條件是無法直接使用索引的

     首先看兩者的執行計劃在ICP優化上的區別

  關閉ICP之後的執行計劃

  然後分別執在開啟與關閉ICP的情況下,觀察其執行過程中的profile資訊

  檢視兩個sql執行的詳細資訊,也即分別在開啟與關閉ICP優化的情況下,如下,在stage/sql/Sending data環節有超過一個數量級的差異。
  也就意味著通過ICP機制的優化,server 層和 engine 層之間資料互動的次數減少。

  

  引用MySQL · 特性分析 · Index Condition Pushdown (ICP)中的一句話
  在二級索引是複合索引且前面的條件過濾性較低的情況下,開啟 ICP 可以有效的降低 server 層和 engine 層之間互動的次數,從而有效的降低在執行時間。

 

  最後,再思考一個問題,
  對於select * from test_orderdetail where order_id = 10900 and product_name like '%00163e0496af%';這個查詢,
  如果order_id 包含在一個二級索引中,但是product_name 沒有包含在這個二級索引中,MySQL會不會採用ICP的方式進行優化?
  答案是否定的。
  因為ICP的前提兩個查詢條件包被索引覆蓋,但是次選條件無法直接使用索引查詢,如果次選條件沒有被索引覆蓋,是無法得知次選條件的值的,也就無從 索引條件下推優化了。

  

 

  

 

二、Multi-Range Read(MRR)

非MRR優化下存在的問題:
首先了解一點背景知識:MySQL的Innodb表都是聚集索引表,沒有顯式指定聚集索引的情況下,會自動生成一個聚集索引。
在使用二級索引(或者說是非聚集索引)進行範圍查詢的條件下,二級索引會根據其B樹結構的葉子節點儲存的聚集索引進行資料的查詢(回表操作),
但是符合條件的資料(二級索引超找的資料)有可能是隨機分佈在聚集索引B樹的任何一個部分,這樣就可能存在表上過多的隨機IO。
當表非常大的時候,每一行的查詢過程都需要在磁碟上隨機進行,可能會對效能造成影響。

舉個例子,
如下圖,參考藍線的移動軌跡,二級索引查詢到的目標資料行的物理位置為1,2,3,4(主要的是以何種順序去獲取這四個位置的資料,可以隨機的方式獲取,也可以順序的方式獲取,講究就在這一點)
在查詢這四個位置的資料的時候,如果直接按照二級索引對應的聚集索引的順序查詢,
由於二級索引排序的情況下,其對應的聚集索引的順序可能是隨機的,那麼其對應的資料的物理位置也就是隨機的了
如果按照二級索引的順去回表超找對應的資料行,那麼這個過程就需要隨機IO查詢。
這種查詢方式的缺點,可以理解為在查詢這四行資料的過程中,在物理位置差異較大的情況下,需要磁頭來回擺臂來實現(隨機IO讀取)。

MRR多範圍讀取優化的目的是通過對記錄的讀取請求進行排序,然後再讀取資料行的時候以順序IO的方式進行,避免隨機IO
究竟是對哪個欄位排序?個人認為可以理解成二級索引範圍查詢到的對應的聚集索引的key值進行排序。
有序掃描的過程可以認為是:

(1)通過非聚集索引找到目標資料的聚集索引的key值
(2)對通過二級索引找到的目標資料的聚集索引的key值排序,此時聚集索引與物理位置一一對應。
(3)(回表的過程)通過二級索引對應的有序的聚集索引,執行一個有序的磁碟掃描來獲取資料,從而來加快讀取資料的速度。

順序讀磁碟通常會更快,當然也不是說這種方式的效率總是較高的,凡事有利必有弊,也有例外的情況

1,如果掃描的是一個較小的資料範圍,並且目標資料已經在磁碟的快取當中,MRR的唯一影響是為了緩衝/排序額外的增加了一些CPU開銷。
2,order by *** LIMIT n查詢,當n值比較小的時候,可能會變的更慢,
   原因是 MRR試圖通過順序讀盤的方式(來或取資料),可能一開始讀取到的資料並非總是排在(order by ***)符合前N條的。
3,MRR是一個實現過程,個人理解,極端情況下,如果MySQL不知道目標資料的行數,
   如果僅僅只有一行,依然要進行排序操作,然後回表讀取資料行,這種情況下也是得不償失的。

  開啟MRR優化
  set global optimizer_switch = 'mrr=on,mrr_cost_based=off';

  啟用MRR優化的前提是要進行書籤超找,也即要回表,如果不需要回表的話,二級索引本身就可以查詢出來需要的欄位了,沒有隨機IO的機會的所謂了。

  如下截圖,如果去掉order_status,也就意味著無需回表查詢,那麼就不會出現MRR優化了。

  同時,一旦出現MRR優化,查詢出來的結果的順序,必然是按照聚集索引來排序的,這個原理應該是不難理解的。

  

 

  當然MRR優化也有在表關聯情況下的優化措施,原理大同小異。

 

總結:

    Index Condition Pushdown(索引條件下推)和Multi-Range Read(多範圍讀)都是MySQL為了提高查詢優化而備用的選項,屬於MySQL5.6裡面的新特性。
    無奈樓主接觸MySQL不久,見識不夠,很是覺得新鮮,高手勿噴。
    兩者的共同的特點都是在使用索引超找(或者索引範圍掃描)的過程中的一些優化措施。
    這些優化措施可以在二級索引查詢(索引範圍掃描)的過程中優化查詢動作的行為,
    當然這些優化措施並非總是萬能的,允許使用者顯式開啟或者關閉,給使用者充分的自由,然而自由也並非完全沒有問題,這也要求使用者在做相關優化的時候需要進行充分的權衡和考慮。

 

參考:

    https://mariadb.com/kb/en/mariadb/multi-range-read-optimization/
    http://blog.itpub.net/22664653/viewspace-1673682/
    http://blog.itpub.net/22664653/viewspace-1678779/
       http://mysql.taobao.org/monthly/2015/12/08/

     以及各種網上搜索……

 

本文出處:http://www.cnblogs.com/wy123/p/7374078.html 

最後,mariadb官方這幾張圖非常贊,對理解問題很有幫助,先盜下來,備用(無恥一笑,O(∩_∩)O~),

突然又想到做人了,為什麼一定要直來直去呢,很多時候是欲速則不達,迂迴一下,暫時停下來,好好計劃計劃再出發,未必是壞事。