目錄
- single_table訪問方法
- const
- ref
- ref_or_null
- range
- index
- all
- MRR多范圍讀取優化
- 索引合并
- intersection
- union
- sort-union
single_table訪問方法
const
在主鍵列或者unique二級索引與一個常數進行等值比較時才有效。
如果主鍵或者unique二級索引的索引列由多個列構成,則只有在索引列中的每個列都與常數進行等值比較時,才是const訪問
ref
搜索條件為二級索引(非unique)與常數進行等值比較,形成的掃描區間為單點掃描區間(即【‘abc’,‘abc’】),采用二級索引來執行查詢的訪問方法為ref。注意采用二級索引執行查詢時,每獲取到一條二級索引記錄就會進行一次回表操作。
TIPS:
- 二級索引列允許存儲NULL值時不限制NULL值的數量,所以執行key is NULL查詢時最優只能執行ref操作
- 索引列中包含多個列的二級索引時,只要最左邊連續的列是與常數進行等值比較,就可以使用ref訪問。
ref_or_null
當想找出某個二級索引列的值等于某個常數的記錄,并且將該列中值為NULL的記錄也找出來:
select * from single_table where key1 = 'abc' or key1 is null;
若使用二級索引,此時的掃描區間為:[‘abc’,‘abc’] 以及[NULL,NULL]。
這種訪問方法即為ref_or_null。
range
select * from single_table where key2 IN (1438,6328) OR (key2 >= 38 AND key2 <= 79);
使用二級索引,掃描區間為[1438,1438] 、[6328,6328]、[38,79],改掃描區間為若干個單點掃描區間或者范圍掃描區間。訪問方法為range。當然(-無窮,+無窮)不為range訪問方法。
index
key_part1,key_part2,key_part3 為二級索引,它們三個構成了一個聯合索引,并且key_table2并不是聯合索引的最左列。
select key_part1,key_part2,key_part3 from single_table where key_table2 = 'abc';
此時無法形成合適的范圍區間來減少掃描的記錄數量。
需要注意此時的查詢符合兩個條件:
- 查詢列表中key_part1,key_part2,key_part3,都包含在聯合索引中
- 搜索條件只有key_part2,這個列也包含在聯合索引中
很顯然,需要掃描全部的聯合索引,掃描區間為[-無窮,+無窮]。由于二級索引記錄只有存放索引列和主鍵,也不需要回表,所以此時掃描去不的二級索引記錄比直接掃描全部的聚集索引記錄成本要小。這種方法稱為index訪問。
又如:
select * from single_table order by id;
通過全表掃描對表進行查詢時有order by。此時也是使用index方法。
all
全表掃描,直接掃描全部的聚集索引記錄。
MRR多范圍讀取優化
select * from single_table where key1 = 'abc' and key2 > 1000;
該語句的執行步驟:
1、通過key1的索引定位掃描區間[‘abc’,‘abc’];
2、根據上面得到的主鍵值回表,得到完整用戶記錄,然后檢測記錄是否滿足key2 > 1000的條件,滿足則返回
3、重復2步驟,直到不滿足key1 = ‘abc’
每次從二級索引中讀取到一條記錄后,就會根據該記錄的主鍵值執行回表操作。
而某個掃描區間中的二級索引記錄的主鍵值是無序的,每次回表都會隨機讀取一個聚集索引頁面,帶來的IO開銷較大。
MRR會先讀取一部分二級索引記錄,將它們的主鍵值排序后再同意執行回表操作,節省IO開銷。
索引合并
intersection
使用多個索引完成一次查詢的執行方法稱為索引合并
select * from single_table where key1 = 'a' and key3 = 'b';
可以先搜key1的索引,然后回表,根據key3條件篩選。
也可以先搜key1的索引,然后回表,根據key1條件篩選。
當然可以同時使用key1和key2的索引。在key1索引中掃描key1值得到區間[‘a’,‘a’],在key3索引中掃描key3值得到區間[‘b’,‘b’];
然后從兩者操作結果中找到id列值相同的記錄。然后根據共有的id值執行回表,這樣可能會省下回表操作帶來的開銷。
當然需要注意的是要求從不同二級索引中獲取到的二級索引記錄都按照主鍵值排好序:
- 從兩個有序集合中取交集比兩個從無序集合中取交集要容易
- 如果獲取到的id值有序排列,則在根據這些id值執行回表操作時不再是進行單純的隨機IO,就會提高效率。
如果從掃描區間中獲得的記錄并不是按照主鍵值排序的,那么就不能使用intersection索引合并。
union
select * from single_table where key1 = 'a' or key3 = 'b';
同時使用key1和key2的索引。在key1索引中掃描key1值得到區間[‘a’,‘a’],在key3索引中掃描key3值得到區間[‘b’,‘b’];
然后對兩個結果進行去重,對去重后的id值進行回表操作。
同樣二級索引記錄都是要按照主鍵值排序,如果從掃描區間中獲得的記錄并不是按照主鍵值排序的,那么就不能使用union索引合并。
sort-union
union索引合并條件苛刻,下面的查詢就不能使用union索引合并
select * from single_table where key1 < 'a' or key3 > 'z';
我們可以這樣操作;
1、根據key1<'a’條件從key1的二級索引中獲取記錄,并將獲取到的記錄的主鍵值排序
2、根據key3<'z’條件從key3的二級索引中獲取記錄,并將獲取到的記錄的主鍵值排序
3、按照union操作兩個記錄合并
sort-union 索引合并比union索引合并多了一步對二級索引記錄的主鍵值進行排序。