一次 MySQL 索引面試,被面試官懟的體無完膚!

一次 MySQL 索引面試,被面試官懟的體無完膚!

以前有過一次面試,關於MySQL索引的原理及使用被面試官懟的體無完膚,立志要總結一番,而後一直沒有時間(實際上是懶……),準備好了嗎?python


20a59ca99fc80f8d92eb3849e1efd6ac.jpg


索引是什麼?

數據庫索引,是數據庫管理系統(DBMS)中一個排序的數據結構,它能夠對數據庫表中一列或多列的值進行排序,以協助更加快速的訪問數據庫表中特定的數據。通俗的說,咱們能夠把數據庫索引比作是一本書前面的目錄,它能加快數據庫的查詢速度。mysql

爲何須要索引?

思考:如何在一個圖書館中找到一本書? 設想一下,假如在圖書館中沒有其餘輔助手段,只能一條道走到黑,一本書一本書的找,通過3個小時的連續查找,終於找到了你須要看的那本書,但此時天都黑了。爲了不這樣的事情,每一個圖書館才都配備了一套圖書館管理系統,你們要找書籍的話,先在系統上查找到書籍所在的房屋編號、圖書架編號還有書在圖書架幾層的那個方位,而後就能夠直接大搖大擺的去取書了,就能夠很快速的找到咱們所須要的書籍。索引就是這個原理,它能夠幫助咱們快速的檢索數據。面試

通常的應用系統對數據庫的操做,遇到最多、最容易出問題是一些複雜的查詢操做,當數據庫中數據量很大時,查找數據就會變得很慢,這樣就很影響整個應用系統的效率,咱們就可使用索引來提升數據庫的查詢效率。算法

B-Tree和B+Tree

目前大部分數據庫系統及文件系統都採用B-Tree或其變種B+Tree做爲索引結構, 我在這裏分別講一下:sql

B-Tree

即B樹,注意(不是B減樹),B樹是一種多路搜索樹。使用B-Tree結構能夠顯著減小定位記錄時所經歷的中間過程,從而加快存取速度。數據庫

B-Tree有以下一些特徵數據結構

  1. 定義任意非葉子結點最多隻有M個子節點,且M>2。
  2. 根結點的兒子數爲[2, M]。
  3. 除根結點之外的非葉子結點的兒子數爲[M/2, M], 向上取整 。
  4. 每一個結點存放至少M/2-1(取上整)和至多M-1個關鍵字;(至少2個關鍵字)。
  5. 非葉子結點的關鍵字個數=指向兒子的指針個數-1。
  6. 非葉子結點的關鍵字:K[1], K[2], …, K[M-1],且K[i] <= K[i+1]。
  7. 非葉子結點的指針:P[1], P[2], …,P[M](其中P[1]指向關鍵字小於K[1]的子樹,P[M]指向關鍵字大於K[M-1]的子樹,其它P[i]指向關鍵字屬於(K[i-1], K[i])的子樹)。
  8. 全部葉子結點位於同一層。

有關b樹的一些特性ide

  1. 關鍵字集合分佈在整顆樹的全部結點之中;
  2. 任何一個關鍵字出現且只出如今一個結點中;
  3. 搜索有可能在非葉子結點結束;
  4. 其搜索性能等價於在關鍵字全集內作一次二分查找。

B樹的搜索:從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,若是命中則結束,不然進入查詢關鍵字所屬範圍的兒子結點;重複執行這個操做,直到所對應的節點指針爲空,或者已是是葉子結點。函數

例以下面一個B樹,那麼查找元素43的過程以下:性能

根據根節點指針找到1八、37所在節點,把此節點讀入內存,進行第一次磁盤IO,此時發現43>37,找到指針p3。

根據指針p3,找到4二、51所在節點,把此節點讀入內存,進行第二次磁盤IO,此時發現42<43<51,找到指針p2。

根據指針p2,找到4三、46所在節點,把此節點讀入內存,進行第三次磁盤IO,此時咱們就已經查到了元素43。

在此過程總共進行了三次磁盤IO。


d62a328f5845f47f7d14d822a4f98dd9.jpeg


B+Tree

B+Tree屬於B-Tree的變種。與B-Tree相比,B+Tree有如下不一樣點:

  1. 有n棵子樹的非葉子結點中含有n個關鍵字(B樹是n-1個),即非葉子結點的子樹指針與關鍵字個數相同。這些關鍵字不保存數據,只用來索引,全部數據都保存在葉子節點(B樹是每一個關鍵字都保存數據)。
  2. 全部的葉子結點中包含了所有關鍵字的信息,及指向含這些關鍵字記錄的指針,且葉子結點自己依關鍵字的大小自小而大順序連接。
  3. 全部的非葉子結點能夠當作是葉子節點的索引部分。
  4. 同一個數字會在不一樣節點中重複出現,根節點的最大元素就是b+樹的最大元素。


a6c77e8a6f7d586e9d3f666a539b1688.jpeg


相對B樹,B+樹作索引的優點

  1. B+樹的磁盤IO代價更低: B+樹非葉子節點沒有指向數據行的指針,因此相同的磁盤容量存儲的節點數更多,相應的IO讀寫次數確定減小了。
  2. B+樹的查詢效率更加穩定:因爲全部數據都存於葉子節點。全部關鍵字查詢的路徑長度相同,每個數據的查詢效率至關。
  3. 全部的葉子節點造成了一個有序鏈表,更加便於查找。

