阿里面試:MySQL的索引查詢效率到底有多高?

我相信大家在數(shù)據(jù)庫(kù)優(yōu)化的時(shí)候都會(huì)說(shuō)到索引缨硝,我也不例外摩钙,大家也基本上能對(duì)數(shù)據(jù)結(jié)構(gòu)的優(yōu)化回答個(gè)一二三,以及頁(yè)緩存之類的都能扯上幾句查辩,但是有一次阿里P9的一個(gè)面試問我:你能從計(jì)算機(jī)層面開始說(shuō)一下一個(gè)索引數(shù)據(jù)加載的流程么胖笛?(就是想讓我聊IO)

我當(dāng)場(chǎng)就去世了…因?yàn)橛?jì)算機(jī)網(wǎng)絡(luò)和操作系統(tǒng)的基礎(chǔ)知識(shí)真的是我的盲區(qū)网持,不過(guò)后面我惡補(bǔ)了,廢話不多說(shuō)长踊,我們就從計(jì)算機(jī)加載數(shù)據(jù)聊起功舀,講一下?lián)Q個(gè)角度聊索引。

正文

MySQL的索引本質(zhì)上是一種數(shù)據(jù)結(jié)構(gòu)

讓我們先來(lái)了解一下計(jì)算機(jī)的數(shù)據(jù)加載身弊。

磁盤IO和預(yù)讀:

先說(shuō)一下磁盤IO辟汰,磁盤讀取數(shù)據(jù)靠的是機(jī)械運(yùn)動(dòng),每一次讀取數(shù)據(jù)需要尋道阱佛、尋點(diǎn)莉擒、拷貝到內(nèi)存三步操作。

尋道時(shí)間是磁臂移動(dòng)到指定磁道所需要的時(shí)間瘫絮,一般在5ms以下涨冀;

尋點(diǎn)是從磁道中找到數(shù)據(jù)存在的那個(gè)點(diǎn),平均時(shí)間是半圈時(shí)間麦萤,如果是一個(gè)7200轉(zhuǎn)/min的磁盤鹿鳖,尋點(diǎn)時(shí)間平均是600000/7200/2=4.17ms;

拷貝到內(nèi)存的時(shí)間很快壮莹,和前面兩個(gè)時(shí)間比起來(lái)可以忽略不計(jì)翅帜,所以一次IO的時(shí)間平均是在9ms左右。聽起來(lái)很快命满,但數(shù)據(jù)庫(kù)百萬(wàn)級(jí)別的數(shù)據(jù)過(guò)一遍就達(dá)到了9000s涝滴,顯然就是災(zāi)難級(jí)別的了。

考慮到磁盤IO是非常高昂的操作胶台,計(jì)算機(jī)操作系統(tǒng)做了預(yù)讀的優(yōu)化歼疮,當(dāng)一次IO時(shí),不光把當(dāng)前磁盤地址的數(shù)據(jù)诈唬,而是把相鄰的數(shù)據(jù)也都讀取到內(nèi)存緩沖區(qū)內(nèi)韩脏,因?yàn)楫?dāng)計(jì)算機(jī)訪問一個(gè)地址的數(shù)據(jù)的時(shí)候,與其相鄰的數(shù)據(jù)也會(huì)很快被訪問到铸磅。

每一次IO讀取的數(shù)據(jù)我們稱之為一頁(yè)(page)赡矢,具體一頁(yè)有多大數(shù)據(jù)跟操作系統(tǒng)有關(guān),一般為4k或8k阅仔,也就是我們讀取一頁(yè)內(nèi)的數(shù)據(jù)時(shí)候吹散,實(shí)際上才發(fā)生了一次IO。

(突然想到個(gè)我剛畢業(yè)被問過(guò)的問題八酒,在64位的操作系統(tǒng)中空民,Java中的int類型占幾個(gè)字節(jié)?最大是多少丘跌?為什么袭景?)

那我們想要優(yōu)化數(shù)據(jù)庫(kù)查詢,就要盡量減少磁盤的IO操作闭树,所以就出現(xiàn)了索引耸棒。

索引是什么?

MySQL官方對(duì)索引的定義為:索引(Index)是幫助MySQL高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)报辱。

