探究問題:爲了提高查詢效率,添加索引很常見,但是建立多個單列索引與聯合索引有何區別呢?
準備工作
- 數據庫:MySql 5.7.11-log
- 創建表
create table temp_index_test(
indexId int,
indexName varchar(10),
indexValue int,
is_del int
);
- 插入數據
insert temp_index_test value (1,'索引1',1,0);
insert temp_index_test value (2,'索引2',2,0);
insert temp_index_test value (3,'索引3',3,0);
insert temp_index_test value (4,'索引4',4,0);
insert temp_index_test value (5,'索引5',5,0);
- 查詢數據插入情況
聯合索引驗證
添加聯合索引 indexId_indexName
- 只使用indexId,查看執行計劃
explain select * from temp_index_test where indexId='1';
索引生效!
- 只使用indexName,查看執行計劃
explain
select *
from temp_index_test where indexName='索引1';
索引未生效!
- 同時使用indexId,indexName,並且indexId在前,indexName在後
explain
select *
from temp_index_test where indexId=1 and indexName='索引1';
索引生效!
- 同時使用indexId,indexName,並且indexName在前,indexId在後
explain
select *
from temp_index_test where indexName='索引1' and indexId=1;
索引生效!
中間添加一個沒有添加索引的字段
explain
select *
from temp_index_test where indexId=1 and indexValue=1 and indexName='索引1';
索引生效!
總結:
- 利用索引中的附加列,您可以縮小搜索的範圍,但使用一個具有兩列的索引不同於使用兩個單獨的索引。複合索引的結構與電話簿類似,人名由姓和名構成,電話簿首先按姓氏對進行排序,然後按名字對有相同姓氏的人進行排序。如果您知道姓,電話簿將非常有用;如果您知道姓和名,電話簿則更爲有用,但如果您只知道名不姓,電話簿將沒有用處。
- 所以說創建複合索引時,應該仔細考慮列的順序。對索引中的所有列執行搜索或僅對前幾列執行搜索時,複合索引非常有用;僅對後面的任意列執行搜索時,複合索引則沒有用處。
- 如何建立聯合索引順序?最左前綴原則,可以理解爲在複合索引中,越是左邊越好
因爲分別對A,B,C三個列建聯合索引index,實際上是建立3個索引,每個索引都包含A,組合分別爲【A|A,B,C|A,B】
AC組合也可以,只不過只用到了A的索引,C的沒有用到!
【注意】
最左前綴原則
- 如果第一個字段是範圍查詢需要單獨建一個索引
- 在創建聯合索引時,要根據業務需求,where子句中使用最頻繁的一列放在最左邊。這樣的話擴展性較好
- 【列基數】,是指它所容納的所有非重複值的個數,可以理解爲 distinct(某列)後的結果,比如某個列包含值1,3,7,4,7,3,那麼它的基數就是4了,列基數越大,重複值越少,需要建索引,因爲如果某列的值都是重複的就沒必要建索引了是(`DistriType`,`DistriButTime`) 這樣建?還是這樣建 (`DistriButTime`,`DistriType`) ?
count(distinct DistriType) 與 count(distinct DistriButTime)比較猜測肯定是前者>後者,所以聯合索引把DistriTyp放左邊更優
多個單列索引驗證
添加單列索引indexId,indexName
- 使用indexId查詢
explain
select *
from temp_index_test where indexid=1;
索引生效!
- 使用IndexId,indexName查詢
explain
select *
from temp_index_test where indexid=1 and indexName='索引1';
只使用了index索引!
【爲什麼沒有用上indexName索引】
那麼爲什麼沒有用上呢?按照我們的理解,2個字段都加索引了,無論怎麼排列組合查詢,應該都能利用到這2個索引纔對!
其實這裏其實涉及到了mysql優化器的優化策略!當多條件聯合查詢時,優化器會評估用哪個條件的索引效率最高!它會選擇最佳的索引去使用,也就是說,此處indexId、indexName這三個索引列都能用,只不過優化器判斷只需要使用indexId這一個索引就能完成本次查詢,故最終explain展示的key爲indexId。
當然,如果優化器判斷本次查詢非要全使用三個索引才能效率最高,那麼explain的key就會是indexId、indexName,都會生效!
【是不是跟索引的先後順序有關呢】
將indexId與indexName調換位置,測試一下
#兩個索引都使用
explain
select indexName
from temp_index_test where indexName='索引1' and indexId = 1 and is_del=0;
依舊使用的索引爲indexId,所以跟順序無關!
- 將索引條件連接由AND換成OR
explain
select indexName
from temp_index_test where indexName='索引1' or indexId = 1;
沒有使用索引!
涉及到的其他點:
- 需要加索引的字段,要在where條件中
- 數據量少的字段不需要加索引;因爲建索引有一定開銷,如果數據量小則沒必要建索引(速度反而慢)
- 避免在where子句中使用or來連接條件,因爲如果倆個字段中有一個沒有索引的話,引擎會放棄索引而產生全表掃描
- 聯合索引比對每個列分別建索引更有優勢,因爲索引建立得越多就越佔磁盤空間,在更新數據的時候速度會更慢。另外建立多列索引時,順序也是需要注意的,應該將嚴格的索引放在前面,這樣篩選的力度會更大,效率更高
- 有人說where查詢是按照從左到右的順序,所以篩選力度大的條件儘量放前面。網上百度過,很多都是這種說法,但是據我研究,mysql執行優化器會對其進行優化,當不考慮索引時,where條件順序對效率沒有影響,真正有影響的是是否用到了索引!但是推薦按照篩選力度大的排前面,mysql優化器優化也會耗費性能!
- 同時存在聯合索引和單列索引(字段有重複的),當一個表有多條索引可走時, Mysql 根據查詢語句的成本來選擇走哪條索引
參考: