我們都知道,當數據表中的數據日益增長后,查詢會變得越來越慢,當初在表設計之初,尚未考慮創建索引的話,那么現在正是必要的時候。可是,如果對於MySQL使用索引的策略不了解,或是脫離了具體業務場景,那么,創建出來的索引,也發揮不了多大的作用。本文,就從我剛剛完成的一個項目入手,介紹如何正確的設計聯合索引,以在實戰項目中真正的發揮作用。
實際的業務場景
下圖展示了項目所涉及的實際業務場景:
可以看到,查詢條件主要涉及:視頻分區、產品類別、產品、視頻時長 和 發布時間。
其中,視頻分區下,還有子分區:
產品下面,還有所屬品牌:
所以,我們可以把查詢條件做個梳理:1、視頻分區 > 子分區,2、產品類別 > 產品 > 品牌。其中 產品類別
和 產品
會根據用戶選擇的 視頻分區
或 子分區
的不同而變化,這樣就會衍生出很多不同的業務邏輯 和 查詢條件的組合:
- 視頻分區 > 子分區 > 產品類別 > 產品 > 品牌
這代表了用戶既選了視頻分區和子分區,同時又基於所屬子分區下的產品類別,又選擇了產品和品牌,即 所有關鍵的查詢條件都用上了。下面的示意邏輯相同,只是組合不同而已。
- 視頻分區 > 子分區 > 產品類別
視頻分區 > 子分區 > 產品 > 品牌
- 視頻分區 > 產品類別 > 產品 > 品牌
視頻分區 > 產品類別
視頻分區 > 產品 > 品牌
產品類別 > 產品 > 品牌
產品 > 品牌
這里只列舉了部分查詢條件的組合,其實還有更多。其中特別標注出來的,是在實際使用場景下,常用的查詢組合。下面我們就來看一看,如何基於這些查詢條件,設計出合理的索引。
基於業務場景的聯合索引的設計方案
我們把索引簡單的分為 單列索引
和 多列索引
,多列索引被稱為 聯合索引
或 復合索引
,對於查詢語句中的 where 條件,如果某些條件是這個查詢中頻繁用到的組合,那么,通常會創建聯合索引,來提升查詢效率。
但是,對於一個沒有深諳其道的人來講,他可能會這樣設計聯合索引:
-
給每個查詢字段創建一個
單列索引
CREATE INDEX 索引名稱 ON 表名 (視頻分區);
CREATE INDEX 索引名稱 ON 表名 (子分區);
...
-
只創建一個能覆蓋到所有查詢條件的
聯合索引
CREATE INDEX 索引名稱 ON 表名 (視頻分區, 子分區, 產品類別, 產品, 品牌);
以為這樣,就可以讓索引發揮全部作用,適用於所有查詢條件的組合了。其實,聯合索引
有一個最左匹配原則,從左至右匹配你的查詢條件,直至斷掉終止,如果索引列的第一個字段都尚不能匹配,則用不上此索引。比如:
- 我們查詢
視頻分區 > 子分區 > 產品類別 > 產品 > 品牌
這些條件組合,當然可以用到上面創建的索引,因為查詢條件與索引列完全對的上。 - 如果查詢
產品類別 > 產品 > 品牌
的話,則索引就無效了,因為索引列的第一個索引字段視頻分區
不在 where 查詢條件中,最左匹配原則一開始就失敗了,所以用不上索引。 - 如果查詢
視頻分區 > 子分區
,這是符合最左匹配原則的【英文叫 leftmost prefix of the index】,這個leftmost
就告訴我們,雖然索引列並不完全匹配查詢條件,但是部分匹配,而且必須頂着頭的、排着隊的、中間沒有斷掉的匹配了,即使尾巴斷掉了沒有關系,依然可以用上此索引。 - 最后,如果查詢
視頻分區 > 子分區 + 發布時間
這組條件,能否用的上此索引呢?答案是肯定的,我不管你 where 條件中,哪些列不是索引列,我只關心你 where 條件中,哪些列在索引列之中,並且符合最左匹配就行了。
好了,到這里,我們應該逐漸清楚的認識到,僅僅創建一個包含所有查詢條件的聯合索引 視頻分區, 子分區, 產品類別, 產品, 品牌
是遠遠不夠的,而是需要根據業務需求和使用場景,將可能會頻繁用到的查詢條件,進行不同的排列組合,設計出一個折中的 聯合索引
的方案。
正如你在本文前一部分看到的,在我列舉出的部分查詢條件中,着重標注出來的那些,就是我認為會最頻繁使用到的組合,所以,需要相應的創建多個不同組合的 聯合索引
,以此來應對前端操作用戶選擇不同查詢條件時,能夠最大限度的命中索引,提升查詢效率。
索引無法解決的問題
你應該也能看得出來,即使合理的創建了 聯合索引
,也無法覆蓋到全部的查詢條件的各種組合。好吧,就算你真的根據所有的組合,都相應的創建了 聯合索引
,但還有最后一關你過不去,那就是 模糊查詢
。你注意到,我的項目中,用到了根據用戶輸入的內容,來進行搜索,這必然需要使用模糊查詢來實現,但是,%xxx%
這種形式的模糊查詢,是無論如何也用不上索引的。
針對以上問題,我突發奇想,想到了一個絕妙的方案,並加以實施,應用到了我的項目中,結果效果非常好。在下一篇,我再作具體介紹。