談?wù)?MySQL 索引 是如何提高 查詢效率 的谤狡?

前言

我們都知道當(dāng)查詢數(shù)據(jù)庫變慢時灸眼,需要建索引去優(yōu)化。但是只知道索引能優(yōu)化顯然是不夠的墓懂,我們更應(yīng)該知道索引的原理焰宣,因為不是加了索引就一定會提升性能。那么接下來就一起探索MYSQL索引的原理吧

什么是索引

索引其實是一種能高效幫助MYSQL獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)捕仔,通常保存在磁盤文件中匕积,好比一本書的目錄,能加快數(shù)據(jù)庫的查詢速度榜跌。除此之外闪唆,索引是有序的,所以也能提高數(shù)據(jù)的排序效率钓葫。

通常MYSQL的索引包括聚簇索引悄蕾,覆蓋索引,復(fù)合索引础浮,唯一索引帆调,普通索引,通常底層是B+樹的數(shù)據(jù)結(jié)構(gòu)豆同。

總結(jié)一下番刊,索引的優(yōu)勢在于:

  • 提高查詢效率。

  • 降低數(shù)據(jù)排序的成本影锈。

缺點在于:

  • 索引會占用磁盤空間芹务。

  • 索引會降低更新表的效率。因為在更新數(shù)據(jù)時鸭廷,要額外維護索引文件枣抱。

索引的類型

  • 聚簇索引

索引列的值必須是唯一的,并且不能為空靴姿,一個表只能有一個聚簇索引沃但。

  • 唯一索引

索引列的值是唯一的磁滚,值可以為空佛吓。

  • 普通索引

沒有什么限制,允許在定義索引的列中插入重復(fù)值和空值垂攘。

  • 復(fù)合索引

也叫組合索引维雇,用戶可以在多個列上組合建立索引,遵循“最左匹配原則”晒他,在條件允許的情況下使用復(fù)合索引可以替代多個單列索引的使用吱型。

索引的數(shù)據(jù)結(jié)構(gòu)

我們都知道索引的底層數(shù)據(jù)結(jié)構(gòu)采用的是B+樹,但是在講B+樹之前陨仅,要先知道B樹津滞,因為B+樹是在B樹上面進行改進優(yōu)化的铝侵。

首先講一下B樹的特點:

  • B樹的每個節(jié)點都存儲了多個元素,每個內(nèi)節(jié)點都有多個分支触徐。

  • 節(jié)點中元素包含鍵值和數(shù)據(jù)咪鲜,節(jié)點中的鍵值從小到大排序。

  • 父節(jié)點的數(shù)據(jù)不會出現(xiàn)在子節(jié)點中撞鹉。

  • 所有的葉子節(jié)點都在同一層疟丙,葉節(jié)點具有相同的深度。

image

在上面的B樹中鸟雏,假如我們要找值等于18的數(shù)據(jù)享郊,查找路徑就是磁盤塊1->磁盤塊3->磁盤塊8。

過程如下:

第一次磁盤IO:首先加載磁盤塊1到內(nèi)存中孝鹊,在內(nèi)存中遍歷比較炊琉,因為17<18<50,所以走中間P2又活,定位到磁盤塊3温自。

第二次磁盤IO:加載磁盤塊3到內(nèi)存,依然是遍歷比較皇钞,18<25悼泌,所以走左邊P1,定位到磁盤塊8夹界。

第三次磁盤IO:加載磁盤塊8到內(nèi)存馆里,在內(nèi)存中遍歷,18=18可柿,找到18鸠踪,取出data衡未。

如圖所示:

image

如果data存儲的是行數(shù)據(jù)天梧,直接返回,如果存的是磁盤地址則根據(jù)磁盤地址到磁盤中取出數(shù)據(jù)冰蘑∧慷В可以看出B樹的查詢效率是很高的评汰。

B樹存在著什么問題,需要改進優(yōu)化呢痢虹?

第一個問題:B樹在范圍查詢時被去,性能并不理想。假如要查詢13到30之間的數(shù)據(jù)奖唯,查詢到13后又要回到根節(jié)點再去查詢后面的數(shù)據(jù)惨缆,就會產(chǎn)生多次的查詢遍歷。

