第五章 索引與算法 (中)B+樹索引的使用

5.6 B+樹索引的使用
5.6.1 不同應(yīng)用中B+樹索引的使用
在數(shù)據(jù)庫中存在兩種類型的應(yīng)用,OLTP和OLAP應(yīng)用乏奥。在OLTP應(yīng)用中买窟,查詢操作只從數(shù)據(jù)庫中取得一小部分?jǐn)?shù)據(jù),一般可能都在10條記錄以下喷户,甚至在很多時候只取1條記錄,如根據(jù)主鍵值來取得用戶信息访锻,根據(jù)訂單號取得訂單的詳細(xì)信息褪尝,這都是典型OLTP應(yīng)用的查詢語句闹获。在這種情況下,B+樹索引建立后河哑,對該索引的使用應(yīng)用只是通過該索引取得表中少部分?jǐn)?shù)據(jù)避诽。這是建立B+樹索引才是有意義的。否則即使建立了璃谨,優(yōu)化器也可能選擇不使用索引沙庐。在OLAP應(yīng)用中,都需要訪問表中大量的數(shù)據(jù)睬罗,根據(jù)這些數(shù)據(jù)來產(chǎn)生查詢的結(jié)果轨功,對于OLAP中復(fù)雜的查詢,要涉及到多張表之間的聯(lián)接操作容达,因此索引的添加仍然是有意義的古涧。

5.6.2 聯(lián)合索引
聯(lián)合索引是指對表上多個列進(jìn)行索引。前面討論的情況都是只對表上的一個列進(jìn)行索引花盐。聯(lián)合索引的創(chuàng)建方法與單個索引的創(chuàng)建方法一樣羡滑,不同之處僅在于有多個索引列。
創(chuàng)建如下表t

image.png

從本質(zhì)上來看算芯,聯(lián)合索引也是一顆B+樹柒昏,不同的是聯(lián)合索引的鍵值的數(shù)量大于等于2,不是1
多個鍵值的B+樹.png

對于查詢 select * from t where a=xxx and b=xxx 顯然是可以使用(a熙揍,b)這個聯(lián)合索引的职祷。對于單個的a列查詢select * from t where a=xxx 也可以使用這個(a,b)索引届囚。但對于 select * from t where b=xxx有梆,則不可以使用這顆B+樹索引。
聯(lián)合索引的第二個好處是已經(jīng)對第二個鍵值進(jìn)行了排序處理意系∧嘁可以避免一次排序操作。
創(chuàng)建如下表 buy_log
bug.png

插入如下數(shù)據(jù)
image.png

創(chuàng)建如下索引


image.png
image.png

對于 select * from buy_date where userid = 2蛔添,優(yōu)化器的選擇為如圖5-23所示:

image.png

possible_key表示有兩個索引可供使用痰催,分別是單個的userid索引和(userid,buy_date)的聯(lián)合索引迎瞧。但是優(yōu)化器最終選擇的索引userid夸溶,因為該索引的葉子節(jié)點包含單個鍵值,所以理論上一個頁能存放的記錄應(yīng)該更多凶硅。

對于 select * from buy_log where userid = 1 order by buy_date desc limit 3
執(zhí)行計劃如下:

image.png

可以使用userid索引蜘醋,也可以使用(userid,buy_date)索引咏尝。但是這次優(yōu)化器使用了(user_id压语,buy_date)的聯(lián)合索引userid_2,因為在這個聯(lián)合索引中buy_date已經(jīng)排序好了编检。根據(jù)該聯(lián)合索引取出數(shù)據(jù)胎食,無需再對buy_date做一次額外的排序操作。
若強制使用userid索引允懂,則執(zhí)行計劃如下所示:


image.png

在Extra選項中可以看到Using filesort厕怜,即需要額外的一次排序操作才能完成查詢。

