【轉(zhuǎn)載】從MySQL Bug#67718淺談B+樹(shù)索引的分裂優(yōu)化

原文地址:http://hedengcheng.com/?p=525

問(wèn)題背景

今天,看到Twitter的DBA團(tuán)隊(duì)發(fā)布了其最新的MySQL分支:Changes in Twitter MySQL 5.5.28.t9衔憨,此分支最重要的一個(gè)改進(jìn)叶圃,就是修復(fù)了MySQL 的Bug #67718:InnoDB drastically under-fills pages in certain conditions。關(guān)于此Bug的詳細(xì)描述践图,以及如何重現(xiàn)此問(wèn)題掺冠,可以閱讀以上的Bug鏈接,以下簡(jiǎn)單描述下此Bug對(duì)應(yīng)的問(wèn)題:

InnoDB的索引分裂策略码党,在特定的情況下德崭,索引頁(yè)面的分裂存在問(wèn)題,導(dǎo)致每個(gè)分裂出來(lái)的頁(yè)面闽瓢,僅僅存儲(chǔ)一條記錄接癌,頁(yè)面的空間利用率極低心赶。

此Bug引起了我的興趣扣讼,因此準(zhǔn)備跟大家簡(jiǎn)單聊聊B+樹(shù)索引的結(jié)構(gòu)、B+樹(shù)的分裂缨叫、B+樹(shù)分裂操作的優(yōu)化椭符、Bug #67718的成因,以及個(gè)人對(duì)如何修復(fù)此Bug的一些建議等耻姥。

B+樹(shù)索引結(jié)構(gòu)

傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)(Oracle/MySQL/PostgreSQL…)销钝,其主要的索引結(jié)構(gòu),使用的都是B+樹(shù)琐簇。更有甚者蒸健,InnoDB引擎的表數(shù)據(jù),整個(gè)都是以B+樹(shù)的組織形式存放的婉商。下圖似忧,是一個(gè)經(jīng)典的B+樹(shù)組織結(jié)構(gòu)圖(2層B+樹(shù),每個(gè)頁(yè)面的扇出為4):

image

注意:

  • 此B+樹(shù)丈秩,以InnoDB實(shí)現(xiàn)的B+樹(shù)結(jié)構(gòu)為準(zhǔn)盯捌;

  • 此B+樹(shù),有5條用戶記錄蘑秽,分別是1饺著,2箫攀,3,4幼衰,5靴跛;

  • B+樹(shù)上層頁(yè)面中的記錄,存儲(chǔ)的是下層頁(yè)面中的最小值(Low Key)渡嚣;

  • B+樹(shù)的所有數(shù)據(jù)汤求,均存儲(chǔ)在B+樹(shù)的葉節(jié)點(diǎn);

  • B+樹(shù)葉節(jié)點(diǎn)的所有頁(yè)面严拒,通過(guò)雙向鏈表鏈接起來(lái)扬绪;

B+樹(shù)的分裂

在上圖B+樹(shù)的基礎(chǔ)上,繼續(xù)插入記錄6裤唠,7挤牛,B+樹(shù)結(jié)構(gòu)會(huì)產(chǎn)生以下的一系列變化:

插入記錄6,新的B+樹(shù)結(jié)構(gòu)如下:

image

插入記錄7种蘸,由于葉頁(yè)面中只能存放4條記錄墓赴,插入記錄7,導(dǎo)致葉頁(yè)面分裂航瞭,產(chǎn)生一個(gè)新的葉頁(yè)面诫硕。

image

傳統(tǒng)B+樹(shù)頁(yè)面分裂操作分析:

  • 按照原頁(yè)面中50%的數(shù)據(jù)量進(jìn)行分裂,針對(duì)當(dāng)前這個(gè)分裂操作刊侯,3章办,4記錄保留在原有頁(yè)面,5滨彻,6記錄藕届,移動(dòng)到新的頁(yè)面。最后將新紀(jì)錄7插入到新的頁(yè)面中亭饵;

  • 50%分裂策略的優(yōu)勢(shì):

    • 分裂之后休偶,兩個(gè)頁(yè)面的空間利用率是一樣的;如果新的插入是隨機(jī)在兩個(gè)頁(yè)面中挑選進(jìn)行辜羊,那么下一次分裂的操作就會(huì)更晚觸發(fā)踏兜;
  • 50%分裂策略的劣勢(shì):

    • 空間利用率不高:按照傳統(tǒng)50%的頁(yè)面分裂策略,索引頁(yè)面的空間利用率在50%左右八秃;

    • 分裂頻率較大:針對(duì)如上所示的遞增插入(遞減插入)碱妆,每新插入兩條記錄,就會(huì)導(dǎo)致最右的葉頁(yè)面再次發(fā)生分裂喜德;

