干貨 MySQL常見的面試題 + 索引原理分析


常見的面試必備之MySQL索引底層原理分析:

  • MySQL索引的本質
  • MySQL索引的底層原理
  • MySQL索引的實戰經驗

面試

1)問題:數據庫中最常見的慢查詢優化方式是什么?

  回答:加索引

2)問題:為什么加索引能優化慢查詢?

  回答:因為索引是一種優化查詢的數據結構,比如MySQL中的索引是B+樹實現的,而B+樹就是一種數據結構,可以優化查詢速度,可以利用索引快速查找數據,所以能優化查詢!

3)你知道哪些數據結構可以提高查詢速度?

  回答:哈希表、完全平衡二叉搜索樹、B樹、B+樹等等;

4)那這些數據結構既然都能優化查詢速度,那MySQL為何選擇使用B+樹?

  (1)哈希表的特點

  假設有這么一張表,表名為:users

       

  現在對name字段建立hash索引

  

  注意字段值所對應的數組下標是哈希算法隨機計算出來的,所以可能會出現哈希沖突。那么對於這樣的一個索引結構,現在來執行下面的SQL語句:

    select * from users where name = '周瑜';

  可以直接對 '周瑜' 按哈希算法算出一個數組下標,然后可以直接從數據中取出數據並拿到鎖對應的那一行數據的地址,進而在數據表文件中查詢那一行數據。

  那么如果現在執行下面的SQL語句:

    select * from users where name > '周瑜';   

  此時則無能為力,因為哈希表的特點就是可以快速的精確查詢,但是不支持范圍查詢!

  (2)完全平衡二叉搜索樹

  還是上面的表數據用完全平衡二叉樹表示如下圖(為了簡單,數據對應的地址就不畫在圖中)

  

  圖中的每一個節點實際上應該有四部分:

    1. 左指針,指向左子樹

    2. 鍵值(key)

        3. 鍵值所對應數據的存儲地址(data域中的值)

    4. 右指針,指向右子樹

  需注意:完全平衡二叉搜索樹是有序的,簡單的說就是 "左邊的小於右邊的",假如我們現在來查找 '周瑜' ,需要查找2次(第一次操作,第二次周瑜),比哈希表要多一次。而且由於完全平衡二叉搜索樹是有序的,所以支持范圍查找。

  (3)B樹

  還是上面的表示數據用B樹表示如下圖(為了簡單,數據對應的地址就不畫在圖中了)

  

  可以發現同樣的元素,B樹表示的要比完全平衡二叉搜索樹要 "矮",原因在於B樹中的一個節點可以存儲多個元素!

  (4)B+樹

  還是上面的表示數據用B+樹表示如下圖(為了簡單,數據對應的地址就不畫在圖中了)

  

  我們可以發現同樣的元素,B+樹的表示要比B樹要 "胖",原因在於B+樹中的非葉子節點會冗余一份在葉子節點中,並且葉子節點之間用指針相連!

  B+樹作為索引的優勢

  這里我們用“反證法”,假如我們現在就用完全平衡二叉搜索樹作為索引的數據結構,我們來看一下有什么不妥的地方。實際上,索引也是很“大”的,因為索引也是存儲元素的,我們的一個表的數據行數越多,那么對應的索引文件其實也是會很大的,實際上也是需要存儲在磁盤中的,而不能全部都放在內存中,所以我們在考慮選用哪種數據結構時,我們可以換一個角度思考,哪個數據結構更適合從磁盤中讀取數據,或者哪個數據結構能夠提高磁盤的IO效率。回頭看一下完全平衡二叉搜索樹,當我們需要查詢“張飛”時,需要以下步驟

  1. 從磁盤中取出“曹操”到內存,CPU從內存取出數據進行筆記,“張飛”<“曹操”,取左子樹(產生了一次磁盤IO)

  2. 從磁盤中取出“周瑜”到內存,CPU從內存取出數據進行筆記,“張飛”>“周瑜”,取右子樹(產生了一次磁盤IO)

  3. 從磁盤中取出“孫權”到內存,CPU從內存取出數據進行筆記,“張飛”>“孫權”,取右子樹(產生了一次磁盤IO)

  4. 從磁盤中取出“黃忠”到內存,CPU從內存取出數據進行筆記,“張飛”=“張飛”,找到結果(產生了一次磁盤IO)

  同理,回頭看一下B樹,我們發現只發送三次磁盤IO就可以找到“張飛”了,這就是B樹的優點:一個節點可以存儲多個元素,相對於完全平衡二叉樹所以整棵樹的高度就降低了,磁盤IO效率提高了。而B+樹是B樹的升級版,只是把非葉子節點冗余一下,這么做的好處是 為了提高范圍查找的效率

