平時我們要優化 mysql 查詢效率的時候,最常見的就是給表加上合適的索引了,那今天就來聊聊為什麼加了索引就快了呢。
很多人會說索引就相當於一本書的目錄,通過目錄來找書中的某一頁,確實是很快的,如果沒有目錄,就需要一頁一頁的去翻書了,大大降低了效率。這個比喻其實還挺恰當的,也是一個很經典的索引比喻了。
在 InnoDB 中,每個索引其實都是一顆 B+ 樹,主鍵索引稱作聚簇索引,其他非主鍵索引稱作二級索引,每個表中每一行的記錄值都完整的儲存在主鍵索引的葉子節點上,二級索引的葉子節點儲存的是主鍵的值。
mysql 索引其實就是一顆 B+ 樹。 juejin.im/post/5c8dce…
也就是說每個表至少都有一個主鍵索引,而且表中所有的資料行都是存放在主鍵索引這個 B+ 樹的葉子節點上的。如果你給表的其他欄位加了索引的話,這個索引就是二級索引了,二級索引也是 B+ 樹。
二級索引和主鍵索引的不同之處在於其葉子節點上儲存的值不一樣,表中所有欄位的值都被完整的儲存在主鍵索引的葉子節點上,但是二級索引的葉子節點只儲存對應主鍵的值。
我們舉一個具體的例子來還原下這個問題。首先提供一個表,表中有三個欄位 (id,k,m),分別給主鍵 id 和欄位 k 建立主鍵索引和二級索引。
mysql> create table t( id int primary key, k int not null, m int(11), index (k)) engine=InnoDB;
然後再給表中插入幾條資料,用R1、R2、R3、R4、R5表示,插入的具體資料如下:R1~R5 的 (id,k,m) 值分別為 (100,1,1000)、(200,2,2000)、(300,3,3000)、(500,5,5000)、(600,6,6000)。
剛剛有說過,主鍵索引葉子節點上儲存完整的整行記錄值,二級索引葉子節點儲存主鍵的值,所以上面這個表 t 的資料在 mysql 底層的儲存就如下示意圖。
表 t 一共有 3 個欄位,欄位 m 上沒有索引,也就是說表 t 上有兩個索引,所以對應有 2 個 B+ 樹,一個表上有多少個索引,其實就會有多少個 B+ 樹。
接下來再來看下有索引和沒有索引的查詢區別。
比如下面這條 sql 語句,顯然沒有可用的索引,所以只能走全表掃描了,即把主鍵索引上的葉子節點從頭到尾都掃描一遍,然後每掃描到一行把欄位 m 的值拿出來再比對一下,篩選出滿足條件的記錄,這個查詢是非常低效的。
select * from t where m > 1000 and m < 3000;
再來看另一條 sql 語句,這個語句可以使用索引 k,所以該查詢會先到二級索引 k 這個 B+ 樹上,快速找到滿足要求的葉子節點,而這裡的葉子節點上只儲存了主鍵的值,所以還需要通過獲得的主鍵 ID 值再回到主鍵索引上查出所有欄位的值,這個過程稱作回表。
select * from t where k > 3 and k < 6;
這就是為什麼加了索引後,mysql 查詢會變快的原因了,其實剛提到的這個回表過程還可以再優化的,就是利用覆蓋索引,後面的文章我們再詳細說。
有問題歡迎留言交流,原創不易,如果文章對你有幫助,希望能給文章點個贊,感謝支援,另外文中圖片來源於極客時間專欄。