疑問(wèn)

傳統(tǒng)50%分裂的策略山橄,有不足之處,如何優(yōu)化?接著往下看航棱。

B+樹(shù)分裂操作的優(yōu)化

由于傳統(tǒng)50%分裂的策略睡雇,有不足之處,因此饮醇,目前所有的關(guān)系型數(shù)據(jù)庫(kù)它抱,包括Oracle/InnoDB/PostgreSQL,以及本人以前參與研發(fā)的Oscar數(shù)據(jù)庫(kù)朴艰,目前正在研發(fā)的NTSE观蓄、TNT存儲(chǔ)引擎,都針對(duì)B+樹(shù)索引的遞增/遞減插入進(jìn)行了優(yōu)化祠墅。經(jīng)過(guò)優(yōu)化侮穿,以上的B+樹(shù)索引,在記錄6插入完畢毁嗦,記錄7插入引起分裂之后亲茅,新的B+樹(shù)結(jié)構(gòu)如下圖所示:

image

對(duì)比上下兩個(gè)插入記錄7之后,B+樹(shù)索引的結(jié)構(gòu)圖狗准,可以發(fā)現(xiàn)二者有很多的不同之處:

  • 新的分裂策略克锣,在插入7時(shí),不移動(dòng)原有頁(yè)面的任何記錄腔长,只是將新插入的記錄7寫(xiě)到新頁(yè)面之中袭祟;

  • 原有頁(yè)面的利用率,仍舊是100%捞附;

  • 優(yōu)化分裂策略的優(yōu)勢(shì):

    • 索引分裂的代價(jià)薪砣椤:不需要移動(dòng)記錄;

    • 索引分裂的概率降低:如果接下來(lái)的插入故俐,仍舊是遞增插入想鹰,那么需要插入4條記錄紊婉,才能再次引起頁(yè)面的分裂药版。相對(duì)于50%分裂策略,分裂的概率降低了一半喻犁;

    • 索引頁(yè)面的空間利用率提高:新的分裂策略槽片,能夠保證分裂前的頁(yè)面,仍舊保持100%的利用率肢础,提高了索引的空間利用率还栓;

  • 優(yōu)化分裂策略的劣勢(shì):

    • 如果新的插入,不再滿足遞增插入的條件传轰,而是插入到原有頁(yè)面剩盒,那么就會(huì)導(dǎo)致原有頁(yè)面再次分裂,增加了分裂的概率慨蛙。

因此辽聊,此優(yōu)化分裂策略纪挎,僅僅是針對(duì)遞增遞減插入有效,針對(duì)隨機(jī)插入跟匆,就失去了優(yōu)化的意義异袄,反而帶來(lái)了更高的分裂概率。

在InnoDB的實(shí)現(xiàn)中玛臂,為每個(gè)索引頁(yè)面維護(hù)了一個(gè)上次插入的位置烤蜕,以及上次的插入是遞增/遞減的標(biāo)識(shí)。根據(jù)這些信息迹冤,InnoDB能夠判斷出新插入到頁(yè)面中的記錄讽营,是否仍舊滿足遞增/遞減的約束,若滿足約束泡徙,則采用優(yōu)化后的分裂策略斑匪;若不滿足約束,則退回到50%的分裂策略锋勺。

但是蚀瘸,InnoDB的實(shí)現(xiàn),有不足之處庶橱,會(huì)導(dǎo)致下面提到的一個(gè)Bug贮勃。

Bug#67718的成因

在Bug#67718中提到,在特定的插入情況下苏章,InnoDB的索引頁(yè)面利用率極低寂嘉,這是由于InnoDB不正確的使用優(yōu)化分裂策略導(dǎo)致的。

考慮以下的一個(gè)B+樹(shù)枫绅,已有的用戶數(shù)據(jù)是1泉孩,2,3并淋,4寓搬,5,6县耽,100句喷,并且在插入記錄100之后,引起索引頁(yè)面分裂兔毙,記錄100在分裂后被插入到新的頁(yè)面:

image

由于插入100能夠滿足遞增的判斷條件唾琼,因此采用了優(yōu)化分裂策略,分裂不移動(dòng)數(shù)據(jù)澎剥,新紀(jì)錄100插入到新頁(yè)面之中锡溯,原有頁(yè)面的最后插入位置仍舊是6號(hào)記錄不變,原有頁(yè)面仍舊保持遞增的插入標(biāo)識(shí)不變。

此時(shí)祭饭,考慮連續(xù)插入9涌乳,8,7這幾條記錄甜癞,會(huì)得到什么樣的B+樹(shù)夕晓?此時(shí),全局遞增插入變?yōu)槿诌f減插入悠咱。

插入記錄9后的B+樹(shù)結(jié)構(gòu):

image