第二個問題:因為非葉子節(jié)點和葉子節(jié)點都會存儲數(shù)據(jù),所以占用的空間大坯墨,一個頁可存儲的數(shù)據(jù)量就會變少寂汇,樹的高度就會變高,磁盤的IO次數(shù)就會變多捣染。

基于以上兩個問題健无,就出現(xiàn)了B樹的升級版,B+樹液斜。 B+樹與B樹最大的區(qū)別在于兩點:

  • B+樹只有葉子節(jié)點存儲數(shù)據(jù)累贤,非葉子節(jié)點只存儲鍵值。而B樹的非葉子節(jié)點和葉子節(jié)點都會存儲數(shù)據(jù)少漆。

  • B+樹的最底層的葉子節(jié)點會形成一個雙向有序鏈表臼膏,而B樹不會。

如圖所示:

image

B+樹的等值查詢過程是怎么樣的示损?

如果在B+樹中進行等值查詢渗磅,比如查詢等于13的數(shù)據(jù)。

查詢路徑為:磁盤塊1->磁盤塊2->磁盤塊6检访。

  • 第一次IO:加載磁盤塊1始鱼,在內(nèi)存中遍歷比較,13<17脆贵,走左邊医清,找到磁盤塊2。

  • 第二次IO:加載磁盤塊2卖氨,在內(nèi)存中遍歷比較会烙,10<13<15,走中間筒捺,找到磁盤塊6柏腻。

  • 第三次IO:加載磁盤塊6,依次遍歷系吭,找到13=13五嫂,取出data。

所以B+樹在等值查詢的效率是很高的肯尺。

B+樹的范圍查詢過程又是怎么樣呢沃缘?

比如我們要進行范圍查詢,查詢大于5并且小于15的數(shù)據(jù)蟆盹。

查詢路徑為:磁盤塊1->磁盤塊2->磁盤塊5->磁盤塊6

  • 第一次IO:加載磁盤塊1孩灯,比較得出5<17闺金,然后走左邊逾滥,找到磁盤塊2。

  • 第二次IO:加載磁盤塊2,比較5<10寨昙,然后還是走左邊讥巡,找到磁盤塊5

  • 第三次IO:加載磁盤塊5,然后找大于5的數(shù)據(jù)舔哪。

  • 第四次IO:由于最底層是有序的雙向鏈表欢顷,所以繼續(xù)往右遍歷即可,直到不符合小于15的數(shù)據(jù)為止捉蚤。

過程如圖所示:

image

所以在范圍查詢的時候抬驴,是不需要像B樹一樣,再回到根節(jié)點缆巧,這就是底層采用雙向鏈表的好處布持。

所以B+樹的優(yōu)勢在于,能保證等值查詢和范圍查詢的快速查找陕悬。

InnoDB索引

我們常用的MySQL存儲引擎一般是InnoDB题暖,所以接下來講講幾種不同的索引的底層數(shù)據(jù)結(jié)構(gòu),以及查找過程捉超。

聚簇索引

前面講過胧卤,每個InnoDB表有且僅有一個聚簇索引。除此之外拼岳,聚簇索引在表的創(chuàng)建有以下幾點規(guī)則:

  • 在表中枝誊,如果定義了主鍵,InnoDB會將主鍵索引作為聚簇索引惜纸。

  • 如果沒有定義主鍵侧啼,則會選擇第一個不為NULL的唯一索引列作為聚簇索引。

  • 如果以上兩個都沒有堪簿。InnoDB 會使用一個6 字節(jié)長整型的隱式字段 ROWID字段構(gòu)建聚簇索引痊乾。該ROWID字段會在插入新行時自動遞增。

除了聚簇索引之外的索引都稱為非聚簇索引椭更,區(qū)別在于哪审,聚簇索引的葉子節(jié)點存儲的數(shù)據(jù)是整行數(shù)據(jù),而非聚簇索引存儲的是該行的主鍵值虑瀑。