MySQL中常用的索引在物理上分兩類与殃,B-樹索引和哈希索引。

本次主要講BTree索引碍现。

BTree索引

BTree又叫多路平衡查找樹幅疼,一顆m叉的BTree特性如下:

樹中每個(gè)節(jié)點(diǎn)最多包含m個(gè)孩子。

除根節(jié)點(diǎn)與葉子節(jié)點(diǎn)外昼接,每個(gè)節(jié)點(diǎn)至少有[ceil(m/2)]個(gè)孩子(ceil()為向上取整)爽篷。

若根節(jié)點(diǎn)不是葉子節(jié)點(diǎn),則至少有兩個(gè)孩子慢睡。

所有的葉子節(jié)點(diǎn)都在同一層逐工。

每個(gè)非葉子節(jié)點(diǎn)由n個(gè)key與n+1個(gè)指針組成,其中[ceil(m/2)-1] <= n <= m-1 漂辐。

這是一個(gè)3叉(只是舉例泪喊,真實(shí)會(huì)有很多叉)的BTree結(jié)構(gòu)圖,每一個(gè)方框塊我們稱之為一個(gè)磁盤塊或者叫做一個(gè)block塊髓涯,這是操作系統(tǒng)一次IO往內(nèi)存中讀的內(nèi)容袒啼,一個(gè)塊對(duì)應(yīng)四個(gè)扇區(qū),紫色代表的是磁盤塊中的數(shù)據(jù)key纬纪,黃色代表的是數(shù)據(jù)data蚓再,藍(lán)色代表的是指針p,指向下一個(gè)磁盤塊的位置包各。

來(lái)模擬下查找key為29的data的過(guò)程:

1对途、根據(jù)根結(jié)點(diǎn)指針讀取文件目錄的根磁盤塊1倦踢√钐В【磁盤IO操作1次

2、磁盤塊1存儲(chǔ)17衬横,35和三個(gè)指針數(shù)據(jù)按声。我們發(fā)現(xiàn)17<29<35膳犹,因此我們找到指針p2。

3签则、根據(jù)p2指針须床,我們定位并讀取磁盤塊3〗チ眩【磁盤IO操作2次

4豺旬、磁盤塊3存儲(chǔ)26钠惩,30和三個(gè)指針數(shù)據(jù)。我們發(fā)現(xiàn)26<29<30族阅,因此我們找到指針p2篓跛。

5、根據(jù)p2指針坦刀,我們定位并讀取磁盤塊8愧沟。【磁盤IO操作3次

6鲤遥、磁盤塊8中存儲(chǔ)28沐寺,29。我們找到29盖奈,獲取29所對(duì)應(yīng)的數(shù)據(jù)data混坞。

由此可見,BTree索引使每次磁盤I/O取到內(nèi)存的數(shù)據(jù)都發(fā)揮了作用钢坦,從而提高了查詢效率拔第。

但是有沒有什么可優(yōu)化的地方呢?

我們從圖上可以看到场钉,每個(gè)節(jié)點(diǎn)中不僅包含數(shù)據(jù)的key值蚊俺,還有data值。而每一個(gè)頁(yè)的存儲(chǔ)空間是有限的逛万,如果data數(shù)據(jù)較大時(shí)將會(huì)導(dǎo)致每個(gè)節(jié)點(diǎn)(即一個(gè)頁(yè))能存儲(chǔ)的key的數(shù)量很小泳猬,當(dāng)存儲(chǔ)的數(shù)據(jù)量很大時(shí)同樣會(huì)導(dǎo)致B-Tree的深度較大,增大查詢時(shí)的磁盤I/O次數(shù)宇植,進(jìn)而影響查詢效率得封。

B+Tree索引

B+Tree是在B-Tree基礎(chǔ)上的一種優(yōu)化,使其更適合實(shí)現(xiàn)外存儲(chǔ)索引結(jié)構(gòu)指郁。在B+Tree中忙上,所有數(shù)據(jù)記錄節(jié)點(diǎn)都是按照鍵值大小順序存放在同一層的葉子節(jié)點(diǎn)上,而非葉子節(jié)點(diǎn)上只存儲(chǔ)key值信息闲坎,這樣可以大大加大每個(gè)節(jié)點(diǎn)存儲(chǔ)的key值數(shù)量疫粥,降低B+Tree的高度。