對于聯(lián)合索引(a蕾总,b粥航,c)來說,
select * from table where a = xxx order by b
select * from table where a = xxx and b = xxx order by c
可以通過聯(lián)合索引來得到結(jié)果生百,但是對于
select * from table where a =xxx order by c 來說递雀,聯(lián)合索引不能直接得到結(jié)果,還需要執(zhí)行一次filesort排序操作蚀浆,因為索引(a缀程,c)未排序

5.6.3 覆蓋索引
InnoDB存儲引擎支持覆蓋索引(covering index,或稱覆蓋索引)市俊,即從輔助索引中就可以得到查詢記錄杨凑,而不需要查詢聚集索引中的記錄。使用覆蓋索引的一個好處是摆昧,輔助索引不包含整行記錄的所有信息撩满,故其大小要遠(yuǎn)小于聚集索引,因此可以減少大量的IO操作绅你。

覆蓋索引的另一個好處是對某些統(tǒng)計問題而言的伺帘。要進(jìn)行如下查詢:
select count(*) from buy_log
InnoDB存儲引擎并不會選擇通過查詢聚集索引來進(jìn)行統(tǒng)計查詢。由于buy_log表上還有輔助索引勇吊,而輔助索引遠(yuǎn)小于聚集索引曼追,選擇輔助索引可以減少IO操作,故優(yōu)化器的選擇為如下圖所示:

image.png

possible_keys列為NULL汉规,但是實際執(zhí)行時優(yōu)化器卻選擇了userid索引礼殊,而列Extra的Using index就是代表了優(yōu)化器進(jìn)行了覆蓋操作操作。
在通常情況下针史,諸如(a晶伦,b)的聯(lián)合索引,一般是不可以選擇列b中所謂的查詢條件啄枕。但是如果是統(tǒng)計操作婚陪,并且是覆蓋索引的,則優(yōu)化器會進(jìn)行選擇频祝,如下述語句:
select count(*) from buy_log where buy_date >= '2009-01-01' and buy_date < '2009-02-01'

表buy_log中有(userid泌参,buy_date)的聯(lián)合索引脆淹,這里只根據(jù)列b進(jìn)行條件查詢,一般情況下是不能進(jìn)行該聯(lián)合索引的沽一,但是這句SQL查詢是統(tǒng)計操作盖溺,并且可以利用到覆蓋索引的信息,因此優(yōu)化器會選擇改聯(lián)合索引铣缠,其執(zhí)行計劃如下圖:


image.png

** 5.6.4 優(yōu)化器選擇不使用索引的情況 **
在某些情況下烘嘱,當(dāng)執(zhí)行EXPLAIN命令進(jìn)行SQL語句分析時,會發(fā)現(xiàn)優(yōu)化器并沒有選擇索引去查找數(shù)據(jù)蝗蛙,而是通過掃描聚集索引蝇庭,也就是直接進(jìn)行全部掃描來得到數(shù)據(jù)。這種情況多發(fā)生于范圍查找捡硅、JOIN鏈接操作等情況下哮内。例如:
select * from orderdetails where orderid > 10000 and orderid < 102000;
表orderdetails有(orderid,productid)的聯(lián)合索引,此外還有對于列orderid的單個索引病曾。上述SQL顯然是可以通過掃描orderid上的索引進(jìn)行數(shù)據(jù)的查找牍蜂。然而通過EXPLAIN命令,用戶會發(fā)現(xiàn)優(yōu)化器并沒有按照orderid上的索引來查找數(shù)據(jù)泰涂。優(yōu)化器選擇了primary聚集索引鲫竞,也就是表掃描,而非orderid輔助索引掃描逼蒙。
原因在于要選取的數(shù)據(jù)是整行信息从绘,而orderid索引不能覆蓋到我們要查詢的信息,因此在對orderid索引查詢到指定數(shù)據(jù)后是牢,還需要一次書簽訪問來查找整行數(shù)據(jù)的信息僵井。雖然orderid索引中數(shù)據(jù)是順序存放的,但是再一次進(jìn)行書簽查找的數(shù)據(jù)則是無序的驳棱,因此變?yōu)榱舜疟P上的離散讀操作批什。如果要求訪問的數(shù)據(jù)量很小,則優(yōu)化器還是會選擇輔助索引社搅,但是當(dāng)訪問的數(shù)據(jù)占整個表中數(shù)據(jù)的蠻大一部分時(一般20%左右)驻债,優(yōu)化器會選擇通過聚集索引來查找數(shù)據(jù)。因為順序讀要遠(yuǎn)遠(yuǎn)快于離散讀形葬。

