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

做者:是虎子呀
https://my.oschina.net/u/4062...

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

索引是什麼?

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

爲何須要索引?

思考:如何在一個圖書館中找到一本書?設想一下,假如在圖書館中沒有其餘輔助手段,只能一條道走到黑,一本書一本書的找,通過3個小時的連續查找,終於找到了你須要看的那本書,但此時天都黑了。mysql

爲了不這樣的事情,每一個圖書館才都配備了一套圖書館管理系統,你們要找書籍的話,先在系統上查找到書籍所在的房屋編號、圖書架編號還有書在圖書架幾層的那個方位,而後就能夠直接大搖大擺的去取書了,就能夠很快速的找到咱們所須要的書籍。索引就是這個原理,它能夠幫助咱們快速的檢索數據。面試

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

B-Tree和B+Tree

目前大部分數據庫系統及文件系統都採用B-Tree或其變種B+Tree做爲索引結構, 我在這裏分別講一下。[](http://mp.weixin.qq.com/s?__b...爲何索引能提升查詢速度?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樹的一些特性:微信

  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。

B+Tree

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

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

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

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

關於MySQL的兩種經常使用存儲引擎MyISAM和InnoDB的索引均以B+樹做爲數據結構,兩者卻有不一樣(這裏只說兩者索引的區別)。推薦看下:InnoDB一棵B+樹能夠存放多少行數據?

MyISAM索引和Innodb索引的區別

MyISAM使用B+樹做爲索引結構,葉節點葉節點的data域保存的是存儲數據的地址,主鍵索引key值惟一,輔助索引key能夠重複,兩者在結構上相同。關注微信公衆號:Java技術棧,在後臺回覆:mysql,能夠獲取我整理的 N 篇 MySQL 教程,都是乾貨。

所以,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存儲引擎均支持全文索引。)推薦看下:MySQL 索引B+樹原理,以及建索引的幾大原則

索引的使用策略及優缺點

使用索引

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

不使用索引

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

最左匹配原則

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

優勢

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

缺點

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

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

建立測試表index_test

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

import pymysql

def 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;

查找第1萬條數據ha-99999

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

查看執行的時間:

show profiles;

爲表index_test的name列建立索引:

create index name\_index on index\_test(name(10));

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

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

最後,感謝女友在生活中,工做上的包容、理解與支持 !

推薦去個人博客閱讀更多:

1.Java JVM、集合、多線程、新特性系列教程

2.Spring MVC、Spring Boot、Spring Cloud 系列教程

3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程

4.Java、後端、架構、阿里巴巴等大廠最新面試題

以爲不錯,別忘了點贊+轉發哦!

相關文章
相關標籤/搜索