摘要
索引是資料庫裡重要的組成部分,也是提高查詢效率必備的知識點。本文将會介紹索引作用、索引類型、索引優化以及索引底層結構,也算是對索引知識的一次歸納。
一、索引介紹
什麼是索引?
資料庫是用來存儲與讀取資料的,如何在這龐大的資料中查詢我們想要的那一行呢?最簡單的辦法便是掃描整個資料表,一一對比。然而這樣效率太低了。
如果我們有類似字典的功能,在查詢某行資料前,先到字典裡定位到行位置,再根據行位置找到具體資料,是否能更快呢?是的,索引就是這麼設計的。
一般的,我們往表裡插入某一行資料時,總會有額外的資訊來定位到這一行。這個資訊可能是一個指針位址,也可能是一個主鍵辨別。
在拿到這一行的定位資訊後,就可以将列資料和定位資訊做關聯了。下次想查找這個字段列所對應的行資料時,就可以先到關聯資訊裡搜尋,拿到定位資訊後直接查找即可。這就是索引,存儲了列和定位資訊,這定位資訊也可以了解指向資料記錄的引用指針。
需要注意的是,索引是由
存儲引擎這個子產品來實作的,不同的存儲引擎有不同的實作方式。像 innodb 的主鍵就包含了行資料,找到了主鍵,也就找到了資料。
索引的分類
在資料庫裡,索引有好多種。我們可以從下面幾方面來分類歸納。
從資料結構劃分
:B+ 樹、hash 索引、全文索引
從實體結構劃分
:聚集索引、非聚集索引
從邏輯使用者劃分
:主鍵、唯一索引、複合索引、普通單列索引
其中, B+ 樹、 hash 索引、全文索引将會在後面具體介紹其底層結構,我們來看看其他的索引:
聚集索引:該索引除了存儲索引資訊還存儲了行資料,像剛剛提到的主鍵就是。找到它也就意味找到資料了。并且它的排序直接對應了實體存儲順序。
非聚集索引:該索引除了存儲索引資訊還存儲了定位到資料記錄的資訊,需要根據這個資訊再做一次查詢,才能擷取到資料,并且它的排序是邏輯上,不是實體存儲順序。
主鍵:唯一地辨別表中一條記錄的索引,不能有 NULL 值。在 InnoDB 裡,主鍵就是聚集索引。
唯一索引:索引所對應的列值裡是不能有重複值的,允許有 NULL 值。像剛剛提到的主鍵是不允許有 NULL 值的。
複合索引:有多列組合在一起的索引,但隻能按最左原則查找,即第一列字段才能被索引查找,後面隻是作為附帶資訊存放着。主要是為了找到索引後,不需要再去行資料裡撈資料,直接從索引裡提取字段資訊即可。
普通單列索引:沒有什麼限制條件的索引列。
索引的缺點
引入索引,并不總意味着高效,它是需要付出代價維護的。每當有資料需要添加更新時,都得更新對應的索引,這是額外的性能開銷,甚至有可能有出現死鎖。
另外,索引是需要占用磁盤空間的,不能無限制的添加索引,要有針對性的建索引。
二、索引的使用
使用原則
索引之是以那麼快,是因為我們将平時查詢頻率較高的字段單獨維護了起來。當我們有多個查詢選項,多個查詢條件就不一定能發揮作用了,是以索引的使用是有注意事項的,下面總結了一些:
- where 裡最經常用到的查詢字段才建索引,能利用主鍵 id,就用主鍵 id 來增删改查
- 按最左比對原則,将多個單列索引改為複合索引,減少維護量
- 盡量挑選擇度高,也就是重複率低的列作為索引,像性别這種列就不适合了,會在 B+ 樹裡做多層次多範圍的搜尋,還不如全表掃描呢
- 查找時,不對索引列做函數計算,否則不能使用到索引
- 查詢條件盡量用 union 來取代 or
- like 用法: ‘列%’ 這樣還是可以用到索引的,'%列%' 就不行了
- IS NULL,IS NOT NULL 是用不到索引的
- 在 order by,group by 裡盡量使用索引字段
- join 的 on 條件裡盡量使用索引字段
性能分析
當我們使用了索引後,又如何知道它有沒有使用到索引呢?我們可以借助執行計劃來分析,執行計劃是 mysql 根據我們的查詢語句進行一系列的分析後得到的優化方案。我們可以通過執行計劃來擷取執行過程。
執行計劃的擷取:
explain select 語句