B+Tree相對(duì)于B-Tree有幾點(diǎn)不同:

非葉子節(jié)點(diǎn)只存儲(chǔ)鍵值信息腰懂, 數(shù)據(jù)記錄都存放在葉子節(jié)點(diǎn)中梗逮, 將上一節(jié)中的B-Tree優(yōu)化,由于B+Tree的非葉子節(jié)點(diǎn)只存儲(chǔ)鍵值信息绣溜,所以B+Tree的高度可以被壓縮到特別的低慷彤。

具體的數(shù)據(jù)如下:

InnoDB存儲(chǔ)引擎中頁(yè)的大小為16KB,一般表的主鍵類型為INT(占用4個(gè)字節(jié))或BIGINT(占用8個(gè)字節(jié)),指針類型也一般為4或8個(gè)字節(jié)底哗,也就是說(shuō)一個(gè)頁(yè)(B+Tree中的一個(gè)節(jié)點(diǎn))中大概存儲(chǔ)16KB/(8B+8B)=1K個(gè)鍵值(因?yàn)槭枪乐邓晁撸瑸榉奖阌?jì)算,這里的K取值為〖10〗^3)跋选。

也就是說(shuō)一個(gè)深度為3的B+Tree索引可以維護(hù)10^3 * 10^3 * 10^3 = 10億 條記錄涕癣。(這種計(jì)算方式存在誤差,而且沒有計(jì)算葉子節(jié)點(diǎn)野建,如果計(jì)算葉子節(jié)點(diǎn)其實(shí)是深度為4了)

我們只需要進(jìn)行三次的IO操作就可以從10億條數(shù)據(jù)中找到我們想要的數(shù)據(jù)属划,比起最開始的百萬(wàn)數(shù)據(jù)9000秒不知道好了多少個(gè)華萊士了恬叹。

而且在B+Tree上通常有兩個(gè)頭指針候生,一個(gè)指向根節(jié)點(diǎn),另一個(gè)指向關(guān)鍵字最小的葉子節(jié)點(diǎn)绽昼,而且所有葉子節(jié)點(diǎn)(即數(shù)據(jù)節(jié)點(diǎn))之間是一種鏈?zhǔn)江h(huán)結(jié)構(gòu)唯鸭。所以我們除了可以對(duì)B+Tree進(jìn)行主鍵的范圍查找和分頁(yè)查找,還可以從根節(jié)點(diǎn)開始硅确,進(jìn)行隨機(jī)查找目溉。

數(shù)據(jù)庫(kù)中的B+Tree索引可以分為聚集索引(clustered index)和輔助索引(secondary index)。

上面的B+Tree示例圖在數(shù)據(jù)庫(kù)中的實(shí)現(xiàn)即為聚集索引菱农,聚集索引的B+Tree中的葉子節(jié)點(diǎn)存放的是整張表的行記錄數(shù)據(jù)缭付,輔助索引與聚集索引的區(qū)別在于輔助索引的葉子節(jié)點(diǎn)并不包含行記錄的全部數(shù)據(jù),而是存儲(chǔ)相應(yīng)行數(shù)據(jù)的聚集索引鍵循未,即主鍵陷猫。

當(dāng)通過(guò)輔助索引來(lái)查詢數(shù)據(jù)時(shí),InnoDB存儲(chǔ)引擎會(huì)遍歷輔助索引找到主鍵的妖,然后再通過(guò)主鍵在聚集索引中找到完整的行記錄數(shù)據(jù)绣檬。

不過(guò),雖然索引可以加快查詢速度嫂粟,提高 MySQL 的處理性能娇未,但是過(guò)多地使用索引也會(huì)造成以下弊端

創(chuàng)建索引和維護(hù)索引要耗費(fèi)時(shí)間,這種時(shí)間隨著數(shù)據(jù)量的增加而增加星虹。

除了數(shù)據(jù)表占數(shù)據(jù)空間之外零抬,每一個(gè)索引還要占一定的物理空間。如果要建立聚簇索引宽涌,那么需要的空間就會(huì)更大媚值。

