索引的使用规则和设计考虑
當我們建立好一個聯合索引之后,我們的SQL語句要怎么寫,才能讓他的查詢使用到我們建立好的索引呢?
索引最基本的規則就是等值匹配了,就是where條件里的查詢條件字段和聯合索引的字段完全一樣,都是基于等于來匹配的。
最左側列匹配,假如我們設計的索引是INDEX(class_name, student_name, course_name),查詢的SQL語句where里不一定要根據三個字段來查詢,只要最左側的部分字段來查,就可以了
比如:select * from student_score where class_name='xx' and student_name='xx',只有class_name和student_name可以在索引里搜索,就可以查詢某個學生所有科目的成績。
但如果你SQL是select * from where course_name='xx’,那就不能走索引了,因為B+樹里必須先按class_name查找,再按student_name查找,不能跳過前面兩個字段。
最左側匹配規則,假如你要用like語法來查,比如select * from student_socre where class_name like '1%',查找所有1開頭的班級的分數,那么也是可以用索引的。
因為你的聯合索引的B+樹里,是按照class_name排序的,所以你要是給出class_name的最左前綴就是1,然后后面的給一個模糊匹配符號,那也是可以基于索引來查找的。
但如果你where條件是class_name like '%班',左側是模糊查詢,就沒法用索引了。
范圍查找,假如你查詢的SQL是:select * from student_score where class_name>'1班’ and class_name<'5班',查找幾個班的分數。也是可以用到索引的。
因為我們的索引的最下層的數據頁都是按順序組成雙向鏈表的,所以完全可以先找到'1班'對應的數據頁,再找到'5班'對應的數據頁,兩個數據頁中間的那些數據頁,就全都是在你范圍內的數據了!
ORDER BY如何使用上索引?
假設你有一個查詢:select * from table where xxx=xxx order by xxx,似乎應該是基于where語句通過索引快速篩選出來一波數據,接著放到內存里,或者放在一個臨時磁盤文件里,然后通過排序算法按照某個字段來一個排序,最后把排序好的數據返回。
如果是這樣的話,肯定就會比較慢,所以最好別這么搞。
尤其是類似于select * from table order by xx1,xx2,xx3 limit 10,這樣的SQL語句,查出一批數據后,按照多個字段進行排序,最后返回前10條數據,類似的語句其實常常見于分頁SQL語句里。
這種情況,我們可以建立一個聯合索引INDEX(xx1, xx2, xx3),這時候數據在索引里默認是按照xx1,xx2,xx3排序的,就不需要查出一批數據再在內存里或磁盤里做復雜的排序工作了。
直接拿到10條數據的主鍵去聚簇索引里回表查詢剩下的字段。
所以,你的SQL里最好是按照聯合索引的字段順序去進行order by排序,這樣就可以直接利用聯合索引的數據有序性,到索引樹里直接按照字段值的順序去獲取數據了。
GROUP BY如何使用上索引?
那假設你有這樣一個查詢:select count(*) from table group by xx語句,看起來必須先查出一批數據,然后數據放到一個臨時磁盤文件里還有加上部分內存,按照指定字段的值分成一組一組的,接著對每一組都執行一個聚合函數,這個性能也是極差的,因為畢竟涉及大量的磁盤交互。
在索引樹里數據都是按照指定的一些字段都排序好的,其實字段值相同的數據都是在一起的,假設要是走索引去執行分組后再聚合,那性能一定比臨時磁盤文件去執行好多了。
所以,group by后的字段,最好也是按照聯合索引里最左側的字段開始,按照順序排列的,這樣就可以用上索引來提取一組一組的數據了。
order by和group by用上索引的原理差不多,都是依賴索引數據的順序性。
索引設計的考慮因素
一般建立索引,盡量使用那些區分度比較大的字段,那么才能發揮出B+樹快速二分查找的優勢來。
什么是區分度比較大?
就是你表里一個列的值不同的越多,區分度越大,值不同的越少,區分度越小。比如訂單號,訂單表每條數據的訂單號都不一樣,所以它的區分度大,而性別字段一般就男,女,未知,三個值,區分度就比較小。
要是針對區分度小的字段建立索引,會查出一大批數據,沒有太大意義。
盡量對字段類型比較小的列設計索引,比如說tinyint之類的,因為他的字段類型比較小,說明這個字段自己本身的值占用磁盤空間小,此時你在搜索的時候性能也會比較好一點。
如果你的字段是name VARCHAR(255),你可以針對字段的前20個字符建立索引,就是說,對這個字段的每個值的前20個字符放在索引樹里。
但此時,你order by name就沒法用上索引了,group by name也是一樣的道理。因為你索引樹里僅僅包含了前20個字符。
函數與索引
假設你設計好了索引,但你SQL里這么寫,where sum(xxx)=xx,你給你索引里的字段xxx使用了函數,還能用上索引嗎?
不能了,所以盡量不要讓你的查詢語句里的字段搞什么函數。
現在設計索引的時候需要注意的點都已經講完了,其實就是好好設計索引,讓你的查詢語句都能用上索引,同時注意一下字段基數、前綴索引和索引列套函數的問題,盡量讓你的查詢都能用索引,別因為一些原因用不上索引了。
由于索引本身要占用存儲空間,增刪改的時候也要維護索引本身,索引一般不要設計太多索引,建議兩三個聯合索引就應該覆蓋掉你這個表的全部查詢了。
覆蓋索引
一般二級索引的葉子節點僅僅包含了索引里的幾個字段值和主鍵值,如果你要查詢表里的很多字段,就需要回表查詢,就是先在二級索引里拿到主鍵id,再回表根據主鍵id查出所有字段,這個性能其實也不高。
如果能只在二級索引里查到所有字段,那就完美了,這就要引入覆蓋索引的概念了。
覆蓋索引不是一種索引,是一種基于索引查詢的方式。
假設你有查詢select xx1,xx2,xx3 from table order by xx1,xx2,xx3這樣的語句,這種情況下,你僅僅需要聯合索引里的幾個字段的值,那么其實就只要掃描聯合索引的索引樹就可以了,不需要回表去聚簇索引里找其他字段了。
你要查的xx1,xx2,xx3在二級索引里都能提取出來,不需要到聚簇索引里查,這就是覆蓋索引。
所以你SQL里最好指定你僅僅需要的幾個字段,不要動不動搞一個select *把所有字段都拿出來,甚至最好是直接走覆蓋索引的方式,不要去回表到聚簇索引。
即使是必須要回表到聚簇索引,那你也盡可能用limit、where之類的語句限定一下回表到聚簇索引的次數,從二級索引里篩選最少的數據,然后再回表到聚簇索引里去,這樣性能也會好一些。
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的索引的使用规则和设计考虑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 又偷偷夹带反战私货!npm生态还能信任吗
- 下一篇: 记一次提升18倍的性能优化