首先,我們常說的索引類型有:主鍵索引、唯一索引、普通索引、前綴索引、全文索引,甚至還有聚簇索引、覆蓋索引等。
其中,唯一索引、普通索引、前綴索引等索引屬於二級索引。
主鍵索引(Primary Key)
數據表的主鍵列使用的就是主鍵索引。
一張數據表有只能有一個主鍵,並且主鍵不能為 null,不能重復。
在 MySQL 的 InnoDB 的表中,當沒有顯示的指定表的主鍵時,InnoDB 會自動先檢查表中是否有唯一索引的字段,如果有,則選擇該字段為默認的主鍵,否則 InnoDB 將會自動創建一個 6Byte 的自增主鍵。
二級索引(輔助索引)
二級索引又稱為輔助索引,是因為二級索引的葉子節點存儲的數據是主鍵。也就是說,通過二級索引,可以定位主鍵的位置。
常用的二級索引包括:
- 唯一索引(Unique Key) :唯一索引也是一種約束。唯一索引的屬性列不能出現重復的數據,但是允許數據為 NULL,一張表允許創建多個唯一索引。 建立唯一索引的目的大部分時候都是為了該屬性列的數據的唯一性,而不是為了查詢效率。
- 普通索引(Index) :普通索引的唯一作用就是為了快速查詢數據,一張表允許創建多個普通索引,並允許數據重復和 NULL。
- 前綴索引(Prefix) :前綴索引只適用於字符串類型的數據。前綴索引是對文本的前幾個字符創建索引,相比普通索引建立的數據更小, 因為只取前幾個字符。

說到二級索引,我們要先知道聚簇索引和非聚簇索引。
聚集索引即索引結構和數據一起存放的索引。主鍵索引屬於聚集索引。(InnoDB中也只有主鍵索引才能是聚簇索引)
非聚集索引即索引結構和數據分開存放的索引。
二級索引就屬於非聚集索引。
非聚集索引的優點
更新代價比聚集索引要小 。非聚集索引的更新代價就沒有聚集索引那么大了,非聚集索引的葉子節點是不存放數據的
非聚集索引的缺點
- 跟聚集索引一樣,非聚集索引也依賴於有序的數據
- 可能會二次查詢(回表) :這應該是非聚集索引最大的缺點了。 當查到索引對應的指針或主鍵后,可能還需要根據指針或主鍵再到數據文件或表中查詢。

非聚集索引一定回表查詢嗎(覆蓋索引)?
也不一定,使用覆蓋索引就不會回表查詢。
試想一種情況,用戶准備使用 SQL 查詢用戶名,而用戶名字段正好建立了索引。
SELECT name FROM table WHERE name='guang19';
那么這個索引的 key 本身就是 name,查到對應的 name 直接返回就行了,無需回表查詢。
覆蓋索引
如果一個索引包含(或者說覆蓋)所有需要查詢的字段的值,我們就稱之為“覆蓋索引”。我們知道在 InnoDB 存儲引擎中,如果不是主鍵索引,葉子節點存儲的是主鍵+列值。最終還是要“回表”,也就是要通過主鍵再查找一次。這樣就會比較慢覆蓋索引就是把要查詢出的列和索引是對應的,不做回表操作!
覆蓋索引即需要查詢的字段正好是索引的字段,那么直接根據該索引,就可以查到數據了, 而無需回表查詢。
如主鍵索引,如果一條 SQL 需要查詢主鍵,那么正好根據主鍵索引就可以查到主鍵。
再如普通索引,如果一條 SQL 需要查詢 name,name 字段正好有索引, 那么直接根據這個索引就可以查到數據,也無需回表。
覆蓋索引:

創建索引的注意事項
1.選擇合適的字段創建索引:
- 不為 NULL 的字段 :索引字段的數據應該盡量不為 NULL,因為對於數據為 NULL 的字段,數據庫較難優化。如果字段頻繁被查詢,但又避免不了為 NULL,建議使用 0,1,true,false 這樣語義較為清晰的短值或短字符作為替代。
- 被頻繁查詢的字段 :我們創建索引的字段應該是查詢操作非常頻繁的字段。
- 被作為條件查詢的字段 :被作為 WHERE 條件查詢的字段,應該被考慮建立索引。
- 頻繁需要排序的字段 :索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間。
- 被經常頻繁用於連接的字段 :經常用於連接的字段可能是一些外鍵列,對於外鍵列並不一定要建立外鍵,只是說該列涉及到表與表的關系。對於頻繁被連接查詢的字段,可以考慮建立索引,提高多表連接查詢的效率。
2.被頻繁更新的字段應該慎重建立索引。
雖然索引能帶來查詢上的效率,但是維護索引的成本也是不小的。 如果一個字段不被經常查詢,反而被經常修改,那么就更不應該在這種字段上建立索引了。
3.盡可能的考慮建立聯合索引而不是單列索引。
因為索引是需要占用磁盤空間的,可以簡單理解為每個索引都對應着一顆 B+樹。如果一個表的字段過多,索引過多,那么當這個表的數據達到一個體量后,索引占用的空間也是很多的,且修改索引時,耗費的時間也是較多的。如果是聯合索引,多個字段在一個索引上,那么將會節約很大磁盤空間,且修改數據的操作效率也會提升。
4.注意避免冗余索引 。
冗余索引指的是索引的功能相同,能夠命中索引(a, b)就肯定能命中索引(a) ,那么索引(a)就是冗余索引。如(name,city )和(name )這兩個索引就是冗余索引,能夠命中前者的查詢肯定是能夠命中后者的 在大多數情況下,都應該盡量擴展已有的索引而不是創建新索引。
5.考慮在字符串類型的字段上使用前綴索引代替普通索引。
前綴索引僅限於字符串類型,較普通索引會占用更小的空間,所以可以考慮使用前綴索引帶替普通索引。
參考地址:
JavaGuide大佬的博客
