天天看點

這樣了解 Mysql 索引,阿裡面試官也給你點贊

作者:慕楓技術筆記

#頭條創作挑戰賽#

引言

索引是 Mysql 的一塊硬骨頭,但是對于程式猿來說又是十分重要的基礎技能。在平常的項目開發中,它是重要的 SQL 優化手段。在求職面試中,它是面試官常常用來考察求職者資料庫性能優化方面的重要考量。是以透徹地掌握索引原理,并能夠将其運用到資料庫查詢實戰是每個程式猿必備的能力。本文将從索引原理、索引設計原則方面闡述 Mysql 索引。相信閱讀完本文之後,在 Mysql 索引查詢資料了解這塊完全可以征服阿裡面試官。準備好了嗎?我們發車了。

這樣了解 Mysql 索引,阿裡面試官也給你點贊

索引原理

在進行索引設計以及優化之前,我們先深入了解下索引的原理。因為所有的設計以及優化一定是建立在你對原理的透徹了解的基礎上。

很多人都知道,在進行 SQL 查詢時,同樣一張表、同樣的資料。不加索引以及加索引進行資料查詢。兩者差别很多。那麼到底是為什麼有這種差距。簡單來說,如果把業務資料比作為一本字典的話,那麼索引就是這本字典的目錄。如果我讓你查一個字,在你不使用目錄查的時候,那隻能一頁一頁地翻,運氣不好的話可能要翻到最後一頁才能查到想要的字,這就是傳說中的全表掃描。但是如果我們通過目錄來查找,那麼可以很快定位字所在頁,進而查找到對應的字。看到了吧,索引的威力就在于提高資料查詢的效率。好了,現在我們對于索引有了感性的認識。那麼我們接下來就深入了解下。

我們都知道在 Mysql 中索引的資料結構是 B+樹(這裡不再說明 B 樹、Hash 索引等結構的優劣,不是本文的重點),那麼我們就一步一步來看看,索引在磁盤中的 B+樹是怎麼長成的。

1、資料頁

在日常的項目開發中,我們的業務資料大部分都存在關系型資料中。那麼資料庫中各個表中的資料最終也都是存儲在伺服器的硬碟當中的。不知道大家有沒有想過這個資料到底是怎麼存儲的呢?實際上 Mysql 資料庫中我們每天都在使用的資料庫表是對于人來了解的邏輯表。它實際在磁盤當中是通過一頁頁的資料頁進行存儲的。資料頁是磁盤與記憶體互動的基本機關,Mysql 的 Innodb 存儲引擎,實際通過 buffer pool 與磁盤中的資料頁進行互動,而不是直接操作磁盤中的資料頁。資料頁的結構如下圖所示:

這樣了解 Mysql 索引,阿裡面試官也給你點贊

同時相鄰的資料頁之間通過雙向連結清單來維護資料頁之間的互相引用。如下圖所示,橙紅色部分即為資料頁,中間的小框框可以了解為一條條具體的資料。Mysql 的 InnoDB 存儲引擎資料頁大小是 16KB。Mysql 的 Innodb 存儲引擎通過頁号來唯一定位一個資料頁,是以每個資料頁都有自己的頁号。通過上圖可知,每個資料頁都有都有對應的 Page Header,在 Page Header 中儲存了目前資料頁的頁号,以及其下一頁的頁号和上一頁的頁号。

這樣了解 Mysql 索引,阿裡面試官也給你點贊

相鄰的資料之間通過指針進行互相引用,指針标注資料頁的頁号,每個資料頁中存儲了連續的一段資料,每個資料行中的記錄頭部存有下一行記錄真實資料的位址偏移量,簡單了解為擁有指針指向下一行資料的位址。是以在資料頁的内部,實際是關于資料行的單向連結清單。這個單向連結清單是關于主鍵 id 的,從小到大進行排列。

這樣了解 Mysql 索引,阿裡面試官也給你點贊

從上述的資料頁結構可知,每次進行資料插入時 User Records 區域就會變大,相應的的 User Record 區域就會減少。當 User Record 區域消耗完之後,就會發生頁分裂,形成新的資料頁。這裡需要注意的是,如果我們使用的是 Mysql 中的自增主鍵,那麼可以保證按照 id 的增長順序進行資料行排列,但是如果主鍵是我們自己設定的并不是自增長的,那麼有可能出現後面插入的資料的主鍵值小于前面資料的主鍵值,那麼在進行頁分裂的時候,Mysql 會按照主鍵大小重新進行排列。此處不知道大家有沒有疑問,為什麼一定要按照主鍵大小進行排列呢?實際上和後續的資料查詢有關系,資料頁中的資料按照主鍵順序進行排列是索引可以正常運作的基礎。大緻的過程如下圖所示:

這樣了解 Mysql 索引,阿裡面試官也給你點贊

2、頁目錄

