MySQL索引相關知識點及面試常問題

常見面試題

  首先引入索引相關的常見的面試題,讓咱們更客觀的學習索引相關的內容mysql

    • 問:數據庫中最多見的慢查詢優化方式是什麼?
      • 同窗A:加索引。
    • 問:爲何加索引能優化慢查詢?
      • 同窗A:...不知道
      • 同窗B:由於索引其實就是一種優化查詢的數據結構,好比Mysql中的索引是用B+樹實現的,而B+樹就是一種數據結構,能夠優化查詢速度,能夠利用索引快速查找數據,因此能優化查詢。
    • 問:你知道哪些數據結構能夠提升查詢速度?(聽到這個問題就感受此處有坑...)
      • 同窗B:哈希表、徹底平衡二叉樹、B樹、B+樹等等。
    • 問:那這些數據結構既然都能優化查詢速度,那Mysql種爲什麼選擇使用B+樹?
      • 同窗B:...不知道
    • 問:有一個titles表,主鍵由empno,title,fromdate三個字段組成。那麼如下幾個語句會用到索引嗎?
      select * from employees.titles where emp_no=1
      select * from employees.titles where title='1'
      select * from employees.titles where emp_no='1'andtitle=1
      select * from employees.titles where title='1'andemp_no=1
    • 問:爲何哈希表、徹底平衡二叉樹、B樹、B+樹均可以優化查詢,爲什麼Mysql獨獨喜歡B+樹?哈希表有什麼特色?
    • 答:假若有這麼一張表(表名:sanguo):                                                                                                                                                                        

    如今對name字段創建哈希索引,注意字段值所對應的數組下標是哈希算法隨機算出來的,因此可能出現哈希衝突。那麼對於這樣一個索引結構,如今來執行下面的sql語句:
select * from sanguo where name='周瑜'

能夠直接對‘周瑜’按哈希算法算出來一個數組下標,而後能夠直接從數據中取出數據並拿到鎖對應那一行數據的地址,進而查詢那一行數據。那麼若是如今執行下面的sql語句:面試

select * from sanguo where name>'周瑜'

則無能爲力,由於哈希表的特色就是能夠快速的精確查詢,可是不支持範圍查詢。算法

若是用徹底平衡二叉樹呢?sql

仍是上面的表數據用徹底平衡二叉樹表示以下圖(爲了簡單,數據對應的地址就不畫在圖中了。):數據庫

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

  • 左指針,指向左子樹
  • 鍵值
  • 鍵值所對應的數據的存儲地址
  • 右指針,指向右子樹

另外須要提醒的是,二叉樹是有順序的,簡單的說就是「左邊的小於右邊的」假如咱們如今來查找‘周瑜’,須要找2次(第一次曹操,第二次周瑜),比哈希表要多一次。並且因爲徹底平衡二叉樹是有序的,因此也是支持範圍查找的。數據結構

 

若是用B樹呢?學習

仍是上面的表數據用B樹表示以下圖(爲了簡單,數據對應的地址就不畫在圖中了。):優化

 

能夠發現一樣的元素,B樹的表示要比徹底平衡二叉樹要「矮」,緣由在於B樹中的一個節點能夠存儲多個元素。spa

 

若是用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+樹裏的元素也是有序的。

那麼,一個B+樹的節點中到底存多少個元素合適呢?

  其實也能夠換個角度來思考B+樹中一個節點到底多大合適?

  答案是:B+樹中一個節點爲一頁或頁的倍數最爲合適。由於若是一個節點的大小小於1頁,那麼讀取這個節點的時候其實也會讀出1頁,形成資源的浪費;若是一個節點的大小大於1頁,好比1.2頁,那麼讀取這個節點的時候會讀出2頁,也會形成資源的浪費;因此爲了避免形成浪費,因此最後把一個節點的大小控制在1頁、2頁、3頁、4頁等倍數頁大小最爲合適。

那麼,Mysql中B+樹的一個節點大小爲多大呢?

  這個問題的答案是「1頁」,這裏說的「頁」是Mysql自定義的單位(其實和操做系統相似),Mysql的Innodb引擎中一頁的默認大小是16k(若是操做系統中一頁大小是4k,那麼Mysql中1頁=操做系統中4頁),可使用命令SHOW GLOBALSTATUS like 'Innodbpagesize'; 查看。而且還能夠告訴你的是,一個節點爲1頁就夠了。

爲何一個節點爲1頁(16k)就夠了?

  解決這個問題,咱們先來看一下Mysql中利用B+樹的具體實現。

  Mysql中MyISAM和innodb使用B+樹

  一般咱們認爲B+樹的非葉子節點不存儲數據,只有葉子節點才存儲數據;而B樹的非葉子和葉子節點都會存儲數據,會致使非葉子節點存儲的索引值會更少,樹的高度相對會比B+樹高,平均的I/O效率會比較低,因此使用B+樹做爲索引的數據結構,再加上B+樹的葉子節點之間會有指針相連,也方便進行範圍查找。上圖的data區域兩個存儲引擎會有不一樣。

 

 MyISAM中的B+樹

  MYISAM中葉子節點的數據區域存儲的是數據記錄的地址,MyISAM存儲引擎在使用索引查詢數據時,會先根據索引查找到數據地址,再根據地址查詢到具體的數據。而且主鍵索引和輔助索引沒有太多區別。

 主鍵索引

 

 

輔助索引

  

 

 InnoDB中的B+樹

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

主鍵索引

輔助索引

 

 Innodb中的主鍵索引和實際數據時綁定在一塊兒的,也就是說Innodb的一個表必定要有主鍵索引,若是一個表沒有手動創建主鍵索引,Innodb會查看有沒有惟一索引,若是有則選用惟一索引做爲主鍵索引,若是連惟一索引也沒有,則會默認創建一個隱藏的主鍵索引(用戶不可見)。另外,Innodb的主鍵索引要比MyISAM的主鍵索引查詢效率要高(少一次磁盤IO),而且比輔助索引也要高不少。因此,咱們在使用Innodb做爲存儲引擎時,咱們最好:

  1. 手動創建主鍵索引
  2. 儘可能利用主鍵索引查詢

 

回到咱們的問題:爲何一個節點爲1頁(16k)就夠了?

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

  • § 非葉子節點:主鍵+指針
  • § 葉子節點:數據

 

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

 

最左前綴原則

  咱們模擬數據創建一個聯合索引 select *,concat(right(emp_no,1),"-",right(title,1),"-",right(from_date,2)) from employees.titles limit 10;

 

咱們判斷一個查詢條件能不能用到索引,咱們要分析這個查詢條件能不能利用某個索引縮小查詢範圍

對於 select from employees.titles where emp_no=1是能用到索引的,由於它能利用上面的索引全部查詢範圍,首先和第一個節點「4-r-01」比較,1<4,因此能夠直接肯定結果在左子樹,同理,依次按順序進行比較,逐步能夠縮小查詢範圍。對於select from employees.titles where title='1'是不能用到索引的,由於它不能用到上面的因此,和第一節點進行比較時,沒有empno這個字段的值,不能肯定到底該去左子樹仍是右子樹繼續進行查詢。對於 select * from employees.titles where title='1' and emp_no=1是能用到索引,按照咱們的上面的分析,先用title='1'這個條件和第一個節點進行比較,是沒有結果的,可是mysql會對這個sql進行優化,優化以後會將empno=1這個條件放到第一位,從而能夠利用索引。

相關文章
相關標籤/搜索