一起學習Mysql索引三(ICP,索引條件下推)

上一篇文章一起學習Mysql索引二(索引的高性能策略)中我們提到了Mysql5.7版本的一個改進: "索引條件下推"(index condition pushdown)。 那麼,今天就讓我們來揭開它的神祕面紗。

從ICP(index condition pushdown)的字面意思來看,大家疑惑的點應該就是這個"pushdown"--下推了。 什麼是下推,下推到哪裏,有什麼用?帶着疑問,我們先從關閉和開啓ICP對一些sql語句的影響,然後再進一步的解答提出的問題。

首先,我們可以通過如下語句開啓或關閉Myslq的ICP特性:
SET optimizer_switch = 'index_condition_pushdown=off'; //關閉
SET optimizer_switch = 'index_condition_pushdown=on'; //開啓

Mysql 新版本默認開啓該特性,然後我們準備一張表:

CREATE TABLE demo (id bigint(11) unsigned NOT NULL AUTO_INCREMENT,pid int(11) DEFAULT '0',nid int(5) DEFAULT NULL,country varchar(10) DEFAULT '',name varchar(10) DEFAULT '',status tinyint(1) DEFAULT '1',num int(10) DEFAULT '0', PRIMARY KEY (id) USING BTREE,KEY ix_pnnc (pid,nid,name,country) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT;

其中表中的數據量如下圖所示:
一起學習Mysql索引三(ICP,索引條件下推)

不使用ICP的情況

關閉ICP:
SET optimizer_switch = 'index_condition_pushdown=off';

然後調用如下語句進行三次查詢:
select * from demo where pid =14 and nid like '%2%' and country like '%a%' and name like '%a%';

通過show profiles 查看三條語句的查詢時間分別是:
+----------+------------+-------------------------------------------------------------------------------------------------+| Query_ID | Duration | Query |+----------+------------+-------------------------------------------------------------------------------------------------+
| 723 | 0.02307625 | select from demo where pid =14 and nid like '%2%' and country like '%a%' and name like '%a%' |
| 724 | 0.0218665 | select
from demo where pid =14 and nid like '%2%' and country like '%a%' and name like '%a%' |
| 725 | 0.02217525 | select * from demo where pid =14 and nid like '%2%' and country like '%a%' and name like '%a%' |+----------+------------+-------------------------------------------------------------------------------------------------+

使用ICP的情況

開啓ICP:
SET optimizer_switch = 'index_condition_pushdown=on';

還是調用上面那條sql語句三次,然後show profiles 查看查詢時間:
+----------+------------+-------------------------------------------------------------------------------------------------+| Query_ID | Duration | Query |+----------+------------+-------------------------------------------------------------------------------------------------+
| 740 | 0.07347475 | select from demo where pid =14 and nid like '%2%' and country like '%a%' and name like '%a%' |
| 741 | 0.07267875 | select
from demo where pid =14 and nid like '%2%' and country like '%a%' and name like '%a%' |
| 742 | 0.072476 | select * from demo where pid =14 and nid like '%2%' and country like '%a%' and name like '%a%' |
+----------+------------+-------------------------------------------------------------------------------------------------+

我們看到,在開啓ICP後,速度提升了3倍之多。

通過explain我們看看兩者的區別:

關閉ICP情況:
一起學習Mysql索引三(ICP,索引條件下推)
開啓ICP情況:一起學習Mysql索引三(ICP,索引條件下推)
我們可以看到,在Extra字段上,關閉ICP時,用的是Using where,而在開啓ICP時,使用的是Using index condition。

通過上面的對比我們可以看到,在使用了ICP之後,確實提升了我們的查詢性能,可是ICP是不是針對所有的sql語句都有效果呢?我們在來看執行下面的語句後,三次查詢時間的差別:
select from demo where pid =14 and num > 0;
關閉ICP:
+----------+------------+-----------------------------------------------+| Query_ID | Duration | Query
|+----------+------------+-----------------------------------------------+
| 100| 0.07347475 | select
from demo where pid =14 and num > 0 || 101| 0.07788525 | select from demo where pid =14 and num > 0 |
| 102| 0.07912425 | select
from demo where pid =14 and num > 0
|+----------+------------+-----------------------------------------------+
開啓ICP:

+----------+------------+-----------------------------------------------+| Query_ID | Duration | Query
|+----------+------------+-----------------------------------------------+
| 117| 0.07916325 | select from demo where pid =14 and num > 0 |
| 118| 0.08040075 | select
from demo where pid =14 and num > 0 |
| 119| 0.07897455 | select * from demo where pid =14 and num > 0
|+----------+------------+-----------------------------------------------+

我們發現,對於這條語句,開啓或關閉ICP並不會對查詢有任何性能提升。

那麼下面就和強哥一起來看看是爲什麼吧。

怎麼理解ICP

Index Condition Pushdown (ICP)是MySQL用索引去表裏取數據的一種優化。如果禁用ICP,引擎層會穿過索引在基表中尋找數據行,然後返回給MySQL Server層,再去爲這些數據行進行WHERE後的條件的過濾。 ICP啓用,如果部分WHERE條件能使用索引中的字段,MySQL Server 會把這部分下推到引擎層。存儲引擎通過使用索引條目,然後推索引條件進行評估,使用這個索引把滿足的行從表中讀取出。ICP能減少引擎層訪問基表的次數和MySQL Server 訪問存儲引擎的次數。

用兩張圖解釋下:

關閉ICP:
一起學習Mysql索引三(ICP,索引條件下推)

此時,索引符合之前推文提過的最左前綴原理,當多列索引的某一列是範圍查詢後,之後的字段便不會走索引。

開啓ICP:

一起學習Mysql索引三(ICP,索引條件下推)

開啓ICP後,查詢同樣符合最左前綴規則,但是當多列索引的某一列是範圍查詢後,之後的字段還是會被下推到存儲引擎(Storage Engine)層進行條件判斷,過濾出符合條件的數據後再返回給Server層。而由於在引擎層就能夠過濾掉大量的數據,這樣無疑能夠減少了對base table和mysql server的訪問次數。 從而提升了性能。

分析試驗數據提升原因

關閉ICP是這樣的: 1. 從索引裏面取出下一條pid=14的記錄,然後利用主鍵字段讀取整個行。 2. 然後對這個完整的行利用其餘的條件這個進行判斷看是否符合條件,在Server層進行過濾和處理。

開啓ICP是這樣的:

  1. 從索引裏面取出下一條pid=14的記錄,然後利用這個索引的其他字段條件進行判斷,如果條件成立,執行第2步。在引擎層上進行過濾和處理。 2. 在上一步中篩選出來符合條件的纔會利用主鍵索引裏面找到這個完整行,返回。

也就是說: 在沒有ICP前,由於優化器只能只能使用前綴索引來過濾滿足條件的查詢,那麼mysql只能夠利用索引的第一個字段pid,來掃描demo表滿足pid=14條件的記錄,而後面的nid和country由於使用了模糊查詢,而不能在索引中繼續過濾滿足條件的記錄,這樣就導致了Server層對demo表的掃描增加了許多;

有了ICP,mysql在讀取demo表前,繼續檢查滿足nid和country條件的記錄,這個行爲在引擎層完成。直接把過濾好的返回給Server層,就減少了Server層的操作。 總之是把之前在SERVER層的下推到引擎層去處理。

這裏也就解釋了ICP(索引條件下推)其實就是將索引條件下推到引擎層啦。

ICP的使用限制

ICP雖然挺好用的,但是並不是所有的sql都能夠通過ICP得到性能提升,就如我們上面的第二條sql就無法通過ICP減少查詢時間。因爲如果where條件的字段不在索引列中,還是要讀取整表的記錄到server端做where過濾。

這裏列出幾點ICP的相關限制:

當sql需要全表訪問時,ICP的優化策略可用於range, ref, eq_ref, ref_or_null 類型的訪問數據方法 。

支持InnoDB和MyISAM表。

ICP只能用於二級索引,不能用於主索引。

並非全部where條件都可以用ICP篩選。如果where條件的字段不在索引列中,還是要讀取整表的記錄到server端做where過濾。

ICP的加速效果取決於在存儲引擎內通過ICP篩選掉的數據的比例。

5.6 版本的不支持分表的ICP 功能,5.7 版本的開始支持。

當sql 使用覆蓋索引時,不支持ICP 優化方法。

結論:

ICP的優化在引擎層就能夠過濾掉大量的數據,這樣無疑能夠減少了對base table和mysql server的訪問次數,提升了性能。

需要index condition pushdown 的query通常索引的字段出現where子句裏面都是範圍查詢。比如:

select from tb where tb.key_part1 < x and tb.key_part2 = y select from tb where tb.key_part1 = x andtb.key_part2 like '%yyyy%'select * from tb where tb.key_part1 > x and tb.key_part1 < y and tb.key_part1 > xx and tb.key_part2 < yy
但是需要注意的是:

如果索引的第一個字段的查詢就是沒有邊界的比如 key_part1 like '%xxx%',那麼不要說ICP,就連索引都會沒法利用。

如果select的字段全部在索引裏面,那麼就是直接的index scan了,沒有必要什麼ICP。

參考:

https://www.cnblogs.com/zhoujinyi/archive/2013/04/16/3016223.html

http://blog.codinglabs.org/articles/index-condition-pushdown.html

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