1. 程式人生 > >【學習】資料庫學習 -- 索引的原理

【學習】資料庫學習 -- 索引的原理

一、索引的基礎認知

  • 索引可以加快資料庫的檢索速度
  • 表常進行增(insert)刪(delete)改(update)操作,不建議建立索引,因為索引會降低增刪改等維護操作的速度
  • 索引佔用物理空間和資料空間
  • 索引具有最左匹配原則
  • 索引分為:聚集索引和非聚集索引
  • Mysql支援Hash索引和B+Tree索引

二、索引的學習要點

  • 為什麼使用索引可以加快資料庫的檢索速度?
  • 為什麼索引會降低增刪改等維護操作的速度?
  • 索引的最左匹配原則指的是什麼?
  • Hash索引和B+Tree索引有什麼區別?
  • 聚集索引和非聚集索引有什麼區別?

三、索引內容學習

   1. 索引的基礎知識

Mysql的基本儲存結構是頁(所有的記錄都儲存在頁裡):

  • 各個資料頁可以組合成一個雙向連結串列
  • 每個資料頁中的所有記錄可以組成一個單向連結串列
  • 每個資料頁都會為儲存在他裡面的記錄生成一個頁目錄,在我們通過主鍵查詢某條記錄的時候就可以在頁目錄中使用二分法快速定位到對應的槽,然後遍歷該槽對應分組中的記錄可以快速找到指定記錄
  • 以其他列作為搜尋條件:只能從最小記錄開始依次遍歷單鏈表中的每條記錄

比如,如果執行 select * from user where username = 'Java' 這樣沒有進行任何優化的sql語句,預設會這樣做:

  • 定位到記錄所在的頁:需要遍歷雙向連結串列,找到所在的頁
  • 從所在的頁內中查詢相應的記錄:由於不是根據主鍵查詢,只能遍歷所在頁的單鏈表

在資料量很大時,查詢速度非常慢!

   2.索引提高檢索速度

索引建立了B+Tree樹形結構,將無序的資料結構重組成有序:

   3.索引降低增刪改的速度

  • B+Tree是一種平衡樹,樹的高度相對比較低,符合矮胖結構,左右兩個子樹的高度差值的絕對值不會超過1,並且所有兩個子樹也是平衡二叉樹。
  • 對B+Tree進行增刪改操作會破壞他的原有平衡結構,為了維持樹的平衡結構就需要將資料重新組合成平衡結構,這些額外的工作就會導致降低增刪改等操作的速度。

   4.雜湊索引

  • 除B+Tree之外常見的一種索引就是雜湊索引
  • 雜湊索引就是採用一定的雜湊演算法,把鍵值換算成新的雜湊值,檢索時不需要類似B+樹那樣從根節點到葉子節點逐級查詢,只需一次雜湊演算法即可立刻定位到相應的位置,速度非常快

       侷限性:

  • 雜湊索引沒辦法利用索引完成排序
  • 不支援最左匹配原則
  • 雜湊碰撞問題
  • 不支援範圍查詢

   5.innoDB支援Hash索引和B+Tree索引

   6.聚集索引和非聚集索引的區別

      簡單來說:

  • 聚集索引就是以主鍵建立的索引
  • 非聚集索引就是以非主鍵建立的索引

      區別:

  • 聚集索引在葉子節點儲存的是表中的資料
  • 非聚集索引在葉子節點儲存的是主鍵和索引列
  • 使用非聚集索引查詢出資料時,拿到葉子上的主鍵再去查到想要查詢的資料(拿到主鍵再查詢這個過程叫做回表)

     非聚集索引在建立的時候也未必是單列的,可以多個列來建立索引

  • 此時就涉及到了哪個列會走索引,哪個列不走索引的問題了(最左匹配原則-->後面有說)
  • 建立多個單列(非聚集)索引的時候,會生成多個索引樹(所以過多建立索引會佔用磁碟空間)

     在建立多列索引中也涉及到了一種特殊的索引 -- 覆蓋索引

  • 我們前面知道了,如果不是聚集索引,葉子節點儲存的是主鍵+列值
  • 最終還是要“回表”,也就是要通過主鍵再查詢一次。這樣就會比較慢
  • 覆蓋索引就是把要查詢出的列和索引是對應的,不做回表操作!

     比如說:

  • 現在我建立了索引(username,age),在查詢資料的時候:select username,age from user where username='Java' and age=20
  • 很明顯地知道,我們上邊的查詢是走索引的,並且,要查詢出的列在葉子節點都存在!所以,就不用回表了
  • 所以,能使用覆蓋索引就儘量使用吧

   7.索引最左匹配原則

  • 索引可以簡單如一個列(a),也可以複雜如多個列(a, b, c, d),即聯合索引。
  • 如果是聯合索引,那麼key也由多個列組成,同時,索引只能用於查詢key是否存在(相等),遇到範圍查詢(>、<、between、like左匹配)等就不能進一步匹配了,後續退化為線性查詢。
  • 因此,列的排列順序決定了可命中索引的列數。

     例子:

  • 如有索引(a, b, c, d),查詢條件a = 1 and b = 2 and c > 3 and d = 4,則會在每個節點依次命中a、b、c,無法命中d。(很簡單:索引命中只能是相等的情況,不能是範圍匹配)

    8.=、in自動優化順序

  • 不需要考慮=、in等的順序,mysql會自動優化這些條件的順序,以匹配儘可能多的索引列
  • 如有索引(a, b, c, d),查詢條件c > 3 and b = 2 and a = 1 and d < 4與a = 1 and c > 3 and b = 2 and d < 4等順序都是可以的,MySQL會自動優化為a = 1 and b = 2 and c > 3 and d < 4,依次命中a、b、c

   9.索引總結

      上面談的其實就是索引最基本的東西,要創建出好的索引要顧及到很多的方面:

  • 最左字首匹配原則。這是非常重要、非常重要、非常重要(重要的事情說三遍)的原則,MySQL會一直向右匹配直到遇到範圍查詢(>,<,BETWEEN,LIKE)就停止匹配。
  • 儘量選擇區分度高的列作為索引,區分度的公式是 COUNT(DISTINCT col) / COUNT(*)。表示欄位不重複的比率,比率越大我們掃描的記錄數就越少。
  • 索引列不能參與計算,儘量保持列“乾淨”。比如,FROM_UNIXTIME(create_time) = '2016-06-06' 就不能使用索引,原因很簡單,B+樹中儲存的都是資料表中的欄位值,但是進行檢索時,需要把所有元素都應用函式才能比較,顯然這樣的代價太大。所以語句要寫成 : create_time = UNIX_TIMESTAMP('2016-06-06')。
  • 儘可能的擴充套件索引,不要新建立索引。比如表中已經有了a的索引,現在要加(a,b)的索引,那麼只需要修改原來的索引即可。
  • 單個多列組合索引和多個單列索引的檢索查詢效果不同,因為在執行SQL時,MySQL只能使用一個索引,會從多個單列索引中選擇一個限制最為嚴格的索引。