聯合索引這點事兒
我們從頭開始
ER圖
建表
按照ER圖,建立資料庫和表,並且進行測試資料的填充。(建表sql和填充指令碼的檔案可公眾號(Vegout)回覆關鍵字“聯合索引”獲取)
sql優化——建索引
1、查詢所有標題以title666開頭的文章
select * from article where title like "title666%"
結果
已找到記錄: 1,111 警告: 0 持續時間 1 查詢: 0.688 sec
用了0.688秒,下面我們給title欄位加上索引
alter table article add key titleindex(title);
再次執行select語句(記得清除快取reset query cache),瞬間查出
已找到記錄: 1,111 警告: 0 持續時間 1 查詢: 0.000 sec.
但如果like條件這樣寫
select * from article where title like "%itle666%"
因為我們測試資料article中的所有title都是以“title”開頭的,所以這樣寫,查出的資料是一樣的,但是
已找到記錄: 1,111 警告: 0 持續時間 1 查詢: 0.656 sec.
又使用了0.656秒,通過explain我們可以看到
這次查詢並沒有走索引。所以建立索引的欄位在like條件下,如果以“%”開頭,索引會失效。
2、查詢title以“title666”開頭並且summary以“summary666”開頭的所有文章
我們執行
select * from article where title like "title666%"
and content like "content666%"
耗時
已找到記錄: 1,111 警告: 0 持續時間 1 查詢: 0.594 sec.
我們給title和summary建立一個聯合索引
alter table article add key title_summary_index(title,summary)
再次執行select語句,耗時
已找到記錄: 1,111 警告: 0 持續時間 1 查詢: 0.031 sec
但此時如果我們只查詢summary like"summary666"的記錄,將不會走索引
select * from article where summary like "summary666%"
已找到記錄: 1,111 警告: 0 持續時間 1 查詢: 0.672 sec.
對與聯合索引title_summary_index來說,遵循最左字首匹配原則,只有先走了title的索引,才會再走summaruy的索引。這裡只有summary是查詢條件,將不會走索引。也就是對與聯合索引(a,b),查詢a會走索引,查詢a,b也會走索引,但查詢b不會走索引。還有就是,對與聯合索引中的欄位出現位置,比如查詢a,b,還是b,a,並沒有嚴格的要求,也就是說我們這樣寫,也會走索引
select * from article where summary like "summary666%"
and title like "title666%"
為了進一步說明最左匹配原則,我們再建一個索引(並刪除上一個聯合索引)
alter table article add key
time_title_summary_index(publish_time,title,summary)
這個索引加入了發表時間,並放在了首位,也就是說只有publish_time 先走了索引,後邊的兩個欄位才有機會走索引。
select * from article where summary like "summary666%"
and title like "title666%" and publish_time>'2018-10-20 21:42:20'
在這個sql中,publish_time走不了索引,於是聯合索引失效,只能進行全表掃描。這裡使之失效的查詢條件是publish_time>‘2018-10-20 21:42:20’,並不是說使用“>”就會失效,mysql中使用了“!=”,“<>”,“not in”,“not exist”會使索引失效,但對於“>”,“<”,“>=”,“<=”的使用,優化器會根據查詢的資料情況來決定走不走索引(走索引快就走索引,索引慢就全表掃描),比如這裡將大於號改成小於號
select * from article where summary like "summary666%"
and title like "title666%" and publish_time<'2018-10-20 21:42:20'
很愉快的走了索引
由此你也可以推斷,庫中pulish_time小於2018-10-20 21:42:20的一定比少,不錯,只有七百多,大於它的有不到一百萬條。在一個部落格上曾看見這麼一段話形如聯合索引:聯合索引就像是一路關斬將,對與聯合索引(a,b,c),a就是第一關,b是第二關,c是第三關,關得一個一個按順序來過(大致這麼個意思)。感覺形如的還真是恰當。
如果當時我們在沒有刪除第一個聯合索引的情況下,執行上邊沒有走索引的select語句,就是這個
select * from article where summary like "summary666%"
and title like "title666%" and publish_time>'2018-10-20 21:42:20'
我們將會發現,它會走我們建立的第一個聯合索引。也就是publish_time使得不能夠走第二個聯合索引,但publish_time之後的條件可以走第一個聯合索引。
當然,我們也可以在title,summary上分別建立單列索引,但當多條件查詢時,只能有一個索引生效。我們給這兩個欄位分別建立索引,然後執行這個sql
explain select * from article where summary like
"summary666%" and title like "title666%"
我們發現
只走了一個索引。但單列索引也有他的好處,比如如果是條件是or的關係,兩個索引就都可以走
explain select * from article where summary like
"summary666%" or title like "title666%"
而如果我們使用是剛才的聯合索引,or將會使聯合索引失效
總結
- 多條件查詢時,單列索引只能用到一個,此時應該選擇聯合索引
- 聯合索引遵循最左字首匹配原則,只有左側先走了索引,之後的欄位才有可能走索引。所以建立聯合索引的時候,一定要注意順序,欄位使用越頻繁越要靠左。這個順序指的是建立索引時的順序,至於sql查詢語句中的順序沒有要求,因為mysql會對這個順序進行優化調整以滿足索引的要求。“%…”,“!=”,“<>”,“not in”,“not exist”,“or”,“is null”,“is not null”會使索引失效,“>”,“<”,“>=”,“<=”有可能使索引失效。
- 聯合索引的本質:當建立了(a,b,c)聯合索引時,相當於建立了(a)單列索引,(a,b)聯合索引,(a,b,c)聯合索引。
- 什麼情況下應該建立索引:選擇性高的欄位,經常where查詢的欄位,穩定的欄位
- 什麼情況下不要建立索引:頻繁變化的欄位,選擇性低的欄位
注:插入測試資料的指令碼大約會執行10分鐘,插入300多萬條資料。文中測試sql涉及到publish_time欄位的地方需要修改成你執行指令碼時插入的時間。
抱歉:建了那麼多表,文中只用了一個,我原來以為都會用到了,捂臉。給自己開脫一下,萬一以後文章用到了嘞。哈哈