深入淺出索引
深入淺出索引
本文是在看極客時間《Mysql實戰45講》時記的筆記,整理下加深理解。
簡單來說,資料庫索引就是為了提高資料庫查詢的效率,就像書的目錄一樣,可以根據目錄快速的找到其中的某一個知識點。
索引模型
- 雜湊表
- 有序陣列
- 搜尋樹
簡單的介紹下以上三種模型:
==雜湊表==是一種以鍵-值(key-value)儲存的資料結構,我們只要輸入待查詢的key值,就可以找到其對應的值value,雜湊的思路很簡單,把值放在數組裡,通過一個雜湊函式把key換算成一個確定的位置,然後把value放在陣列的這個位置。不可避免的情況下,多個Key值經過雜湊運算會出現同一個值的情況,處理這種情況的一種方法是拉出一個連結串列。
由於雜湊表內部的排序並不是遞增的,所以新增元素的時候速度會很快,但缺點是因為不是有序的, 所以雜湊表做區間查詢的速度是很慢的。所以,雜湊表這種結構只適用於只有等值查詢的場景,比如Memcached以及其他Nosql引擎。
==有序陣列==在等值查詢和範圍查詢場景中的效能都非常優秀。但是在需要更新資料的時候就很麻煩了,如果在中間插入一個記錄就必須挪動後面所有的記錄,成本太高。所以 有序陣列只適用於靜態儲存引擎。
N叉樹在讀寫上的效能優點,以及適配磁碟的訪問模式,已經被廣泛應用於資料庫引擎中了。
不管是雜湊還是有序陣列,或者 N 叉樹,它們都是不斷迭代、不斷優化的產物或者解決方案。在我們心裡要有個概念,資料庫底層儲存的核心就是基於這些資料模型的,每碰到一個新的資料庫,我們都應先關注他的資料模型,這樣才能從理論上分析出這個資料庫的應用場景。
InnoDB的索引模型
在InnoDB中,表都是根據主鍵順序以索引的形式存放的,這種儲存方式的表稱為索引組織表。InnoDB使用了B+樹索引模型,所以資料都是儲存在B+樹中的。
每一個索引在InnoDB裡面都對應一顆B+樹。
假設我們有一個主鍵列為ID的表,表中有欄位K,並在K上有索引。
這個表的建表語句:
mysql> create table T( id int primary key, k int not null, name varchar(16), index (k))engine=InnoDB;
表中 R1~R5 的 (ID,k) 值分別為 (100,1)、(200,2)、(300,3)、(500,5) 和 (600,6),兩棵樹的示意圖如下:
從圖中我們可以看出來,根據葉子節點的內容,索引分為主鍵索引和非主鍵索引。
主鍵索引的葉子節點存放的是整行的資料,非主鍵索引的葉子節點存放的是主鍵的值。
根據上面的索引結構說明,我們可以得出一個問題,基於主鍵索引和普通索引的查詢區別:
select * from T where ID = 500; select * from T where k = 5;
也就是說基於非主鍵索引查詢會多掃描一次索引樹。
索引維護
B+樹為了維護索引的有序性,在插入新值的時候需要做必要的維護。
建表時,儘量保持有自增主鍵。每次插入一條新記錄,都是追加操作,都不涉及到挪動其他記錄,也不會觸發葉子節點的分裂。
而有業務邏輯的欄位做主鍵,則往往不容易保證有序插入。
同時主鍵的長度越小,普通索引的葉子節點就越小,普通索引佔用的空間就越小。
所以從效能和儲存空間來看,自增主鍵往往是更合理的選擇。
覆蓋索引
如果執行的語句是 select ID from T where k between 3 and 5
,這時只需要查 ID 的值,而ID 的值已經在 k 索引樹上了,因此可以直接提供查詢結果,不需要回表。也就是說,在這個查詢裡面,索引 k 已經“覆蓋了”我們的查詢需求,我們稱為覆蓋索引。
由於覆蓋索引可以減少樹的搜尋次數,顯著提升查詢效能,所以使用覆蓋索引是常用的效能優化手段。
最左字首原則
第一原則是,如果通過調整順序,可以少維護一個索引,那麼這個順序往往就是需要優先考慮採用的。
其次考慮的就是空間,比如name 欄位是比 age 欄位大的 ,那我就建議你建立一個(name,age) 的聯合索引和一個 (age) 的單欄位索引。
索引下推
在Mysql5.6之前,只能從最左字首查詢到ID開始一個個回表,到主鍵索引上找出資料行,再對比欄位值。
Mysql5.6之後,引入索引下推的優化,可以在遍歷過程中,對索引中包含的欄位先做判斷,直接過濾掉不滿足條件的記錄,減少回表次數。
總之在滿足語句需求的情況下,儘量地減少訪問資源是資料庫設計的重要原則之一。我們在使用資料庫的時候,尤其在設計表結構時,也要以減少資源消耗為目標。
參考資料
極客時間《Mysql實戰45講》