數據庫索引

     數據庫的索引,使用比較多了,但是對於原理一直懵懂,今天來徹底整理一下。

索引的2種數據結構:

  1.平衡樹(非二叉;主流的關係型數據庫一般都默認用它,MySQL裏常用的索引數據結構有B+樹索引和哈希索引兩種)

  2.哈希桶

備註:先說下,在MySQL文檔裏,實際上是把B+樹索引寫成了BTREE。

一.平衡樹

     首先這裏只b+tree,關於B樹概念,可自行百度。通過學習發現它是平衡多叉樹,可以減少訪問磁盤的次數,更爲形象的說法就是降低樹的高度,減少磁盤的I/O次數,優化磁盤訪問的速度。而索引的結構組織就是要儘量減少查找過程中磁盤I/O的存取次數。 

B+樹

   B+樹可以看作是B樹的一種變形,在實現文件索引結構方面比B樹使用得更普遍。

   一個m階的B+樹具有如下幾個特徵:

  • 樹中每個非葉結點最多有 m 棵子樹;
  • 根結點 (非葉結點) 至少有 2 棵子樹。除根結點外, 其它的非葉結點至少有 m/2 棵子樹;有 n 棵子樹的非葉結點有 n-1 個關鍵碼。
  • 所有葉結點都處於同一層次上,包含了全部關鍵碼及指向相應數據對象存放地址的指針,且葉結點本身按關鍵碼從小到大順序鏈接;
  • 每個葉結點中的子樹棵數 n 可以多於 m,可以少於 m,視關鍵碼字節數及對象地址指針字節數而定。
  • 若設結點可容納最大關鍵碼數爲 m1,則指向對象的地址指針也有 m1 個。
  • 結點中的子樹棵數 n 應滿足 n 屬於[m1/2, m1]
  • 若根結點同時又是葉結點,則結點格式同葉結點。
  • 所有的非葉結點可以看成是索引部分,結點中關鍵碼 Ki 與指向子樹的指針 Pi 構成對子樹 (即下一層索引塊) 的索引項 ( Ki, Pi ),Ki 是子樹中最小的關鍵碼。
  • 特別地,子樹指針 P0 所指子樹上所有關鍵碼均小於 K1。結點格式同B樹。
  • 葉結點中存放的是對實際數據對象的索引。
  • 在B+樹中有兩個頭指針:一個指向B+樹的根結點,一個指向關鍵碼最小的葉結點。

B+樹結構圖:

如上圖,是一顆b+樹,關於b+樹的定義可以參見B+樹,淺藍色的塊我們稱之爲一個磁盤塊,可以看到每個磁盤塊包含幾個數據項(深藍色所示)和指針(黃色所示),如磁盤塊1包含數據項17和35,包含指針P1、P2、P3,P1表示小於17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大於35的磁盤塊。真實的數據存在於葉子節點即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非葉子節點只不存儲真實的數據,只存儲指引搜索方向的數據項,如17、35並不真實存在於數據表中。

b+樹的查找過程

         如圖所示,如果要查找數據項29,那麼首先會把磁盤塊1由磁盤加載到內存,此時發生一次IO,在內存中用二分查找確定29在17和35之間,鎖定磁盤塊1的P2指針,內存時間因爲非常短(相比磁盤的IO)可以忽略不計,通過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內存,發生第二次IO,29在26和30之間,鎖定磁盤塊3的P2指針,通過指針加載磁盤塊8到內存,發生第三次IO,同時內存中做二分查找找到29,結束查詢,總計三次IO。真實的情況是,3層的b+樹可以表示上百萬的數據,如果上百萬的數據查找只需要三次IO,性能提高將是巨大的,如果沒有索引,每個數據項都要發生一次IO,那麼總共需要百萬次的IO,顯然成本非常非常高。

