概念
開篇點題,索引是啥?索引就是一本書的目錄,先在目錄裡查到你想看的那幾章的頁碼,然後直接翻到那幾章,比你從頭開始一頁一頁的翻過去快多了!
索引的資料結構
索引常見的資料結構有三種:
- 雜湊表
- 有序陣列
- 搜尋樹
假設現在有一張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%
這種形式
原因:給字串欄位建立索引時,與聯合索引機制類似,也是按照字元從左往右的順序依次排序的,先按第一個字元排序,對於第一個字元相同的關鍵字,再按第二個字元排序,所以要用最左字首匹配