MySQL 覆盖索引
生活随笔
收集整理的這篇文章主要介紹了
MySQL 覆盖索引
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文主要概述mysql的覆蓋索引,以及幾種常見的優化場景
內容概要
- ?聚集索引和輔助索引
- ?什么是覆蓋索引
- ?幾種優化場景??
- ?總體建議
聚集索引和輔助索引
- 聚集索引(主鍵索引)
- 輔助索引(二級索引)
輔助索引檢索數據圖示如下
由于檢索數據時,總是先獲取到書簽值(主鍵值),再返回查詢,因此輔助索引也被稱之為二級索引
什么是覆蓋索引
覆蓋索引(covering index)指一個查詢語句的執行只需要從輔助索引中就可以得到查詢記錄,而不需要查詢聚集索引中的記錄。也可以稱之為實現了索引覆蓋。 那么,優點顯而易見。輔助索引不包含一整行的記錄,因此可以大大減少IO操作。覆蓋索引是mysql dba常用的一種SQL優化手段先看一個簡單示例
從執行計劃看到,這個SQL語句只通過索引,就取到了所需要的數據,這個過程,就稱為索引覆蓋
幾種優化場景
1、無where條件的查詢優化
如下這個查詢
執行計劃中,type為ALL,代表進行了全表掃描,掃描的行數也與表的行數一致
如何改進?優化措施很簡單,就是對這個查詢列建立索引。如下, mysql> alter table t1 add key(staff_id);
我們再看一下優化之后的效果
- 執行計劃
- 查詢消耗
從時間消耗上來看,才100W的數據,已經有了比較明顯的差別了
執行計劃解讀如下: Possible_keys為null,說明沒有where條件時優化器無法通過索引檢索數據; 但是這里使用了索引的另外一個優點,即從索引中獲取數據,減少了讀取的數據塊的數量 無where條件的查詢,可以通過索引來實現索引覆蓋查詢,但前提條件是,查詢返回的字段數足夠少,更不用說select *之類的了。畢竟,建立key length過長的索引,始終不是一件好事情。
2、二次檢索優化
如下這個查詢,
mysql> select sql_no_cache rental_date from t1 where inventory_id<80000;
…
…
| 2005-08-23 15:08:00 |
| 2005-08-23 15:09:17 |
| 2005-08-23 15:10:42 |
| 2005-08-23 15:15:02 |
| 2005-08-23 15:15:19 |
| 2005-08-23 15:16:32 |
+---------------------+
79999 rows in set (0.13 sec) 執行計劃: mysql> explain select sql_no_cache rental_date from t1 where inventory_id<80000\G *************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1type: range possible_keys: inventory_idkey: inventory_idkey_len: 3ref: NULLrows: 153734Extra: Using index condition 1 row in set (0.00 sec)
? 從執行計劃,我們看到,這個SQL其實是使用到了索引的,雖然查詢的數據量很大,但是相對比全表掃描的性能消耗,優化器還是選擇了索引。
更優的改進措施? 從上面執行計劃中,我們發現Extra信息為Using index condition而不是Using index,這說明,使用的檢索方式為二級檢索,即79999個書簽值被用來進行回表查詢。可想而知,還是會有一定的性能消耗的
嘗試針對這個SQL建立聯合索引,如下 mysql> alter table t1 add key(inventory_id,rental_date);
? 這個聯合索引前置列為where子句的檢索字段,第二個字段為查詢返回的字段。下面來看下效果如何。
為避免優化器對索引的選擇出現偏差,我們首先收集一下統計信息 mysql> analyze table t1\G *************************** 1. row ***************************Table: sakila.t1Op: analyze Msg_type: status Msg_text: OK 1 row in set (0.03 sec)
執行計劃 mysql> explain select sql_no_cache rental_date from t1 where inventory_id<80000\G *************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1type: range possible_keys: inventory_id,inventory_id_2key: inventory_id_2key_len: 3ref: NULLrows: 1628841 row in set (0.00 sec) 同樣是使用索引,但這里的提示信息為Using index而不是Using index condition。這代表沒有了回表查詢的過程,也就是實現了索引覆蓋
查詢消耗 mysql> select sql_no_cache rental_date from t1 where inventory_id<80000; … … | 2005-08-23 15:08:00 | | 2005-08-23 15:09:17 | | 2005-08-23 15:10:42 | | 2005-08-23 15:15:02 | | 2005-08-23 15:15:19 | | 2005-08-23 15:16:32 | +---------------------+ 79999 rows in set (0.09 sec) ? 從執行時間上來看,快了大約40ms,雖然只有40ms,但在實際的生產環境下,卻可能會因系統的總體負載被無限放大。
和前面場景限制類似,當where條件與查詢字段總數較少的情況下,使用這種優化建議,是個不錯的選擇。
3、分頁查詢優化
分頁查詢的優化,相信大部分的DBA同學都碰到過,通常比較常規的優化手段就是查詢改寫,這里主要介紹一下新的思路,就是通過索引覆蓋來優化
如下這個查詢場景 mysql> select tid,return_date from t1 order by inventory_id limit 50000,10; +-------+---------------------+ | tid | return_date | +-------+---------------------+ | 50001 | 2005-06-17 23:04:36 | | 50002 | 2005-06-23 03:16:12 | | 50003 | 2005-06-20 22:41:03 | | 50004 | 2005-06-23 04:39:28 | | 50005 | 2005-06-24 04:41:20 | | 50006 | 2005-06-22 22:54:10 | | 50007 | 2005-06-18 07:21:51 | | 50008 | 2005-06-25 21:51:16 | | 50009 | 2005-06-21 03:44:32 | | 50010 | 2005-06-19 00:00:34 | +-------+---------------------+ 10 rows in set (0.75 sec)
在未優化之前,我們看到它的執行計劃是如此的糟糕 mysql> explain select tid,return_date from t1 order by inventory_id limit 50000,10\G *************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1type: ALL possible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 10236751 row in set (0.00 sec) 全表掃描,加上額外的排序,相信產生的性能消耗是不低的
如何通過覆蓋索引優化呢? 我們創建一個索引,包含排序列以及返回列,由于tid是主鍵字段,因此,下面的復合索引就包含了tid的字段值
mysql> alter table t1 add index liu(inventory_id,return_date); Query OK, 0 rows affected (3.11 sec) Records: 0 Duplicates: 0 Warnings: 0mysql> analyze table t1\G *************************** 1. row ***************************Table: sakila.t1Op: analyze Msg_type: status Msg_text: OK 1 row in set (0.04 sec)
那么,效果如何呢?
mysql> select tid,return_date from t1 order by inventory_id limit 50000,10; +-------+---------------------+ | tid | return_date | +-------+---------------------+ | 50001 | 2005-06-17 23:04:36 | | 50002 | 2005-06-23 03:16:12 | | 50003 | 2005-06-20 22:41:03 | | 50004 | 2005-06-23 04:39:28 | | 50005 | 2005-06-24 04:41:20 | | 50006 | 2005-06-22 22:54:10 | | 50007 | 2005-06-18 07:21:51 | | 50008 | 2005-06-25 21:51:16 | | 50009 | 2005-06-21 03:44:32 | | 50010 | 2005-06-19 00:00:34 | +-------+---------------------+ 10 rows in set (0.03 sec) 可以發現,添加復合索引后,速度提升0.7s!
我們看一下改進后的執行計劃 mysql> explain select tid,return_date from t1 order by inventory_id limit 50000,10\G *************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1type: index possible_keys: NULLkey: liukey_len: 9ref: NULLrows: 500101 row in set (0.00 sec) 執行計劃也可以看到,使用到了復合索引,并且不需要回表
接下來,我們對比一下,索引覆蓋與常規優化手段的效果差異
為了讓結果更明顯,我將查詢修改為 limit 800000,10
現在看一下,通過覆蓋索引查詢的性能消耗 mysql> select tid,return_date from t1 order by inventory_id limit 800000,10; +--------+---------------------+ | tid | return_date | +--------+---------------------+ | 800001 | 2005-08-24 13:09:34 | | 800002 | 2005-08-27 11:41:03 | | 800003 | 2005-08-22 18:10:22 | | 800004 | 2005-08-22 16:47:23 | | 800005 | 2005-08-26 20:32:02 | | 800006 | 2005-08-21 14:55:42 | | 800007 | 2005-08-28 14:45:55 | | 800008 | 2005-08-29 12:37:32 | | 800009 | 2005-08-24 10:38:06 | | 800010 | 2005-08-23 12:10:57 | +--------+---------------------+
與之對比的是如下改寫SQL方式
改寫后的sql如下,思想是通過索引消除排序 select a.tid,a.return_date from t1 a inner join (select tid from t1 order by inventory_id limit 800000,10) b on a.tid=b.tid;
并在此基礎上,我們為inventory_id列創建索引,并刪除之前的覆蓋索引 mysql> alter table t1 add index idx_inid(inventory_id),drop index liu; 然后收集統計信息。
查詢消耗如下
mysql> select a.tid,a.return_date from t1 a inner join (select tid from t1 order by inventory_id limit 800000,10) b on a.tid=b.tid; +--------+---------------------+ | tid | return_date | +--------+---------------------+ | 800001 | 2005-08-24 13:09:34 | | 800002 | 2005-08-27 11:41:03 | | 800003 | 2005-08-22 18:10:22 | | 800004 | 2005-08-22 16:47:23 | | 800005 | 2005-08-26 20:32:02 | | 800006 | 2005-08-21 14:55:42 | | 800007 | 2005-08-28 14:45:55 | | 800008 | 2005-08-29 12:37:32 | | 800009 | 2005-08-24 10:38:06 | | 800010 | 2005-08-23 12:10:57 | +--------+---------------------+ ? 可以看到,這種優化手段較前者時間消耗多了大約140ms。 這種優化手段雖然使用索引消除了排序,但是還是要通過主鍵值回表查詢。因此,在select返回列較少或列寬較小的時候,我們可以通過建立復合索引的方式優化分頁查詢,效果更佳,因為它不需要回表!
總體建議
索引具有以下兩大用處: 1、通過索引檢索僅需要數據 2、從索引中直接獲取查詢結果覆蓋索引的優勢,就是利用到索引的第二大用處,在某些場景下,具有意想不到的優化效果。個人總結如下:
- Select查詢的返回列包含在索引列中
- 有where條件時,where條件中要包含索引列或復合索引的前導列
- 查詢結果的總字段長度可以接受
總結
以上是生活随笔為你收集整理的MySQL 覆盖索引的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [MySQL] 索引与性能(3)- 覆盖
- 下一篇: MySQL Index详解