MySQL學習之索引機制詳解(B+樹)


一、索引是什么?

  索引是為了加速對表中數據行的檢索而創建的一種分散存儲的數據結構。而且是實現了高級查找算法的數據結構,索引一般以文件形式存儲在磁盤上,索引檢索需要磁盤I/O操作。

二、為什么要使用索引?

  1. 索引能極大的減少存儲引擎需要掃描的數據量。
  2. 索引可以把隨機IO變成順序IO。
  3. 索引可以幫助我們在進行分組、排序等操作時,避免使用臨時表。

三、磁盤存取原理

  磁盤存取有以下特點:

  • 尋道時間(速度慢,費時);
  • 旋轉時間(速度較快);
  • 預讀:長度為頁的整倍數( 主存和磁盤以頁為單位交換數據,一頁4K);
  • 空間連續性原理:被訪問的數據,其所在的位置周圍的數據也有更高的可能性被訪問。

        

四、索引是什么實現的?

  Indexes是第三方公司提供的可插拔的插件式存儲引擎。

  MySQL結構體系:

              

 五、為什么選用B+樹?

  1、Hash索引方式

             

  其實在我們數據庫中也使用到了Hash的索引方式,數據庫默認有就是這兩種實現方式:Hash和B+樹,如圖所示:

      

  優點:

  • 如果是等值查詢,哈希索引明顯有絕對優勢, 前提:鍵值唯一,而且不涉及到范圍查找,模糊匹配,如電話號碼就很適合。

  缺點:

  • 利用Hash存儲的話需要將所有的數據文件添加到內存,比較耗費內存空間。
  • 哈希索引沒辦法完成范圍查詢檢索
  • 哈希索引也沒辦法利用索引完成排序,以及like ‘xxx%’ 這樣的部分模糊查詢
  • 哈希索引也不支持多列聯合索引的
  • 在有大量重復鍵值情況下,哈希索引的效率也最左前綴原則是極低的,因為存在哈希碰撞問題

  2、二叉樹、紅黑樹索引方式

            

   缺點:

  • 太深:數據處的高/深度決定着他的IO操作次數, IO操作耗時大。應先把數據讀取效率。
  • 太小:每一個磁盤塊(節點/頁)保存的數據量太小了。

  3、B樹(B-樹)的索引方式

  

  缺點:

  • B樹的每個節點既有key又有data,但是每個頁存儲的空間是有限的,如果data較大的話,那么會導致每個節點存儲的key數量變小。
  • 當存儲的數據量很大的時候會導致深度較大,增大查詢時候的磁盤IO次數,影響查詢性能。
  • Mysql查詢的時候使用到了大量的范圍查找,B樹在性能上並不能滿足大量范圍查找。

  

  B+Tree與B-Tree的區別:

  • B+節點關鍵字搜索采用閉合區間。(MYSQL推崇使用ID作為索引,由於ID是自增的數字類型,只會增大,所以采用向右拓展的一個方式。)
  • B+非葉節點不保存數據相關信息, 只保存關鍵字和子節點的引用。
  • B+關鍵字對應的數據保存在葉子節點中。
  • B+葉子節點是順序排列的, 並且相鄰節點具有順序引用的關系。

  選擇B+Tree優點:

  • B+樹是B-樹的變種( PLUS版) 多路絕對平衡查找樹, 他擁有B-樹的優勢。
  • B+樹的葉子節點是順序排列的,且相鄰接點具有順序引用關系,因此在范圍查找時掃庫、 表能力更強,同時排序能力更強。
  • B+樹僅僅在葉子節點存儲數據,這樣索引文件更小,磁盤讀寫能力更強。
  • B+樹的樹高較低,索引文件較小,IO次數穩定,查詢效率更加穩定。

六、B+樹的索引實現方式

  查看數據存儲位置:

show variables like 'datadir';

  1、MyIsam(非聚集索引)

  索引和數據分別存儲。表定義存在.frm文件中(每個存儲引擎都會有)。表中數據存在.MYD文件中。索引存在.MYI文件中。

            

  如果同一張表多個索引:

          

  每個索引都存有每條數據的地址,一旦有變,維護起來比較耗時。 

  2、InnoDB(聚集索引)

            

  數據就存在索引的葉子節點中。
  輔助索引:

            

   其他索引存主鍵,再從主鍵索引中找數據。

  注意:

  • InnoDB是通過B+樹結構對主鍵創建索引,然后葉子節點中儲存記錄,如果沒有主鍵,那么會選擇唯一鍵,如果沒有唯一鍵,那么會生成一個6位的row_id來作為主鍵。
  • 如果創建索引的鍵是其他字段,那么在葉子節點中存儲的是該記錄的主鍵,然后再通過主鍵索引找到對應的記錄。  

  為什么不推薦UUID,使用UUID有以下問題:

  • UUID一般是32位,長度較長,由於一次預讀是4K,這樣就增加了讀的次數,同時會造成度degree變小,樹變高;
  • UUID是一個無序的序列,在插入到B+樹的時候,設計多次B+樹的平衡調整,當樹大了的時候性能就會變得很低;
  • 由於無序的原因,因此分頁查詢的時候就會很麻煩。

  因此推薦一些自增int、long型的字段作為主鍵,比如雪花算法。雪花算法(SnowFlake)Java實現

七、索引注意事項

  1、聯合索引列選擇原則

  • 經常用的列優先 【 最左匹配原則】
  • 選擇性( 離散度) 高的列優先【 離散度高原則】(列的離散性越高,選擇性就越好。)
  • 寬度小的列優先【 最少空間原則】

  2、覆蓋索引

  如果查詢列可通過索引節點中的關鍵字直接返回, 則該索引稱之為覆蓋索引。

  覆蓋索引可減少數據庫IO, 將隨機IO變為順序IO, 可提高查詢性能。

  比如創建索引:

create index idx_name_phoneNum on users(name,phoneNum);

  查詢語句:

select name,phoneNum from user where name=?

  可直接從索引樹中返回關鍵字,不會再去查數據內容。

  3、其他注意事項

  1. 索引列的數據長度能少則少。
  2. 索引一定不是越多越好, 越全越好, 一定是建合適的。
  3. 匹配列前綴可能用到索引 like 9999%, like %9999%、 like %9999用不到索引。like 9999%得看情況,如果索引列離散性高,就能用到索引,離散性低,就用不到索引。
  4. Where 條件中 not in 和 <>操作無法使用索引。
  5. 匹配范圍值, order by 也可用到索引。
  6. 多用指定列查詢, 只返回自己想到的數據列, 少用select * 以減少IO。
  7. 聯合索引中如果不是按照索引最左列開始查找, 無法使用索引。在執行常量等值查詢時,改變索引列的順序並不會更改explain的執行結果,因為mysql底層優化器會進行優化,但是推薦按照索引順序列編寫sql語句。
  8. 聯合索引中精確匹配最左前列並范圍匹配另外一列可以用到索引。(索引列為name,age的話,name=‘zhangsan’ and age>20)
  9. 聯合索引中如果查詢中有某個列的范圍查詢, 則其右邊的所有列都無法使用索引。(索引列為age,name的話,age>20 and name=‘zhangsan’)

附:B+樹添加和刪除數據圖解 (請放大查看)


免責聲明!

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



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