前言
一直想深刻的研究一下mysql的索引原理,奈何工做太忙沒有時間,最近數據量過大,作了好多sql優化...終於,是時候研究一波了。html
索引的本質
聊索引以前,咱們得先知道索引是什麼?有什麼用?目前經常使用的索引是以何種形式呈現的?mysql
MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構。提取句子主幹,就能夠獲得索引的本質:索引是數據結構。算法
咱們知道,查詢在數據庫中常常用到,可是設想,若是沒有索引,每次找到一條數據都須要以時間複雜度爲O(n)去查找,當數據量少的時候沒什麼感受,可是數據量大了之後,這是一個巨大的數字...你的查詢簡直不可直視。因此,咱們須要在數據旁邊,創建一些索引,當咱們要查某些數據的時候,若是對應字段有索引,咱們能夠根據索引找到數據的指向,而後去對應地址拿出數據,豈不很快(和小時候新華字典查的時候好像,哈哈哈)?下面是我在網上找的一個圖,能夠很好的解釋我剛剛描述的。左邊表明存在disk上的數據,右邊表明索引(二叉樹的方式)。sql
能夠看到,咱們在col2上創建了一個索引,當咱們要找col2 = 5 的數據的時候,咱們只須要到索引裏面找到5,根據他的指針指向,就能夠去拿到對應數據了。二叉樹的時間複雜度是O(log2n) ,節約了很多時間!數據庫
可是真是狀況咱們會這樣作,可是不會使用二叉樹去作索引,緣由在這篇文章的後面一點介紹。
要想理解索引理解的更加深入,我以爲須要理解一下計算機的硬盤,內存相關的知識。數據結構
內存/硬盤存取原理
RAM,運行時內存。cpu在與內存交互的時候,好比讀數據:會經過地址總線把要讀取的地址發給RAM,而後RAM讀取完數據再將數據放到數據總線,供CPU讀取,以下圖:mysql索引
好比地址總線傳過來C1,那麼數據總線就會給出相應地址的數據「f」.而且內存速度很快,內存IO基本上不會對速度有影響。優化
DISK,磁盤,也叫外存。咱們知道,數據庫的數據基本上都是放在磁盤裏面。可是磁盤的速度就不像內存那麼快了,咱們簡單的看下磁盤的結構:spa
如上圖:一個磁盤由大小相同且同軸的圓形盤片組成,磁盤能夠轉動(各個磁盤必須同步轉動)。在磁盤的一側有磁頭支架,磁頭支架固定了一組磁頭,每一個磁頭負責存取一個磁盤的內容。磁頭不能轉動,可是能夠沿磁盤半徑方向運動(實際是斜切向運動),每一個磁頭同一時刻也必須是同軸的,即從正上方向下看,全部磁頭任什麼時候候都是重疊的(不過目前已經有多磁頭獨立技術,可不受此限制)。.net
那麼定位數據位置是怎樣的呢?咱們看下面這張圖:
盤片被劃分紅一系列同心環,圓心是盤片中心,每一個同心環叫作一個磁道,全部半徑相同的磁道組成一個柱面。磁道被沿半徑線劃分紅一個個小的段,每一個段叫作一個扇區,每一個扇區是磁盤的最小存儲單元。爲了簡單起見,咱們下面假設磁盤只有一個盤片和一個磁頭。
當須要從磁盤讀取數據時,系統會將數據邏輯地址傳給磁盤,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址,即肯定要讀的數據在哪一個磁道,哪一個扇區。爲了讀取這個扇區的數據,須要將磁頭放到這個扇區上方,爲了實現這一點,磁頭須要移動對準相應磁道,這個過程叫作尋道,所耗費時間叫作尋道時間,而後磁盤旋轉將目標扇區旋轉到磁頭下,這個過程耗費的時間叫作旋轉時間。
因此,磁盤讀取數據耗時 = 尋道時間 + 旋轉時間 (很重要!!!說了這麼多計算機硬件相關,就是爲了這句話...好辛苦...)
基於這個公式,咱們發現,若是順序讀取數據,尋道時間能夠變小或者不須要,只須要旋轉時間該!這就是後面使用B樹的核心緣由之一。
這裏還有一個硬件方面的概念須要解釋一下,就是頁。
計算機科學中著名的局部性原理:當一個數據被用到時,其附近的數據也一般會立刻被使用。因此計算機讀取數據是以頁爲單位的。也就是說,假設我讀取了數據id爲1的數據,可能id=2,3的數據也在這個頁上,也讀取了。
B+樹
那麼什麼是B+樹,mysql爲何會使用B+樹呢?(B+樹懂了再去看B-樹會很簡單,暫不介紹B-樹了)
1.以前介紹過,影響索引速度的瓶頸是IO次數。而檢索一次最多要訪問h個結點裏面的數據,而數據庫在設計的時候,將每一個結點設置成頁的倍數(利用預讀),這樣,一個結點就只用掃描一次就好
2.咱們能夠看到,在B+樹裏面,數據所有存在葉子結點上面。B樹的時間複雜度是O(logdN) 其中d是開度。開度越大,那麼所花費的時間越短。
MySQL索引實現
mysql索引常見的有MyISAM和InnoDB兩個存儲引擎,下面分別分析:
MyISAM
MyISAM引擎使用B+Tree做爲索引結構,葉節點的data域存放的是數據記錄的地址。下圖是MyISAM索引的原理圖:
這裏設表一共有三列,假設咱們以Col1爲主鍵,則圖8是一個MyISAM表的主索引(Primary key)示意。能夠看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是惟一的,而輔助索引的key能夠重複。若是咱們在Col2上創建一個輔助索引,則此索引的結構以下圖所示:
一樣也是一顆B+Tree,data域保存數據記錄的地址。所以,MyISAM中索引檢索的算法爲首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,則取出其data域的值,而後以data域的值爲地址,讀取相應數據記錄。
MyISAM的索引方式也叫作「非彙集」的,之因此這麼稱呼是爲了與InnoDB的彙集索引區分。
InnoDB
雖然InnoDB也使用B+Tree做爲索引結構,但具體實現方式卻與MyISAM大相徑庭。
第一個重大區別是InnoDB的數據文件自己就是索引文件。從上文知道,MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在InnoDB中,表數據文件自己就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,所以InnoDB表數據文件自己就是主索引。
上面是InnoDB主索引(同時也是數據文件)的示意圖,能夠看到葉節點包含了完整的數據記錄。這種索引叫作彙集索引。由於InnoDB的數據文件自己要按主鍵彙集,因此InnoDB要求表必須有主鍵(MyISAM能夠沒有),若是沒有顯式指定,則MySQL系統會自動選擇一個能夠惟一標識數據記錄的列做爲主鍵,若是不存在這種列,則MySQL自動爲InnoDB表生成一個隱含字段做爲主鍵,這個字段長度爲6個字節,類型爲長整形。
第二個與MyISAM索引的不一樣是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB的全部輔助索引都引用主鍵做爲data域。例如,圖11爲定義在Col3上的一個輔助索引:
這裏以英文字符的ASCII碼做爲比較準則。彙集索引這種實現方式使得按主鍵的搜索十分高效,可是輔助索引搜索須要檢索兩遍索引:首先檢索輔助索引得到主鍵,而後用主鍵到主索引中檢索得到記錄。
瞭解不一樣存儲引擎的索引實現方式對於正確使用和優化索引都很是有幫助,例如知道了InnoDB的索引實現後,就很容易明白爲何不建議使用過長的字段做爲主鍵,由於全部輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。再例如,用非單調的字段做爲主鍵在InnoDB中不是個好主意,由於InnoDB數據文件自己是一顆B+Tree,非單調的主鍵會形成在插入新記錄時數據文件爲了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增字段做爲主鍵則是一個很好的選擇。
好了,本文比較深刻的介紹了mysql的索引相關知識,感謝網上相關文檔,本文在寫的過程當中,參考了https://blog.csdn.net/u013967...
https://www.cnblogs.com/aspir...