關於MySQL的兩種經常使用存儲引擎MyISAM和InnoDB的索引均以B+樹做爲數據結構,兩者卻有不一樣(這裏只說兩者索引的區別)。

MyISAM索引和Innodb索引的區別

MyISAM使用B+樹做爲索引結構,葉節點葉節點的data域保存的是存儲數據的地址主鍵索引key值惟一,輔助索引key能夠重複,兩者在結構上相同。 所以,MyISAM中索引檢索的算法爲首先按照B+Tree搜索算法搜索索引,若是要找的Key存在,則取出其data域的值,而後以data域的值爲地址,去讀取相應數據記錄 。所以,索引文件和數據文件是分開的,從索引中檢索到的是數據的地址,而不是數據。

Innodb也是用B+樹做爲索引結構,但具體實現方式卻與MyISAM大相徑庭,首先,數據表自己就是按照b+樹組織,因此數據文件自己就是主鍵索引文件。葉節點key值爲數據表的主鍵,data域爲完整的數據記錄,所以InnoDB表數據文件自己就是主鍵索引(這也就是MyISAM能夠容許沒有主鍵,可是Innodb必須有主鍵的緣由)。第二個與MyISAM索引的不一樣是InnoDB的輔助索引的data域存儲相應數據記錄的主鍵值而不是地址。換句話說,InnoDB的全部輔助索引都引用主鍵做爲data域。

索引類型

普通索引:(由關鍵字KEY或INDEX定義的索引)的惟一任務是加快對數據的訪問速度。

惟一索引: 普通索引容許被索引的數據列包含重複的值,而惟一索引不容許,可是能夠爲null。因此任務是保證訪問速度和避免數據出現重複。

主鍵索引:在主鍵字段建立的索引,一張表只有一個主鍵索引。

組合索引:多列值組成一個索引,專門用於組合搜索。

全文索引:對文本的內容進行分詞,進行搜索。(MySQL5.6及之後的版本,MyISAM和InnoDB存儲引擎均支持全文索引。)

索引的使用策略及優缺點

使用索引

主鍵自動創建惟一索引。
常常做爲查詢條件在WHERE或者ORDER BY 語句中出現的列要創建索引。
查詢中與其餘表關聯的字段,外鍵關係創建索引。
常常用於聚合函數的列要創建索引,如min(),max()等的聚合函數。

不使用索引

常常增刪改的列不要創建索引。
有大量重複的列不創建索引。
表記錄太少不要創建索引,由於數據較少,可能查詢所有數據花費的時間比遍歷索引的時間還要短,索引就可能不會產生優化效果 。

最左匹配原則

創建聯合索引的時候都會默認從最左邊開始,因此索引列的順序很重要,創建索引的時候就應該把最經常使用的放在左邊,使用select的時候也是這樣,從最左邊的開始,依次匹配右邊的。

優勢

能夠保證數據庫表中每一行的數據的惟一性。
能夠大大加快數據的索引速度。
加速表與表之間的鏈接。
能夠顯著的減小查詢中分組和排序的時間。

缺點

建立索引和維護索引要耗費時間,這種時間隨着數據量的增長而增長。
索引須要佔物理空間,除了數據表佔用數據空間以外,每個索引還要佔用必定的物理空間,若是須要創建聚簇索引,那麼須要佔用的空間會更大,其實創建索引就是以空間換時間。
表中的數據進行增、刪、改的時候,索引也要動態的維護,這就下降了維護效率。

驗證索引是否可以提高查詢性能

建立測試表index_test


6785d2102f8e962647fe53c303c5426c.png


使用python腳本程序經過pymsql模塊,向表中添加十萬條數據

import pymysqldef main():
    # 建立Connection鏈接
    conn = pymysql.connect(host='localhost', 
                           port=3306,
                           database='db_test',
                           user='root',
                           password='deepin',
                           charset='utf8')
    # 得到Cursor對象
    cursor = conn.cursor()
    # 插入10萬次數據
    for i in range(100000):
        cursor.execute("insert into index_test values('haha-%d')" % i)
    # 提交數據
    conn.commit()if __name__ == "__main__":
    main()

在mysql終端開啓運行時間監測:set profiling=1;


12d1e720e2f49f69ea141f4a70bb1733.png


查找第1萬條數據ha-99999

select * from index_test where name='haha-99999';

查看執行的時間:

show profiles;


f078660dd15cc50315fc03d250093bd5.jpeg


  • 爲表index_test的name列建立索引:
create index name_index on index_test(name(10));

再次執行查詢語句、查看執行的時間:


88da5f783175a05ed224a87214dcd6a5.jpeg


能夠看出合適的索引確實能夠明顯提升某些字段的查詢效率。


原做者:是虎子呀
原文連接: 那些年被面試官懟的MySQL索引 - 是虎子呀的我的空間 - OSCHINA
原出處:OSCHINA

8498e61271744e9b7e2fd0dabff5a521.jpeg

相關文章
相關標籤/搜索