目录
- 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索引合并多了一步对二级索引记录的主键值进行排序。