** 索引提示 **
MySQL數(shù)據(jù)庫支持索引提示(INDEX HINT)合呐,顯示地告訴優(yōu)化器使用哪個索引。以下兩種情況可能需要用到index hint

  • MySQL數(shù)據(jù)庫的優(yōu)化器錯誤地選擇了某個索引笙以,導(dǎo)致SQL語句運行的很慢淌实。這種情況在最新版的MySQL數(shù)據(jù)庫中很少見
  • 某個SQL語句可以選擇的索引非常多,這是優(yōu)化器選擇執(zhí)行計劃時間的開銷可能會大于SQL語句本身。例如拆祈,優(yōu)化器分析range查詢本身就是比較耗時的操作恨闪。這是人為的分析最優(yōu)索引的選擇,通過index hint來強制使用優(yōu)化器不進(jìn)行各個執(zhí)行路徑的成本分析缘屹,直接選擇指定的索引來完成查詢凛剥。

使用use index只是告訴優(yōu)化器可以選擇該索引,實際上優(yōu)化器還是會再根據(jù)自己的判斷進(jìn)行選擇轻姿,只有使用force index才能是優(yōu)化器的最終選擇和用戶指定的索引一致。

** Multi-Range Read優(yōu)化 **
MySQL5.6 版本開始支持Multi-Range Read(MRR)優(yōu)化逻炊。MRR優(yōu)化的目的就是為了減少磁盤的隨機訪問互亮,并且將隨機訪問轉(zhuǎn)化為較為順序的數(shù)據(jù)訪問,這對于IO-bound類型的SQL查詢語句可帶來極大的性能提升余素。MRR優(yōu)化可適用于range豹休,ref,eq_ref類型的查詢桨吊。
MRR優(yōu)化有以下幾個好處:

  • MRR使數(shù)據(jù)訪問變得較為順序威根。在查詢輔助索引時,首先根據(jù)得到的查詢結(jié)果视乐,按照主鍵排序的順序進(jìn)行書簽查找洛搀。
  • 減少緩沖池中頁被替換的次數(shù)
  • 批量處理對鍵值的查詢操作

對于InnoDB和MyISAM存儲引擎的范圍查詢和JOIN查詢,MRR的工作方式如下:
1佑淀、將查詢得到的輔助索引鍵值放于一個緩沖中留美,這是緩沖中的數(shù)據(jù)是根據(jù)輔助索引鍵來排序的
2、將緩存中的鍵值根據(jù)RowID進(jìn)行排序
3伸刃、根據(jù)RowID的排序順序來訪問實際的數(shù)據(jù)文件