由于InnoDB的B+樹(shù)蒸辆,上層節(jié)點(diǎn)保存的是下層頁(yè)面中的最小值(Low Key),因此記錄9仍舊會(huì)插入到【3析既,4躬贡,5,6】頁(yè)面眼坏,此時(shí)頁(yè)面已滿拂玻,需要分裂。而且判斷出記錄9仍舊滿足頁(yè)面中的遞增判斷條件(Last_Insert_Pos = 6宰译,9插入到6之后檐蚜,并且原來(lái)是遞增插入的)。因此沿侈,采用優(yōu)化的分裂策略闯第,產(chǎn)生新的頁(yè)面插入記錄9,原有頁(yè)面記錄保持不變缀拭。

插入記錄8后的B+樹(shù)結(jié)構(gòu):

image

插入記錄7咳短,也一樣。采用優(yōu)化的分裂策略蛛淋,記錄7獨(dú)占一個(gè)頁(yè)面咙好。

分析:

  • Bug#67718的主要副作用

    • 是頁(yè)面的利用率極低,每個(gè)索引葉頁(yè)面褐荷,只能存放一條記錄勾效;
  • Bug#67718的主要原因

    • InnoDB錯(cuò)誤的采用了優(yōu)化的索引分裂策略。InnoDB判斷是否滿足遞增/遞減的插入模式诚卸,采用的是頁(yè)面級(jí)的判斷葵第,哪怕全局的模式發(fā)生了變化,只要頁(yè)面內(nèi)記錄的模式未變合溺,仍舊會(huì)選擇優(yōu)化后的索引分裂策略;

修復(fù)Bug#67718的建議

在本人做Oscar數(shù)據(jù)庫(kù)的索引分裂優(yōu)化時(shí)缀台,當(dāng)時(shí)也同樣碰到了此問(wèn)題棠赛。當(dāng)時(shí)的解決方案是:每次分裂,若插入的記錄是頁(yè)面中的最后一條記錄,則至少將此記錄前一條記錄分裂到新頁(yè)面之中睛约。采用此策略鼎俘,針對(duì)100,9辩涝,8這一個(gè)系列的插入贸伐,會(huì)產(chǎn)生以下的系列B+樹(shù):

插入100,9怔揩,8后的B+樹(shù):

image

插入100時(shí)捉邢,移動(dòng)原有頁(yè)面最后一條記錄到新的頁(yè)面(將6移動(dòng)到新頁(yè)面),此時(shí)新頁(yè)面中的記錄為【6商膊,100】伏伐。接下來(lái)插入9,8晕拆,都會(huì)插入到新的頁(yè)面之中藐翎,不會(huì)產(chǎn)生分裂操作,空間利用率提高实幕,減少了索引頁(yè)面分裂吝镣,解決了Bug#67718的問(wèn)題。

當(dāng)然昆庇,肯定還有更優(yōu)的策略赤惊,歡迎感興趣的朋友們一起討論!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末凰锡,一起剝皮案震驚了整個(gè)濱河市未舟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掂为,老刑警劉巖裕膀,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異勇哗,居然都是意外死亡昼扛,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門欲诺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)抄谐,“玉大人,你說(shuō)我怎么就攤上這事扰法∮己” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵塞颁,是天一觀的道長(zhǎng)浦箱。 經(jīng)常有香客問(wèn)我吸耿,道長(zhǎng),這世上最難降的妖魔是什么酷窥? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任咽安,我火速辦了婚禮,結(jié)果婚禮上蓬推,老公的妹妹穿的比我還像新娘妆棒。我一直安慰自己,他們只是感情好沸伏,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布糕珊。 她就那樣靜靜地躺著,像睡著了一般馋评。 火紅的嫁衣襯著肌膚如雪放接。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天留特,我揣著相機(jī)與錄音纠脾,去河邊找鬼。 笑死蜕青,一個(gè)胖子當(dāng)著我的面吹牛苟蹈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播右核,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼慧脱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了贺喝?” 一聲冷哼從身側(cè)響起菱鸥,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎躏鱼,沒(méi)想到半個(gè)月后氮采,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡染苛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年鹊漠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茶行。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡躯概,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出畔师,到底是詐尸還是另有隱情娶靡,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布茉唉,位于F島的核電站固蛾,受9級(jí)特大地震影響结执,放射性物質(zhì)發(fā)生泄漏度陆。R本人自食惡果不足惜艾凯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望懂傀。 院中可真熱鬧趾诗,春花似錦、人聲如沸蹬蚁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)犀斋。三九已至贝乎,卻和暖如春苍狰,著一層夾襖步出監(jiān)牢的瞬間握童,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工逛漫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虫几,地道東北人锤灿。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像辆脸,于是被迫代替她去往敵國(guó)和親但校。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容