再談ICP

?準(zhǔn)確地說(shuō),這是一篇打臉文章,打的還是自己的臉,真疼。

舊文為什么你的SQL執(zhí)行很慢曾經(jīng)簡(jiǎn)單分析了一下SQL慢查的原因,然而,文中關(guān)于ICP的論述卻是錯(cuò)誤的。但是因?yàn)楣娞?hào)文章無(wú)法修改,所以今天,我們就再來(lái)詳細(xì)了解一下ICP。

為了方便說(shuō)明,這里先給出建表語(yǔ)句和初始化語(yǔ)句:

CREATE TABLE `t` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `a` int(10) DEFAULT NULL,
  `b` varchar(16) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `c` varchar(8) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_a` (`a`)
  KEY `idx_b` (`b`),
  KEY `idx_a_c` (`a`,`c`)
) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci


delimiter ;;
create procedure init()
begin
  declare i int;
  set i=1;
  while(i<=10000)do
    insert into t values(i, i, cast(i as char), '12345678');
    set i=i+1;
  end while;
end;;
delimiter ;
call init();

ICP

ICP,全稱Index Condition Pushdown,是MySQL5.6版本推出的一項(xiàng)新特性。旨在將部分where條件的過(guò)濾從server層下放至engine層,以減少回表次數(shù)和server層對(duì)數(shù)據(jù)記錄的讀取行數(shù),從而提高SQL的執(zhí)行效率。
為了弄清楚為什么ICP可以提高優(yōu)化SQL的執(zhí)行效率,我們先簡(jiǎn)單了解一下MySQL的整體架構(gòu)。
從大的層面來(lái)說(shuō),MySQL可以分為兩層。上層是Server層,主要包含了MySQL一些核心功能的實(shí)現(xiàn),比如優(yōu)化器等等;下層是Engine層,主要就是各種存儲(chǔ)引擎,負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)。如下圖:

image

其中Server層又可以進(jìn)一步細(xì)分如下:

連接器:主要是負(fù)責(zé)建立連接、權(quán)限校驗(yàn)等等工作,解決who的問(wèn)題
分析器:主要是負(fù)責(zé)語(yǔ)法分析和詞法分析,解決what的問(wèn)題
優(yōu)化器:主要是負(fù)責(zé)執(zhí)行計(jì)劃的確定,解決how的問(wèn)題
執(zhí)行器:根據(jù)優(yōu)化器確定的執(zhí)行計(jì)劃向Engine層發(fā)起數(shù)據(jù)請(qǐng)求,返回結(jié)果
下圖給出了在開啟ICP前后SQL的執(zhí)行示意圖。

image

舉個(gè)例子,對(duì)于這樣一條SQL語(yǔ)句:
select * from t2 where a > 9000 and c like '%1'; 關(guān)閉ICP時(shí),其執(zhí)行計(jì)劃如下:

image

開啟ICP時(shí),其執(zhí)行計(jì)劃則變?yōu)?,Extra字段出現(xiàn)了Using index condition的提示,表示使用的ICP優(yōu)化:

image

對(duì)應(yīng)的慢查日志分別為:

image

可以看到,關(guān)閉ICP時(shí),Engine層最終返回了1000條記錄,但最終結(jié)果集為空,而開啟ICP時(shí),Engine層返回的結(jié)果集就已經(jīng)為空了,減少了1000次不必要的回表操作和Server層對(duì)這1000條記錄的讀取操作。而這其中的原因就在于前者在Engine層只應(yīng)用了a > 9000的條件, c like '%1'這個(gè)條件是放到Server層比較的,開啟ICP之后,Engine層則是同時(shí)應(yīng)用了 a > 9000 and c like '%1'的條件。

MRR

既然談到了ICP,那就順便再聊一聊同樣是MySQL5.6版本推出的另外一項(xiàng)新特性:MRR(Multi-Range Read)。MRR的主要目的是減少對(duì)主鍵索引的隨機(jī)訪問(wèn),將隨機(jī)訪問(wèn)轉(zhuǎn)化為較為順序的數(shù)據(jù)訪問(wèn)。

下圖給出了開啟MRR前后的SQL執(zhí)行示意圖:

image

簡(jiǎn)單解釋一下,通常情況下,根據(jù)二級(jí)索引過(guò)濾出來(lái)的數(shù)據(jù)是按照二級(jí)索引有序,因此回表的時(shí)候也是按照二級(jí)索引的順序回表的。然而,由于主鍵索引的順序和二級(jí)索引的順序通常不一樣,因此這就會(huì)造成對(duì)主鍵索引的隨機(jī)訪問(wèn)。為了避免這一問(wèn)題,開啟MRR后,MySQL會(huì)對(duì)二級(jí)索引過(guò)濾之后的結(jié)果先按照主鍵進(jìn)行排序,然后再按照這個(gè)按主鍵有序的列表進(jìn)行回表操作。

舉個(gè)例子,假設(shè)我們的二級(jí)索引樹如下,現(xiàn)在我們要查詢二級(jí)索引字段大于1的數(shù)據(jù):

image

在未開啟MRR的條件下,由于是按照二級(jí)索引順序回表,所以回表時(shí)的主鍵依次為:40、20、10、60、30、50,這就造成了對(duì)主鍵索引的隨機(jī)訪問(wèn)。
開啟MRR之后,按照主鍵排序,這時(shí)回表的主鍵順序就變成了:10、20、30、40、50、60,從隨機(jī)訪問(wèn)變成了順序訪問(wèn)。
在MySQL5.6之后的版本中,MRR都是默認(rèn)開啟的,但是與ICP只有一個(gè)參數(shù)控制不同,MRR有兩個(gè)參數(shù)共同控制:

image

其中mrr用于控制是否開啟mrr優(yōu)化,但開啟mrr優(yōu)化并不意味著所有符合條件的SQL語(yǔ)句都會(huì)采用此優(yōu)化,因?yàn)榕判虿僮饕彩怯写鷥r(jià)的,如果優(yōu)化器評(píng)估認(rèn)為排序代價(jià)太大,也可能會(huì)放棄mrr優(yōu)化。因此如果你想一直啟用mrr功能,則需要這么設(shè)置:mrr=on,mrrcostbased=off.

全文完(?′?‵?)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容