聯合索引避免臨時表排序
在上一篇筆記(MySQL——order by邏輯(全字段排序與rowid排序))中,講到查詢語句查詢多個字段的時候使用order by語句實現返回值是有序的,而order by是使用到了臨時表的,會帶來時間和空間損失。
其實使用聯合索引,就可以避免臨時表的排序操作。
只要保證city這個索引上取出來的行天然就是按照name遞增排序的話,就可以不用再排序了。
alter table t add index city_user(city,name);
在這個索引里面,通過樹搜索的方式定位到第一個滿足city = '杭州’的記錄,并且額外確保了,接下來按順序取“下一條記錄”的遍歷過程中,只要city值是杭州,name值一定有序。
查詢流程變為:
1、從索引(city,name)找到第一個滿足city = '杭州’條件的主鍵id;
2、到主鍵id索引取出整行,取name、city、age三個字段值,作為結果集的一部分直接返回
3、從索引(city,name)取下一個記錄主鍵id;
4、重復step2、3直到查到第1000條記錄,或者不滿足city = '杭州’條件時循環結束。
覆蓋索引優化查詢
可以使用覆蓋索引繼續優化查詢的執行流程:
覆蓋索引指,索引上的信息足夠滿足查詢請求,不需要再回到主鍵索引上取數據。
針對select city,name,age from t
這個查詢,可以創建一個city、name和age的聯合索引,對應語句為:
alter table t add index city_user_age(city,name,age);
這時,對于city字段的值相同的行來說,還是按照name字段的值遞增排序。查詢語句的執行流程變為:
1、從索引(city,name,age)找到第一個滿足city = '杭州’條件的記錄,取出其中的city、name和age三個字段值,作為結果集的一部分直接返回
2、從索引(city,name,age)取下一個記錄,同樣取出這三個字段的值,作為結果集的一部分直接返回
3、重復步驟2,直到查到第1000條記錄,或者是不滿足city = '杭州’條件時循環結束。
當然,并不是說每個查詢能用上覆蓋索引,就要把語句中涉及的字段都建上聯合索引。因為索引有維護代價。
思考
假設表里面已經有了city_name(city,name)聯合索引。你需要查詢杭州和蘇州兩個城市中所有市民的名字,并且按名字排序,顯示前100條記錄。
select * from t where city in('杭州','蘇州') order by name limit 100;
這個語句會有排序。因為條件是蘇州或杭州。如果只有一個條件如只有杭州,那么就不需要排序操作。
如果我們需要實現一個在數據庫端不需要排序的方案,可以這么實現:
把這一條語句拆成兩條語句,流程如下:
1、執行select * from t where city = '杭州' order by name limit 100;
(這個語句不需要排序,客戶端用一個長度為100的內存數組A保存結果)
2、執行select * from where city = '蘇州' order by name limit 100;
(相同的方法,結果被存入內存數組B)
3、對AB兩個有序數組采用歸并排序,得到name最小的前100值,這就是我們需要的結果了。