涉及的字段含義如下:
- id: 該 SELECT 辨別符
- select_type: 該 SELECT 類型
- table: 輸出行的表
- partitions: 比對的分區
- type: 聯接類型
- possible_keys: 可供選擇的可能索引
- key: 實際選擇的索引
- key_len: 所選密鑰的長度
- ref: 與索引比較的列
- rows: 估計要檢查的行數
- filtered: 按表條件過濾的行百分比
- Extra: 附加資訊
其中,有個 type 字段,它的含義大概如下:
- eq_ref: 使用到了 UNIQUE 或 PRIMARY KEY 索引
- ref: 顯示索引的哪一列被使用了
- ref_or_null: 對 Null 進行了索引優化
- range: 索引範圍檢索
- index: 索引掃描
- unique_subquery: 使用了 in 子查詢,裡面涉及了主鍵字段
- index_subquery: 使用了 in 子查詢,裡面涉及了非唯一索引
- fulltext: 全文索引
- all: 全表掃描資料
從上面大概就能分析出索引的使用情況了,如果是 all,那就是沒有用到索引了。
索引的的底層
前面提到過索引的種類時,細分了 B+ 樹、hash 索引、全文索引這三類。現在我們來具體看下對應的底層結構吧。
B+樹
在 B+ 樹之前還有二叉搜尋樹和 B 樹,我們來一步一步演化,看看有什麼不同,先來看二叉搜尋樹:
當要進行查找時,會按小于往左搜尋,大于往右搜尋的規則去尋找。二叉搜尋樹隻存了單個節點值,樹的高度有可能會很高,如果用來存儲索引資料,效率将會降低,不适用于 mysql 的索引,我們來看看 B 樹吧:
一個節點可以存儲多個資料值。當然,在插入删除時需要做對應的拆分或合并動作。
而且 B 樹允許在非葉子節點也存儲具體資料,這意味着在掃描搜尋時也會将資料加載進來,這無疑增加了磁盤 IO。
對于磁盤 IO 要求高的 mysql 而言,B 樹也很不劃算,是以 B+ 樹成了最好的選擇,它長這樣的:
B+ 樹隻在葉子節點存儲具體的資料(注:資料可以是真正的行資料也可以是定位到行資料的指針位址),而非葉子節點值隻存放索引資料,這樣可以降低磁盤 IO,還能充分利用磁盤的預讀功能,批量的加載索引資料。
hash 索引
hash 索引将列通過 hash 運算得到 hash code,然後将 hash code 跟資料行的指針位址關聯在一起,下次查找時隻需查找對應 hash code 的資料行位址即可。
hash 索引非常的緊湊,查找速度很快,适用于記憶體存儲引擎的應用。不過它隻能精确查詢,不支援範圍查找,也不能直接進行排序。限制還是挺多的。
全文索引
全文索引主要是用于文檔查找,像我們可能會從多篇文章中查找包含某些詞語的文章,這時就可以使用全文索引了。雖然 like 也可以使用,但是效率太低了。
全文索引在接收到文檔時,會對它進行分詞處理,以擷取到關鍵詞。然後會将關鍵詞和屬于這個文檔的 id 關聯起來。
下次查找,就會先到關鍵詞清單裡找到關聯的文檔 id ,最後利用文檔 id 去查找到文檔資料。
總結
索引所涉及的知識點還是挺多的,從了解索引到用好索引再到優化索引,我想這應該是我們進行查詢優化的必經之路吧。希望本文能為大家帶來不一樣的認識,也歡迎一起探讨!