Intersection 翻译过来的意思是交集。这里是说某个查询可以使用多个二级索引,将从多个二级索引中查询到的结果取交集,比方说下边这个查询:
SELECT * FROM order_exp WHERE order_no = ‘a’ AND expire_time = ‘b’;
假设这个查询使用 Intersection 合并的方式执行的话,那这个过程就是这样的:

  • 从 idx_order_no 二级索引对应的 B+树中取出 order_no= ‘a’的相关记录。
  • 从 idx_insert_time 二级索引对应的 B+树中取出 insert_time= ‘b’的相关记录。二级索引的记录都是由索引列 + 主键构成的,所以我们可以计算出这两个结果集中 id 值的交集。
  • 按照上一步生成的 id 值列表进行回表操作,也就是从聚簇索引中把指定 id 值的完整用户记录取出来,返回给用户。

为啥不直接使用 idx_order_no 或者 idx_insert_time 只根据某个搜索条件去读 取一个二级索引,然后回表后再过滤另外一个搜索条件呢?这里要分析一下两种查询执行方式之间需要的成本代价。
只读取一个二级索引的成本:
按照某个搜索条件读取一个二级索引,根据从该二级索引得到的主键值进行回表操作,然后再过滤其他的搜索条件

读取多个二级索引之后取交集成本:
按照不同的搜索条件分别读取不同的二级索引,将从多个二级索引得到的主键值取交集,然后进行回表操作。
虽然读取多个二级索引比读取一个二级索引消耗性能,但是大部分情况下读取二级索引的操作是顺序 I/O,而回表操作是随机 I/O,所以如果只读取一个二级索引时需要回表的记录数特别多,而读取多个二级索引之后取交集的记录数非常少,当节省的因为回表而造成的性能损耗比访问多个二级索引带来的性能损耗更高时,读取多个二级索引后取交集比只读取一个二级索引的成本更低。

MySQL 在某些特定的情况下才可能会使用到 Intersection 索引合并,哪些情况呢?

情况一:等值匹配

二级索引列是等值匹配的情况,对于联合索引来说,在联合索引中的每个列都必须等值匹配,不能出现只匹配部分列的情况。
而下边这两个查询就不能进行 Intersection 索引合并:
SELECT FROM order_exp WHERE order_no> ‘a’ AND insert_time = ‘a’ AND order_status = ‘b’ AND expire_time = ‘c’;
SELECT
FROM order_exp WHERE order_no = ‘a’ AND insert_time = ‘a’;
第一个查询是因为对 order_no 进行了范围匹配,第二个查询是因为联合索引u_idx_day_status中的order_status和expire_time列并没有出现在搜索条件中, 所以这两个查询不能进行 Intersection 索引合并。

情况二:主键列可以是范围匹配

比方说下边这个查询可能用到主键和u_idx_day_status进行Intersection索引合并的操作:
SELECT FROM order_exp WHERE id > 100 AND insert_time = ‘a’;
对于 InnoDB 的二级索引来说,记录先是按照索引列进行排序 ,如果该二级索引是一个联合索引,那么会按照联合索引中的各个列依次排序。而二级索引的用户记录是由索引列 + 主键构成的,二级索引列的值相同的记录可能会有好多条,这些索引列的值相同的记录又是按照主键的值进行排序的。
所以重点来了,之所以在二级索引列都是等值匹配的情况下才可能使用Intersection 索引合并,是因为只有在这种情况下根据二级索引查询出的结果集是按照主键值排序的。
Intersection 索引合并会把从多个二级索引中查询出的主键值求交集,如果从各个二级索引中查询的到的结果集本身就是已经按照主键排好序的,那么求交集的过程就很容易。
假设某个查询使用 Intersection 索引合并的方式从 idx_order_no 和idx_expire_time 这两个二级索引中获取到的主键值分别是:
从 idx_order_no 中获取到已经排好序的主键值:1、3、5
从 idx_expire_time 中获取到已经排好序的主键值:2、3、4
那么求交集的过程就是这样:逐个取出这两个结果集中最小的主键值,如果两个值相等,则加入最后的交集结果中,否则丢弃当前较小的主键值,再取该丢弃的主键值所在结果集的后一个主键值来比较,直到某个结果集中的主键值用完了,时间复杂度是 O(n)。
但是如果从各个二级索引中查询出的结果集并不是按照主键排序的话,那就要先把结果集中的主键值排序完再来做上边的那个过程,就比较耗时了。
按照有序的主键值去回表取记录有个专有名词,叫:Rowid Ordered Retrieval,简称 ROR。
另外,不仅是多个二级索引之间可以采用 Intersection 索引合并,索引合并也可以有聚簇索引参加,也就是我们上边写的情况二:在搜索条件中有主键的范围匹配的情况下也可以使用 Intersection 索引合并索引合并。为啥主键这就可以范围匹配了?还是得回到应用场景里:
SELECT
FROM order_exp WHERE id > 100 AND order_no = ‘a’;
假设这个查询可以采用 Intersection 索引合并,我们理所当然的以为这个查询会分别按照id > 100这个条件从聚簇索引中获取一些记录,在通过order_no= ‘a’这个条件从 idx_order_no 二级索引中获取一些记录,然后再求交集,其实这样就把问题复杂化了,没必要从聚簇索引中获取一次记录。别忘了二级索引的记录中都带有主键值的,所以可以在从 idx_order_no 中获取到的主键值上直接运用条件 d > 100 过滤就行了,这样多简单。所以涉及主键的搜索条件只不过是为了从别的二级索引得到的结果集中过滤记录罢了,是不是等值匹配不重要。
当然,上边说的情况一和情况二只是发生 Intersection 索引合并的必要条件,不是充分条件。也就是说即使情况一、情况二成立,也不一定发生 Intersection索引合并,这得看优化器的心情。优化器只有在单独根据搜索条件从某个二级索引中获取的记录数太多,导致回表开销太大,而通过 Intersection 索引合并后需要回表的记录数大大减少时才会使用 Intersection 索引合并。
**