覆蓋索引有何用?
通常開發人員會根據查詢的where條件來建立合適的索引,但是優秀的索引設計應該考慮到整個查詢。其實mysql可以使用索引來直接獲取列的資料。如果索引的葉子節點包含了要查詢的資料,那麼就不用回表查詢了,也就是說這種索引包含(亦稱覆蓋)所有需要查詢的欄位的值,我們稱這種索引為覆蓋索引。
注:引入資料表t_user,插入約1千萬條記錄,用作下文例子使用。
1工欲善其事,必先利其器
explain命令是檢視查詢優化器如何決定執行查詢的主要方法。要使用此命令,只需要在select關鍵字之前新增這個命令即可。當執行查詢時,它會返回資訊,顯示出執行計劃中的每一部分和執行的次序,而並非真正執行這個查詢。如圖1.1所示,是執行explain的顯示結果,其中sql語句中的\G表示將輸出按列顯示:
圖1.1 explain顯示查詢執行計劃
當發起一個被索引覆蓋的查詢時,在explain的Extra列可以看到 Using index的標識。
2場景:查詢表中name列有值的記錄數
圖2.1 查詢name列有值的記錄數
圖2.2 執行計劃
如上圖2.1所示,其中查詢語句用SQL_NO_CACHE關鍵字來禁止快取查詢結果。此查詢耗時6.43秒。從圖3的執行計劃得知,type:ALL,表示MySQL掃描整張表,從頭到尾去找到需要的行。下面對此查詢列建立索引。
圖2.3 為name列建立索引
圖2.4 重新執行的查詢sql
圖2.5 重新檢視執行計劃
如圖2.3所示,為name列建立索引之後,重新執行查詢。此時查詢耗時3.80秒,比未加索引提高了2.63秒。從圖2.5的查詢計劃可知,type:index,這個跟全表掃描一樣,只是MySQL掃描表時按索引次序進行而不是行。但是看到Extra:Using index,說明MySQL正在使用覆蓋索引,它只掃描索引的資料,而不是按索引次序的每一行。它比按索引次序全表掃描的開銷少很多。
3分頁查詢email
圖3.1 分頁查詢email
圖3.2 分頁查詢執行計劃
從圖3.1可知,分頁查詢耗時53.99,如圖3.2所示,type:All,說明MySQL進行了全表掃描。下面在password和email列上建立聯合索引。
圖3.3 新增聯合索引
圖3.4 重新分頁查詢
圖3.5 重新執行查詢計劃
如圖3.4所示,分頁查詢基本不耗時間。從圖3.5可知,Extra:Using index,MySQL使用了覆蓋索引進行查詢。查詢效率得到極大的提升。
4覆蓋索引總結
回想一下,如果查詢只需要掃描索引而無須回表,將帶來諸多好處。
(1)索引條目通常遠小於資料行大小,如果只讀取索引,MySQL就會極大地減少資料訪問量。
(2)索引按照列值順序儲存,對於I/O密集的範圍查詢會比隨機從磁碟中讀取每一行資料的I/O要少很多。
(3)InnoDB的輔助索引(亦稱二級索引)在葉子節點中儲存了行的主鍵值,如果二級索引能夠覆蓋查詢,則可不必對主鍵索引進行二次查詢了。
覆蓋索引就是從索引中直接獲取查詢結果,要使用覆蓋索引需要注意select查詢列中包含在索引列中;where條件包含索引列或者複合索引的前導列;查詢結果的欄位長度儘可能少。