每個資料頁都有自己的頁目錄上面頁結構中的 Page Directory,這個頁目錄的作用實際上就是用來進行資料行定位的。資料頁中的資料實際上是按組配置設定的,頁目錄中的不同的槽位,其實是對應了資料頁中的不同的分組,查詢資料時,通過 id 找到對應的槽,再根據對應的槽來知道對應在資料頁中的資料行分組,周遊資料行分組中的資料直到找到對應的資料。

這樣了解 Mysql 索引,阿裡面試官也給你點贊

3、索引原理分析

(1)索引基礎

有了上面兩節的資料頁的基礎知識之後,我們再來探讨索引原理就更加容易了解了。在沒有索引時,資料查詢都是進行全表掃描。周遊查詢資料頁中的每個資料行,再周遊所有的資料頁,知道找到符合條件的資料項。是以查詢效率十分的低下。那麼應該怎麼才能提供資料查詢的效率呢?能不能像字典的目錄一樣,也搞個主鍵目錄來進行資料頁号的定位呢?答案是肯定的,Mysql 實際也正是這麼做的。Mysql 通過主鍵目錄實際就是傳說中的主鍵索引,實作資料的查詢優化。在主鍵目錄中包含了兩個重要元素,一個是資料頁中最小的主鍵,另一個是目前資料頁的頁号。這樣可以通過這個主鍵目錄方面的進行資料查詢。

舉個栗子,如果此時想要查詢主鍵 id=5 的資料,那麼首先在主鍵目錄中進行查找。此時發現主鍵 id=5 大于主鍵 id=1,但是又小于 id=8,那麼就可以确定實際上資料實際是在頁号為 1 的資料頁中的。

當然在實際在 Mysql 中會有很多的資料頁,是以對應的主鍵索引也會很多,那麼此時就需要通過二分查找的方式進行資料頁定位,再查找到對應的資料。

這樣了解 Mysql 索引,阿裡面試官也給你點贊

(2)索引頁

如今當下,各個網際網路公司迅猛發展,對應的業務量也是十分巨大。是以資料庫中的資料量也是十分龐大的。表中的資料幾百萬、上千萬可能很常見,按照上述的主鍵目錄,那麼就需要存儲大量的主鍵與資料頁号。即便是進行二分查找,其資料查詢效率也是比較低的。

Mysql 實際是将索引說句存儲在索引頁中的,當資料量比較大時候,對應的索引也會比較多,是以通過專門的索引頁來存儲索引資料。另外在這些索引頁的上層又通過主鍵與索引頁号來繼續進行索引頁的查詢定位,是以我們得到如下的結構。其中的 id 号指的是對應最小的 id 号。

這樣了解 Mysql 索引,阿裡面試官也給你點贊

如果索引頁中的資料越來越多,索引頁同樣會進行頁分裂。這樣索引頁也就形成了不同的層級,索引頁層、索引頁、資料頁這三個頁資料就形成了我們說的 B+樹。下圖就是索引的 B+樹結構,通過它完成資料查詢效率遠高于全表掃描。B+的葉子節點才會存儲資料,下圖是一種主鍵索引,也叫聚簇索引。其實我們可以看出來,它的根本思想就是分而治之的思想。資料量很大是吧,那我就把資料分成很多的資料頁,資料頁很多是吧,那我就通過索引頁來組織資料頁,索引頁很多是吧,那就再通過索引頁來索引。

這樣了解 Mysql 索引,阿裡面試官也給你點贊

我們再來看下,資料查詢在B+樹中的查詢過程。舉個栗子,如目前需要查詢 id 為 3 的資料,那麼将在索引頁中判斷應該走索引頁為 3 的索引頁。那麼在索引頁為3中繼續判斷id=1應該走索引頁為 1 的索引頁,在索引頁中判斷應該頁号為1的資料頁,在此資料頁中周遊最終查詢到對應的資料。

這樣了解 Mysql 索引,阿裡面試官也給你點贊

以上通過索引頁與資料頁組成的 B+樹就是聚簇索引,當然我們也可以通過其他字段來建立普通索引。知識普通索引會的葉子節點存儲的是對應的主鍵 id,而不是具體的資料,索引會存在回表的問題,即查詢到對應的 id 之後,還需要根據 id 繼續到聚簇索引中查詢具體的資料,通過這樣的操作才能查詢到 select *的所有資料。當然我們可以通過覆寫索引的方式避免這樣的查詢浪費。

總結

本文通過一步步圖解的方式,為大家拆解 Mysql 的 InnoDB 的索引原理,同時建構出對應的 B+樹索引結構。闡述了資料查詢的具體過程。相信大家對于索引這塊有了更加深刻的了解,後面會從實戰的角度出發,分析下如何設計索引以及如何應對索引失效的問題。

我是慕楓,感謝各位小夥伴點贊、收藏和評論,文章持續跟新,我們下期再見!

繼續閱讀