當(dāng)對(duì)表中的數(shù)據(jù)進(jìn)行增加、刪除和修改的時(shí)候护糖,索引也要?jiǎng)討B(tài)地維護(hù)褥芒,這樣就降低了數(shù)據(jù)的維護(hù)速度。

注意:索引可以在一些情況下加速查詢,但是在某些情況下锰扶,會(huì)降低效率献酗。

索引只是提高效率的一個(gè)因素,因此在建立索引的時(shí)候應(yīng)該遵循以下原則:

在經(jīng)常需要搜索的列上建立索引坷牛,可以加快搜索的速度罕偎。

在作為主鍵的列上創(chuàng)建索引,強(qiáng)制該列的唯一性京闰,并組織表中數(shù)據(jù)的排列結(jié)構(gòu)颜及。

在經(jīng)常使用表連接的列上創(chuàng)建索引,這些列主要是一些外鍵蹂楣,可以加快表連接的速度俏站。

在經(jīng)常需要根據(jù)范圍進(jìn)行搜索的列上創(chuàng)建索引,因?yàn)樗饕呀?jīng)排序痊土,所以其指定的范圍是連續(xù)的肄扎。

在經(jīng)常需要排序的列上創(chuàng)建索引,因?yàn)樗饕呀?jīng)排序赁酝,所以查詢時(shí)可以利用索引的排序犯祠,加快排序查詢。

在經(jīng)常使用 WHERE 子句的列上創(chuàng)建索引酌呆,加快條件的判斷速度衡载。

現(xiàn)在大家知道索引為啥能這么快了吧,其實(shí)就是一句話隙袁,通過(guò)索引的結(jié)構(gòu)最大化的減少數(shù)據(jù)庫(kù)的IO次數(shù)痰娱,畢竟,一次IO的時(shí)間真的是太久了藤乙。猜揪。。

總結(jié)

就面試而言很多知識(shí)其實(shí)我們可以很容易就掌握了坛梁,但是要以學(xué)習(xí)為目的而姐,你會(huì)發(fā)現(xiàn)很多東西我們得深入到計(jì)算機(jī)基礎(chǔ)上才能發(fā)現(xiàn)其中奧秘,很多人問我怎么記住這么多東西划咐,其實(shí)學(xué)習(xí)本身就是一個(gè)很無(wú)奈的東西拴念,既然我們不能不學(xué)那為啥不好好學(xué)?去學(xué)會(huì)享受呢褐缠?最近我也在惡補(bǔ)基礎(chǔ)政鼠,后面我會(huì)開始更新計(jì)算機(jī)基礎(chǔ)和網(wǎng)絡(luò)相關(guān)的知識(shí)的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末队魏,一起剝皮案震驚了整個(gè)濱河市公般,隨后出現(xiàn)的幾起案子万搔,更是在濱河造成了極大的恐慌,老刑警劉巖官帘,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞬雹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡刽虹,警方通過(guò)查閱死者的電腦和手機(jī)酗捌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)涌哲,“玉大人胖缤,你說(shuō)我怎么就攤上這事》Щ” “怎么了哪廓?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)稍刀。 經(jīng)常有香客問我撩独,道長(zhǎng)敞曹,這世上最難降的妖魔是什么账月? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮澳迫,結(jié)果婚禮上局齿,老公的妹妹穿的比我還像新娘。我一直安慰自己橄登,他們只是感情好抓歼,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拢锹,像睡著了一般谣妻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卒稳,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天蹋半,我揣著相機(jī)與錄音,去河邊找鬼充坑。 笑死减江,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捻爷。 我是一名探鬼主播辈灼,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼也榄!你這毒婦竟也來(lái)了巡莹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎降宅,沒想到半個(gè)月后俐芯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钉鸯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年吧史,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唠雕。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贸营,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出岩睁,到底是詐尸還是另有隱情钞脂,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布捕儒,位于F島的核電站冰啃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏刘莹。R本人自食惡果不足惜阎毅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望点弯。 院中可真熱鬧扇调,春花似錦、人聲如沸抢肛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)捡絮。三九已至熬芜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間福稳,已是汗流浹背涎拉。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留灵寺,地道東北人曼库。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像略板,于是被迫代替她去往敵國(guó)和親毁枯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345