索引是在存儲引擎層實現的,且在 MySQL
不一樣存儲引擎中的實現也不一樣,本篇文章介紹的是 MySQL
的 InnoDB
的索引。sql
下文將以這張表爲例開展。緩存
# 建立一個主鍵爲 id 的表,表中有字段 k,而且在 k 上有索引。 create table T( `id` int(11) AUTO_INCREMENT, `k` int(11) NOT NULL, `name` varchar(16), `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY (`k`), KEY `name_age` (`name`,`age`) ) ENGINE=InnoDB; # 插入多條數據 insert into T values (100, 'Bob'),(200, 'Peter'),(300,'Mary');
在 InnoDB
中,表都是根據主鍵順序以索引的形式存放的,也就是數據放在主鍵索引上,其餘索引上保存的是主鍵 id
,這種存儲方式的表稱爲索引組織表。性能
InnoDB
使用了 B+樹
索引模型,因此數據都是存儲在 B+樹
中的。每個索引在 InnoDB
裏面對應一棵 B+樹
。優化
主鍵索引的葉子節點存的是整行數據。在 InnoDB
裏,主鍵索引也被稱爲聚簇索引。code
非主鍵索引的葉子節點內容是主鍵的值。在 InnoDB
裏,非主鍵索引也被稱爲二級索引、輔助索引。
blog
主鍵查詢方式:只須要搜索 ID
這棵 B+樹
;
普通索引查詢方式:先搜索普通索引的 B+樹
,獲得主鍵索引 ID
的值,再到 ID
索引樹上搜索,這個過程稱爲回表。排序
從空間的角度出發:主鍵列長度儘量短,每一個二級索引的葉子節點是主鍵,主鍵過長會致使二級索引佔用空間更大。
從性能的角度出發:推薦使用自增索引,非自增主鍵在插入和刪除的操做中,會致使頁分裂和頁合併。索引
先看下面這個 sql
:內存
select * from T where k = 100;
這個 sql
語句會在 k
索引樹上找到 k=100
的記錄,取得 ID=15
;
再到 ID
索引樹查到 ID=15
對應的記錄,發生了回表,若是將 sql
語句改成it
select id from T where k = 100;
由於 ID
的值已經在 k
索引樹上了,所以能夠直接提供查詢結果,不須要回表。
若是一個索引包含(或覆蓋)全部須要查詢的字段的值,稱爲覆蓋索引,即只需掃描索引而無須回表。
若是不使用自增 ID
作主鍵,用業務字段直接作主鍵,則須要知足:只有一個索引,且該索引爲惟一索引。
因爲沒有其餘索引,因此不用考慮其餘索引的葉子節點大小的問題,把這個索引設置爲主鍵,避免每次查詢須要搜索兩棵樹。
# 正確作法 alter table T engine=InnoDB # 錯誤作法 alter table T drop primary key; alter table T add primary key(id);
直接刪掉主鍵索引會使得全部的二級索引都失效,而且會用 ROWID
來做主鍵索引。
alter table T drop index k; alter table T add index(k);
索引可能由於刪除,或者頁分裂等緣由,致使數據頁有空洞,重建索引的過程會建立一個新的索引,把數據按順序插入,這樣頁面的利用率最高,達到省空間的目的。
在上面的建表語句中,能夠看到有兩個索引,一個是 k
索引,一個是name_age
索引,不難看出,前者是單列索引,然後者就是聯合索引了。
爲何會有聯合索引呢?當查詢條件爲2個及以上時,好比當常常要用 name
和 age
去查詢數據時:
select * from T where name = 'Job' and age = 28;
建立一個 (name,age)
的聯合索引,至關於建立了 name
和 name、age
這兩個組合的索引,能夠加速檢索。
顧名思義是最左優先,以最左邊的爲起點任何連續的索引都能匹配上。
聯合索引的示例圖以下:
索引項是按照索引定義裏的字段順序來排序的,所以在建立聯合索引時,要根據業務需求,where子句中使用最頻繁的一列放在最左邊。
當已經有了 (a,b) 這個聯合索引後,通常就不須要單獨在 a 上創建索引了。所以,第一原則是,若是經過調整順序,能夠少維護一個索引,那麼這個順序每每就是須要優先考慮採用的。
當建立 (a,b,c)
聯合索引時,至關於建立了 (a)
單列索引、(a,b)
聯合索引以及 (a,b,c)
聯合索引。
想要索引生效的話,只能使用 a
和 a,b
和 a,b,c
三種組合;a,c
組合也能夠,但實際上只用到了 a
的索引,並無用到 c
。
MySQL 5.6 引入索引下推優化(index condition pushdown),能夠在索引遍歷過程當中,對索引中包含的字段先作判斷,直接過濾掉不知足條件的記錄,減小回表次數。
好比根據(name,age)
聯合索引查詢全部知足名稱以「張」開頭的索引,而後直接再篩選出年齡小於等於10的索引,以後再回表查詢全行數據。
注意:innodb
引擎的表,索引下推只能用於二級索引。
普通索引容許被索引的數據列包含重複的值,建立惟一索引的目的通常不是爲了提升訪問速度,而只是爲了不數據重複。
當須要更新一個數據頁時,若是數據頁在內存中就直接更新,而若是這個數據頁尚未在內存中的話,在不影響數據一致性的前提下,InnoDB 會將這些更新操做緩存在 change buffer 中,這樣就不須要從磁盤中讀入這個數據頁了。在下次查詢須要訪問這個數據頁的時候,將數據頁讀入內存,而後執行 change buffer 中與這個頁有關的操做。經過這種方式就能保證這個數據邏輯的正確性。
不推薦使用惟一索引,這是由於:
從查詢的角度出發:
從更新的角度出發:
redolog
;普通索引將更新操做寫入 change buffer
,通知執行器更新完成;在下次讀相關記錄的時候,先把原記錄讀取到內存,再將 change buffer
上的操做在內存記錄上回放,並寫 redolog
;結論:change buffer 只適用於普通索引,而不適用於惟一索引。
關於索引的知識點比較多,但每看一遍專欄都會有新的收穫。fighting!💪