索引?看這一篇就夠了!

不醉發表於2020-04-04

概念

開篇點題,索引是啥?索引就是一本書的目錄,先在目錄裡查到你想看的那幾章的頁碼,然後直接翻到那幾章,比你從頭開始一頁一頁的翻過去快多了!

索引的資料結構

索引常見的資料結構有三種:

  • 雜湊表
  • 有序陣列
  • 搜尋樹

假設現在有一張User表,需要根據userId查詢,我們分別來看下這三種索引是如何組織的

雜湊表

把userId通過雜湊函式轉換成一個確定的值,代表一個地址,把資料存在這個地址上

查詢的時候也是先把userId通過雜湊函式轉換成地址,再直接去地址裡取資料即可

可以看出雜湊表的更新、等值查詢效率很高,時間複雜度是O(1);但是範圍查詢效率就不行了,我要查n個userId的資料就需要做n次雜湊,時間複雜度退化成O(n)

雜湊表是如何解決衝突的?在我們這個例子中,不同userId經過雜湊函式後可能會得出相同的值,解決方案是在那個地址上拉出一條連結串列,所有雜湊值相同的資料都放在這條連結串列上,並且這條連結串列是無序的,來一個就在末尾插一個,所以能做到更新的時間複雜度是O(1)

有序陣列

這個不用多介紹,把資料按userId的順序存在陣列裡即可

因為有序,查詢的時候可以用二分法,不管等值查詢還是範圍查詢,時間複雜度可以達到O(logn)

但更新的時候就比較麻煩了,為了保持順序,時間複雜度退化成O(n)

搜尋樹

一般可以把等值查詢、更新的時間複雜度控制在O(logn),例如B樹和B+樹 (對B樹和B+樹結構不瞭解的話可以先去複習下)。但大部分資料庫引擎都選擇B+樹作為預設索引結構(例如MySQL的MyISAM、InnoDB),接下來我就詳細介紹下B樹和B+樹的區別

B樹 or B+樹

B樹和B+樹的主要區別是:B樹的各級節點中,關鍵字和資料並存;B+樹非葉子節點中存關鍵字,葉子節點中存資料,且葉子節點按順序組成連結串列

這樣導致的直接結果就是,在同樣大小的空間中,B+樹可以比B樹存下更多的關鍵字,也就是可以為更多的資料建立索引。就像同樣是一頁紙,如果只寫目錄,那可以索引一整本書;如果又寫目錄又寫內容,那可能只能索引一章。如果要查詢到我們需要的全部章節,前者只需要瀏覽一頁,後者需要瀏覽好幾頁。我們瀏覽目錄頁的過程,其實就是資料庫把索引頁從硬碟讀到記憶體的過程,B+樹可以減少這個過程的次數,降低I/O開銷,提高了速度

在範圍查詢時,B樹需要不斷進行中序遍歷,時間複雜度退化成O(n);而B+樹在定位到第一個葉子節點後,可以直接通過連結串列取資料,時間複雜度仍能保持在O(logn)

此外B樹由於資料分佈在各級節點,查詢速度會有較大差異;而B+樹資料都分佈在葉子節點,查詢速度比較穩定

簡單總結一下,B+樹相對於B樹的優勢:

  • 降低I/O開銷
  • 支援O(logn)範圍查詢
  • 查詢速度穩定

接下來我們開始著重說說B+樹索引的一些知識

索引型別

根據索引本身的特點,有以下索引型別:

  • 唯一索引/普通索引
    • 唯一索引:明確規定了該索引不會出現重複值,查詢時查到符合條件的值就停止查詢
    • 普通索引:查詢時查到符合條件的值會繼續查詢,直到查到不符合條件的值
  • 主鍵索引/普通索引
    • 主鍵索引:葉子節點存放資料
    • 普通索引:葉子節點存放主鍵值,需要回表才能得到資料
  • 聚簇索引/非聚簇索引
    • 聚簇索引:索引順序與資料儲存順序一致/葉子節點放資料,一張表只能有一個聚簇索引,主鍵索引就是一種聚簇索引
    • 非聚簇索引:葉子節點放主鍵值或者實體地址,與聚簇索引相比,需要回表或定址操作
  • 聯合索引
    • 由多個欄位組成的索引
  • 覆蓋索引
    • 索引已經包含了查詢所需的所有欄位,可以不用回表

最左字首原則

使用B+樹索引時,有兩個最左字首原則

  • 走聯合索引的話,查詢條件必須按照索引欄位從左往右的順序,跳過索引欄位或者非等值查詢都會停止走索引
    原因:建立聯合索引時,關鍵字由多個欄位組成,關鍵字先按第一個欄位排序,對於第一個欄位相同的關鍵字,再按第二個欄位排序……以此類推,上一個欄位必須定位到一個確定的值,才能繼續走下一個欄位,所以跳過欄位或者有非等值查詢欄位都會導致後續的索引欄位失效
  • 字串模式匹配走索引的話,必須要用最左字首匹配,即like xx%這種形式
    原因:給字串欄位建立索引時,與聯合索引機制類似,也是按照字元從左往右的順序依次排序的,先按第一個字元排序,對於第一個字元相同的關鍵字,再按第二個字元排序,所以要用最左字首匹配

暫時先寫這麼多,有時間會再補充

相關文章