比如有一張user表湿滓,如圖所示:

image

底層的數(shù)據(jù)結(jié)構(gòu)就像這樣:

image.png

當(dāng)我們用主鍵值去查詢的時候,查詢效率是很快的舌狗,因為可以直接返回數(shù)據(jù)叽奥。

image.png

普通索引

也就是用得最多的一種索引,比如我要為user表的age列創(chuàng)建索引痛侍,SQL語句可以這樣寫:


CREATE INDEX INDEX_USER_AGE ON `user`(age);

普通索引屬于非聚簇索引朝氓,所以葉子節(jié)點存儲的是主鍵值,底層的數(shù)據(jù)結(jié)構(gòu)大概長這個樣子:

image

比如要查詢age=33的數(shù)據(jù),那么首先查到磁盤塊7的age=33的數(shù)據(jù)赵哲,獲取到主鍵值待德,主鍵值為4。

接著再通過主鍵值等于4枫夺,查詢到該行的數(shù)據(jù)将宪。所以總得來說,底層會進行兩次查詢橡庞。

這種先通過查詢主鍵值较坛,再通過主鍵值查詢到數(shù)據(jù)的過程就叫做回表查詢。

覆蓋索引

既然上面提到了回表查詢扒最,那么自然而然會想到燎潮,有沒有什么辦法能避免回表查詢呢?答案肯定是有的扼倘,那就是使用覆蓋索引确封。

覆蓋索引不是一種索引的類型,而是一種使用索引的方式再菊。假設(shè)你需要查詢的列是建立了索引爪喘,查詢的結(jié)果在索引列上就能獲取,那就可以用覆蓋索引纠拔。

比如上面的例子秉剑,我們通過age=33查詢,我需要查詢的結(jié)果就只要age這一列稠诲,那就可以用到覆蓋索引侦鹏,如圖所示:

image.png

使用到覆蓋索引的話,就能避免回表查詢臀叙,所以在寫SQL語句時盡量不要寫SELECT *略水。

總結(jié)

這篇文章主要講的是索引的類型,索引的數(shù)據(jù)結(jié)構(gòu)劝萤,以及InnoDB表中常用的幾種索引渊涝。當(dāng)然,除了上述講的這些之外床嫌,還有很多關(guān)于索引的知識跨释,比如索引失效的場景,索引創(chuàng)建的原則等等厌处,由于篇幅過長鳖谈,留著以后再講。

原文鏈接:https://juejin.cn/post/6968068659260424229

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阔涉,一起剝皮案震驚了整個濱河市缆娃,隨后出現(xiàn)的幾起案子捷绒,更是在濱河造成了極大的恐慌,老刑警劉巖龄恋,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疙驾,死亡現(xiàn)場離奇詭異凶伙,居然都是意外死亡郭毕,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門函荣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來显押,“玉大人,你說我怎么就攤上這事傻挂〕吮” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵金拒,是天一觀的道長兽肤。 經(jīng)常有香客問我,道長绪抛,這世上最難降的妖魔是什么资铡? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮幢码,結(jié)果婚禮上笤休,老公的妹妹穿的比我還像新娘。我一直安慰自己症副,他們只是感情好店雅,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贞铣,像睡著了一般闹啦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辕坝,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天亥揖,我揣著相機與錄音,去河邊找鬼圣勒。 笑死费变,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的圣贸。 我是一名探鬼主播挚歧,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吁峻!你這毒婦竟也來了滑负?” 一聲冷哼從身側(cè)響起在张,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎矮慕,沒想到半個月后帮匾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡痴鳄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年瘟斜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痪寻。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡螺句,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出橡类,到底是詐尸還是另有隱情蛇尚,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布顾画,位于F島的核電站取劫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏研侣。R本人自食惡果不足惜谱邪,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望义辕。 院中可真熱鬧虾标,春花似錦、人聲如沸灌砖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽基显。三九已至蘸吓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撩幽,已是汗流浹背库继。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留窜醉,地道東北人宪萄。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像榨惰,于是被迫代替她去往敵國和親拜英。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

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