此外谎砾,若InnoDB存儲引擎或者M(jìn)yISAM存儲引擎的緩沖池不是足夠大,即不能存放下一張表中的所有數(shù)據(jù)捧颅,此外頻繁的離散讀操作還會導(dǎo)致緩沖中的頁被替換出緩沖池景图,然后又不斷地讀入緩沖池。若是按照主鍵順序進(jìn)行訪問碉哑,則可以將此重復(fù)行為降為最低挚币。
MRR還可以將某些將某些范圍查詢,拆分為鍵值對谭梗,以此來進(jìn)行批量的數(shù)據(jù)查詢忘晤。這樣做的好處是可以在拆分過程中,直接過濾一些不符合查詢條件的數(shù)據(jù)激捏,例如:
select * from t where key_part1 >= 1000 and key_part1 < 2000 and key_part2 = 1000 设塔,表t有(key_part1,key_part2)的聯(lián)合索引,因此索引根據(jù)key_part1闰蛔,key_part2的位置關(guān)系進(jìn)行排序痕钢。如沒有MRR,此時查詢類型為Range序六,SQL優(yōu)化器會先將key_part1大于等于1000且小于2000的數(shù)據(jù)都取出任连,即使key_part2不等于1000,待取出行數(shù)據(jù)后再根據(jù)key_part2的條件進(jìn)行過濾例诀。這會導(dǎo)致無用數(shù)據(jù)被取出随抠。如果有大量的數(shù)據(jù)且其key_part2不等于1000,則啟用MRR優(yōu)化使性能有巨大提升繁涂,若是啟用了MRR優(yōu)化拱她,優(yōu)化器會先將查詢條件進(jìn)行拆分,然后再進(jìn)行數(shù)據(jù)查詢扔罪。就上述查詢而言秉沼,優(yōu)化器會將查詢條件進(jìn)行拆分為(1000,1000),(1001,1000)矿酵,...唬复,(1999,1000),最后根據(jù)這些拆分出來的條件進(jìn)行數(shù)據(jù)的查詢全肮。

是否啟動MRR優(yōu)化可以通過參數(shù)optimizer_switch中的標(biāo)記來控制敞咧。當(dāng)mrr為on時,表示啟動MRR優(yōu)化倔矾,mrr_cost_based標(biāo)記表示是否通過cost based的方式來選擇是否啟動mrr妄均。若將mrr設(shè)為on,mrr_cost_based設(shè)為off哪自,則總是啟用MRR優(yōu)化丰包。

通過參數(shù)read_rnd_buffer_size來控制鍵值的緩沖區(qū)大小,當(dāng)大于該值時壤巷,則執(zhí)行器對已經(jīng)緩存的數(shù)據(jù)根據(jù)RowID進(jìn)行排序邑彪,并通過RowID來取得行數(shù)據(jù)。該值默認(rèn)為256K

** Index Condition Pushdown(ICP)優(yōu)化 **
在進(jìn)行索引查詢時胧华,首先根據(jù)索引來查找記錄寄症,然后再根據(jù)where來過濾記錄。在支持ICP后矩动,MySQL數(shù)據(jù)庫會在取出索引的同時有巧,判斷是否可以進(jìn)行where條件過濾,也就是將where的部分過濾操作放在了存儲引擎層悲没。在某些查詢下篮迎,可以大大減少上層SQL層對記錄的索引,從而提高數(shù)據(jù)庫的整體性能

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市甜橱,隨后出現(xiàn)的幾起案子逊笆,更是在濱河造成了極大的恐慌,老刑警劉巖岂傲,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件难裆,死亡現(xiàn)場離奇詭異,居然都是意外死亡镊掖,警方通過查閱死者的電腦和手機乃戈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亩进,“玉大人偏化,你說我怎么就攤上這事「浜睿” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵驶冒,是天一觀的道長苟翻。 經(jīng)常有香客問我,道長骗污,這世上最難降的妖魔是什么崇猫? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮需忿,結(jié)果婚禮上诅炉,老公的妹妹穿的比我還像新娘。我一直安慰自己屋厘,他們只是感情好涕烧,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著汗洒,像睡著了一般议纯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上溢谤,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天瞻凤,我揣著相機與錄音,去河邊找鬼世杀。 笑死阀参,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瞻坝。 我是一名探鬼主播蛛壳,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了炕吸?” 一聲冷哼從身側(cè)響起伐憾,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赫模,沒想到半個月后树肃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡瀑罗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年胸嘴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斩祭。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡劣像,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出摧玫,到底是詐尸還是另有隱情耳奕,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布诬像,位于F島的核電站屋群,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏坏挠。R本人自食惡果不足惜芍躏,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望降狠。 院中可真熱鬧对竣,春花似錦、人聲如沸榜配。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芥牌。三九已至烦味,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間壁拉,已是汗流浹背谬俄。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留弃理,地道東北人溃论。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像痘昌,于是被迫代替她去往敵國和親钥勋。 傳聞我的和親對象是個殘疾皇子炬转,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

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