哈希桶

      爲了解決線性探測實現哈希表時出現哈希衝突後導致數據堆聚的現象,我們採取了另一種結構來處理哈希衝突,哈希桶(拉鍊法),拉鍊法解決哈希衝突的做法是將所有通過哈希函數計算出來的哈希地址相同的結點存放在一個單鏈表之中。 
實現原理就是將哈希表定義爲一個由N個頭指針組成的指針數組,經過哈希函數計算得到的哈希地址相同的數據全部連在對於的頭指針下面。它繼承了數組的易於查找和鏈表便於刪除插入的特點。

        哈希索引是基於哈希表實現的。只有精確匹配索引所有列的的查詢纔有效。他的實現是存儲殷勤會對每一行數據的索引列計算哈希碼,並將哈希碼和指向該記錄的指針維護起來,對於hash相同的,採用鏈表的方式解決衝突。hashmap的數據結構。因爲索引的結構是十分緊湊的,所以hash索引的查詢很快。
但是hash索引也有他的限制:
1,hash索引只包含了哈希值和行指針,索引不能避免讀取行,不能使用覆蓋索引。
2,hash索引並不是按照索引順序存儲的,無法用於排序。
3,hash索引不支持部分或者區域查找,部分列的hash結果是不同的。
在Mysql中InnoDB引擎有一個特殊的功能叫做自適應哈希索引,他會在內存中基於B-Tree索引的基礎上面創建一個哈希索引,這讓B-Tree索引頁具備了一些哈希索引的優點。

索引原理:
     一般來說,索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲在磁盤上。這樣的話,索引查找過程中就要產生磁盤I/O消耗,相對於內存存取,I/O存取的消耗要高几個數量級。 
所以評價一個數據結構作爲索引的優劣最重要的指標就是在查找過程中磁盤I/O操作次數的漸進複雜度。 
換句話說,索引的結構組織要儘量減少查找過程中磁盤I/O的存取次數。 

索引優化

    1.  MySQL的優化主要分爲結構優化(Scheme optimization)和查詢優化(Query optimization)。

    2.優化策略

  • 最左前綴匹配原則
  • 主鍵外檢一定要建索引
  • 對 where,on,group by,order by 中出現的列使用索引
  • 儘量選擇區分度高的列作爲索引,區分度的公式是count(distinct col)/count(*),表示字段不重複的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0
  • 對較小的數據列使用索引,這樣會使索引文件更小,同時內存中也可以裝載更多的索引鍵
  • 索引列不能參與計算,保持列“乾淨”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);
  • 爲較長的字符串使用前綴索引
  • 儘量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那麼只需要修改原來的索引即可
  • 不要過多創建索引, 權衡索引個數與DML之間關係,DML也就是插入、刪除數據操作。這裏需要權衡一個問題,建立索引的目的是爲了提高查詢效率的,但建立的索引過多,會影響插入、刪除數據的速度,因爲我們修改的表數據,索引也需要進行調整重建
  • 對於like查詢,”%”不要放在前面。 
    SELECT * FROM houdunwang WHERE uname LIKE'後盾%' -- 走索引 
    SELECT * FROM houdunwang WHERE uname LIKE "%後盾%" -- 不走索引
  • 查詢where條件數據類型不匹配也無法使用索引 
    字符串與數字比較不使用索引; 
    CREATE TABLEa(achar(10)); 
    EXPLAIN SELECT * FROMaWHEREa="1" – 走索引 
    EXPLAIN SELECT * FROM a WHERE a=1 – 不走索引 
    正則表達式不使用索引,這應該很好理解,所以爲什麼在SQL中很難看到regexp關鍵字的原因。
  • 更多可以參考MySQL索引優化規則

參考文獻:https://blog.csdn.net/crystal6918/article/details/78073721

                   https://blog.csdn.net/MBuger/article/details/62418754

                   https://blog.csdn.net/wx145/article/details/82839419

                   https://blog.csdn.net/u011504963/article/details/79727849

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章