推荐链接:
总结——》【Java】
总结——》【Mysql】
总结——》【Redis】
总结——》【Kafka】
总结——》【Spring】
总结——》【SpringBoot】
总结——》【MyBatis、MyBatis-Plus】
总结——》【Linux】
总结——》【MongoDB】
总结——》【Elasticsearch】
MySQL在一般情况下执行一个查询时,只会用到单个二级索引,但是在某些特殊情况下,可能会使用到多个二级索引。使用多个索引来完成一次查询的执行方法称之为index merge索引合并,具体的索引合并算法有下边三种。
某个查询可以使用多个二级索引,将从多个二级索引中查询到的结果取交集。
Q:下面2种情况一定会发生索引合并吗?
A:不一定。
这两种情况只是发生Intersection索引合并的必要条件,不是充分条件。也就是说即使符合Intersection的条件,也不一定发生Intersection索引合并,这得看优化器的判断。
优化器只有在单独根据搜索条件从某个二级索引中获取的记录数太多,导致回表开销太大,而通过Intersection索引合并后需要回表的记录数大大减少时才会使用Intersection索引合并。
-- 不用使用索引合并,因为对order_no进行了范围匹配
SELECT * FROM order_exp WHERE order_no> 'a' AND expire_time = 'a';
-- 不用使用索引合并,因为insert_time使用到的联合索引u_idx_day_status中的order_status和expire_time列并没有出现在搜索条件中
SELECT * FROM order_exp WHERE order_no = 'a' AND insert_time = 'a';
-- 使用索引合并,因为order_no 和 expire_time 索引都是等值匹配
SELECT * FROM order_exp WHERE order_no = 'a' AND expire_time = 'b';
Q:为什么在二级索引列都是等值匹配的情况下可能使用Intersection索引合并?
A:因为只有在这种情况下,根据二级索引查询出的结果集是按照主键值排序的。Intersection索引合并会把从多个二级索引中查询出的主键值求交集,如果从各个二级索引中查询的到的结果集本身就是已经按照主键排好序的,那么求交集的过程就很容易。
-- 使用索引合并
SELECT * FROM order_exp WHERE id > 100 AND expire_time = 'a';
Q:为什么主键列可以是范围匹配?
A:因为主键的索引是有序的,而二级索引的用户记录是由索引列 + 主键构成的,所以根据范围匹配出来的主键就是乱序的,导致回表开销很大。
SELECT * FROM order_exp WHERE order_no = 'a' AND expire_time = 'b';
为什么不直接使用idx_order_no或者idx_expire_time只根据某个搜索条件去读取一个二级索引,然后回表后再过滤另外一个搜索条件呢?
虽然读取多个二级索引比读取一个二级索引消耗性能,但是大部分情况下读取二级索引的操作是顺序I/O,而回表操作是随机I/O,所以如果只读取一个二级索引时需要回表的记录数特别多,而读取多个二级索引之后取交集的记录数非常少,当节省的因为回表而造成的性能损耗比访问多个二级索引带来的性能损耗更高时,读取多个二级索引后取交集比只读取一个二级索引的成本更低。
我们在写查询语句时经常想把既符合某个搜索条件的记录取出来,也把符合另外的某个搜索条件的记录取出来,我们说这些不同的搜索条件之间是OR关系。有时候OR关系的不同搜索条件会使用到不同的索引。
Q:下面3种情况一定会发生索引合并吗?
A:不一定。
这三种情况只是发生Union索引合并的必要条件,不是充分条件。也就是说即使符合Union的条件,也不一定发生Union索引合并,这得看优化器的心情(判断)。
优化器只有在单独根据搜索条件从某个二级索引中获取的记录数比较少,通过Union索引合并后进行访问的代价比全表扫描更小时才会使用Union索引合并。
分析同Intersection合并
-- 使用Union合并
SELECT * FROM order_exp WHERE order_no = 'a' OR expire_time = 'b';
-- 不用使用索引合并,因为各个二级索引列必须等值匹配
SELECT * FROM order_exp WHERE order_no< 'a' OR expire_time> 'z'
分析同Intersection合并
搜索条件的某些部分使用Intersection索引合并的方式得到的主键集合和其他方式得到的主键集合取交集。
-- 使用Union合并
SELECT * FROM order_exp WHERE insert_time = 'a' AND order_status = 'b' AND expire_time = 'c'
OR (order_no = 'a' AND expire_time = 'b');
-- 使用Union合并
SELECT * FROM order_exp WHERE insert_time = 'a' AND order_status = 'b' AND expire_time = 'c'
OR (order_no = 'a' AND expire_time = 'b');
首先按照二级索引记录的主键值进行排序,然后按照Union索引合并方式执行的方式称之为Sort-Union索引合并。这种Sort-Union索引合并比单纯的Union索引合并多了一步对二级索引记录的主键值排序的过程。
Q:下面这种情况一定会发生索引合并吗?
A:不一定。
查询条件符合了这些情况也不一定就会采用Sort-Union索引合并,也得看优化器的心情。
优化器只有在单独根据搜索条件从某个二级索引中获取的记录数比较少,通过Sort-Union索引合并后进行访问的代价比全表扫描更小时才会使用Sort-Union索引合并。
SELECT * FROM order_exp WHERE order_no< 'a' OR expire_time> 'z';
Q:为什么不能使用Union合并?
A:使用Union索引合并,必须保证各个二级索引列在进行等值匹配的条件下才可能被用到。
-- 建立联合索引,查询又快又好(既不用多读一棵B+树,也不用合并结果)
SELECT * FROM order_exp WHERE order_no= 'a' And expire_time= 'z';
ALTER TABLE order_exp drop index idx_order_no;
ALTER TABLE order_exp drop idx_expire_time;
ALTER TABLE add index idx_order_no_expire_time(order_no,expire_time);