到這里可以總結出來,Mysql選用B+樹這種數據結構作為索引,可以提高查詢索引時的磁盤IO效率,並且可以提高范圍查詢的效率,並且B+樹里的元素也是有序的。

 

5)問題:一個B+樹的節點中到底存儲多少個元素合適呢?

  回答:B+樹中一個節點為一頁或頁的倍數最為合適。因為如果一個節點的大小小於1頁,那么讀取這個節點的時候其實也會讀出1頁,會造成資源的浪費;如果一個節點的大小大於1頁,比如1.2頁,那么讀取這個節點的時候會讀出2頁,也會造成資源的浪費;所以為了不造成資源的浪費,最后把一個節點的大小控制在1頁、2頁、3頁、4頁等倍數頁大小最為合適!

6)MySQL中B+樹的一個節點大小為多大?

  回答:一頁,這里說的 "頁" 是MySQL自定義的單位(其實和操作系統類似),MySQL的Innodb引擎中一頁的默認大小是16K(如果操作系統中一頁大小是4K,那么MySQL中1頁 = 操作系統中的4頁),這樣存取數據的時候都是一頁一頁的獲取索引文件中節點數據的!

7)為什么B+樹中一個節點為1頁(16K)就夠了?

  回答:先來看一下MySQL中利用B+樹來實現索引的數據結構具體實現:

  MySQL中MyISM和Innodb使用B+樹

  

  通常我們認為B+樹的非葉子節點不存儲數據,只有葉子節點才存儲數據;而B樹的非葉子節點和葉子節點都會存儲數據,會導致非葉子節點存儲的索引值會更少,樹的高度相對會比B+樹高,平均的I/O效率會比較低,所以使用B+樹作為索引的數據結構,再加上B+樹的葉子節點之間使用了指針相連,也方便進行范圍內查找,上圖的data區域兩個存儲引擎會有區別!

  MyISM中的B+樹

  MyISQM中葉子節點的數據區域存儲的是數據記錄的地址

  主鍵索引

  

  輔助索引

  

  MyISAM存儲引擎在使用索引查詢數據時,會先根據索引查找到數據地址,再根據地址查詢到具體的數據。並且主鍵索引和輔助索引沒有太多區別。

  Innodb中的B+樹

  Innodb中主鍵索引的葉子節點的數據區域存儲的是數據記錄,輔助索引存儲的是主鍵值

     

  輔助索引

  

  Innodb中的主鍵索引和實際數據時綁定在一起的,也就是說Innodb的一個表一定要有主鍵索引,如果一個表沒有手動建立主鍵索引,Innodb會查看有沒有唯一索引,如果有則選用唯一索引作為主鍵索引,如果連唯一索引也沒有,則會默認建立一個隱藏的主鍵索引(用戶不可見)。另外,Innodb的主鍵索引要比MyISAM的主鍵索引查詢效率要高(少一次磁盤IO),並且比輔助索引也要高很多。所以,我們在使用Innodb作為存儲引擎時,我們最好:

  1. 手動建立主鍵索引

  2. 盡量利用主鍵索引查詢

  回到我們的問題:為什么一個節點為1頁(16K)就夠了?

  對着上面Mysql中Innodb中對B+樹的實際應用(主要看主鍵索引),可以發現B+樹中的一個節點存儲的內容是:

    1. 非葉子節點:主鍵 + 指針

    2. 葉子節點:數據

  那么,假設我們一行數據大小為1K,那么一頁就能存16條數據,也就是一個葉子節點能存16條數據;再看非葉子節點,假設主鍵ID為bigint類型,那么長度為8B,指針大小在Innodb源碼中為6B,一共就是14B,那么一頁里就可以存儲16K/14=1170個(主鍵+指針),那么一顆高度為2的B+樹能存儲的數據為:1170 * 16=18720條,一顆高度為3的B+樹能存儲的數據為:1170 * 1170 * 16=21902400(千萬級條)。所以在InnoDB中B+樹高度一般為1-3層,它就能滿足千萬級的數據存儲。在查找數據時一次頁的查找代表一次IO,所以通過主鍵索引查詢通常只需要1-3次IO操作即可查找到數據。所以也就回答了我們的問題,1頁=16k這么設置是比較合適的,是適用大多數的企業的,當然這個值是可以修改的,所以也能根據業務的時間情況進行調整。

  

 

 

 

  

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM