mysql索引分析

前言

一直想深入的研究一下mysql的索引原理,奈何工作太忙沒有時間,最近數據量過大,做了好多sql優化...終於,是時候研究一波了。

索引的本質

聊索引之前,我們得先知道索引是什麼?有什麼用?目前常用的索引是以何種形式呈現的?

MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構。提取句子主幹,就可以得到索引的本質:索引是數據結構

我們知道,查詢在數據庫中經常用到,但是設想,如果沒有索引,每次找到一條數據都需要以時間複雜度爲O(n)去查找,當數據量少的時候沒什麼感覺,但是數據量大了以後,這是一個巨大的數字...你的查詢簡直不可直視。所以,我們需要在數據旁邊,建立一些索引,當我們要查某些數據的時候,如果對應字段有索引,我們可以根據索引找到數據的指向,然後去對應地址拿出數據,豈不很快(和小時候新華字典查的時候好像,哈哈哈)?下面是我在網上找的一個圖,可以很好的解釋我剛剛描述的。左邊代表存在disk上的數據,右邊代表索引(二叉樹的方式)。

clipboard.png

可以看到,我們在col2上建立了一個索引,當我們要找col2 = 5 的數據的時候,我們只需要到索引裏面找到5,根據他的指針指向,就可以去拿到對應數據了。二叉樹的時間複雜度是O(log2n) ,節約了不少時間!

但是真是情況我們會這樣做,但是不會使用二叉樹去做索引,原因在這篇文章的後面一點介紹。
要想理解索引理解的更加深刻,我覺得需要理解一下計算機的硬盤,內存相關的知識。

內存/硬盤存取原理

RAM,運行時內存。cpu在與內存交互的時候,比如讀數據:會通過地址總線把要讀取的地址發給RAM,然後RAM讀取完數據再將數據放到數據總線,供CPU讀取,如下圖:

clipboard.png

比如地址總線傳過來C1,那麼數據總線就會給出相應地址的數據“f”.並且內存速度很快,內存IO基本上不會對速度有影響。

DISK,磁盤,也叫外存。我們知道,數據庫的數據基本上都是放在磁盤裏面。但是磁盤的速度就不像內存那麼快了,我們簡單的看下磁盤的結構:

clipboard.png

如上圖:一個磁盤由大小相同且同軸的圓形盤片組成,磁盤可以轉動(各個磁盤必須同步轉動)。在磁盤的一側有磁頭支架,磁頭支架固定了一組磁頭,每個磁頭負責存取一個磁盤的內容。磁頭不能轉動,但是可以沿磁盤半徑方向運動(實際是斜切向運動),每個磁頭同一時刻也必須是同軸的,即從正上方向下看,所有磁頭任何時候都是重疊的(不過目前已經有多磁頭獨立技術,可不受此限制)。

那麼定位數據位置是怎樣的呢?我們看下面這張圖:

clipboard.png

盤片被劃分成一系列同心環,圓心是盤片中心,每個同心環叫做一個磁道,所有半徑相同的磁道組成一個柱面。磁道被沿半徑線劃分成一個個小的段,每個段叫做一個扇區,每個扇區是磁盤的最小存儲單元。爲了簡單起見,我們下面假設磁盤只有一個盤片和一個磁頭。

當需要從磁盤讀取數據時,系統會將數據邏輯地址傳給磁盤,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址,即確定要讀的數據在哪個磁道,哪個扇區。爲了讀取這個扇區的數據,需要將磁頭放到這個扇區上方,爲了實現這一點,磁頭需要移動對準相應磁道,這個過程叫做尋道,所耗費時間叫做尋道時間,然後磁盤旋轉將目標扇區旋轉到磁頭下,這個過程耗費的時間叫做旋轉時間

所以,磁盤讀取數據耗時 = 尋道時間 + 旋轉時間 (很重要!!!說了這麼多計算機硬件相關,就是爲了這句話...好辛苦...)
基於這個公式,我們發現,如果順序讀取數據,尋道時間可以變小或者不需要,只需要旋轉時間該!這就是後面使用B樹的核心原因之一。

這裏還有一個硬件方面的概念需要解釋一下,就是
計算機科學中著名的局部性原理:當一個數據被用到時,其附近的數據也通常會馬上被使用。所以計算機讀取數據是以頁爲單位的。也就是說,假設我讀取了數據id爲1的數據,可能id=2,3的數據也在這個頁上,也讀取了。

B+樹

那麼什麼是B+樹,mysql爲什麼會使用B+樹呢?(B+樹懂了再去看B-樹會很簡單,暫不介紹B-樹了)

clipboard.png

1.之前介紹過,影響索引速度的瓶頸是IO次數。而檢索一次最多要訪問h個結點裏面的數據,而數據庫在設計的時候,將每個結點設置成頁的倍數(利用預讀),這樣,一個結點就只用掃描一次就好

2.我們可以看到,在B+樹裏面,數據全部存在葉子結點上面。B樹的時間複雜度是O(logdN) 其中d是開度。開度越大,那麼所花費的時間越短。

MySQL索引實現

mysql索引常見的有MyISAM和InnoDB兩個存儲引擎,下面分別分析:

MyISAM
MyISAM引擎使用B+Tree作爲索引結構,葉節點的data域存放的是數據記錄的地址。下圖是MyISAM索引的原理圖:

clipboard.png

這裏設表一共有三列,假設我們以Col1爲主鍵,則圖8是一個MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重複。如果我們在Col2上建立一個輔助索引,則此索引的結構如下圖所示:

clipboard.png
同樣也是一顆B+Tree,data域保存數據記錄的地址。因此,MyISAM中索引檢索的算法爲首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其data域的值,然後以data域的值爲地址,讀取相應數據記錄。

MyISAM的索引方式也叫做“非聚集”的,之所以這麼稱呼是爲了與InnoDB的聚集索引區分。

InnoDB

雖然InnoDB也使用B+Tree作爲索引結構,但具體實現方式卻與MyISAM截然不同。

第一個重大區別是InnoDB的數據文件本身就是索引文件。從上文知道,MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在InnoDB中,表數據文件本身就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引。

clipboard.png
上面是InnoDB主索引(同時也是數據文件)的示意圖,可以看到葉節點包含了完整的數據記錄。這種索引叫做聚集索引。因爲InnoDB的數據文件本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),如果沒有顯式指定,則MySQL系統會自動選擇一個可以唯一標識數據記錄的列作爲主鍵,如果不存在這種列,則MySQL自動爲InnoDB表生成一個隱含字段作爲主鍵,這個字段長度爲6個字節,類型爲長整形。

第二個與MyISAM索引的不同是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB的所有輔助索引都引用主鍵作爲data域。例如,圖11爲定義在Col3上的一個輔助索引:

clipboard.png
這裏以英文字符的ASCII碼作爲比較準則。聚集索引這種實現方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然後用主鍵到主索引中檢索獲得記錄。

瞭解不同存儲引擎的索引實現方式對於正確使用和優化索引都非常有幫助,例如知道了InnoDB的索引實現後,就很容易明白爲什麼不建議使用過長的字段作爲主鍵,因爲所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。再例如,用非單調的字段作爲主鍵在InnoDB中不是個好主意,因爲InnoDB數據文件本身是一顆B+Tree,非單調的主鍵會造成在插入新記錄時數據文件爲了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增字段作爲主鍵則是一個很好的選擇。

好了,本文比較深入的介紹了mysql的索引相關知識,感謝網上相關文檔,本文在寫的過程中,參考了https://blog.csdn.net/u013967...
https://www.cnblogs.com/aspir...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章