MySQL:关于二级索引的intersection 索引合并

写在前面

关于 MySQL 里二级索引的intersection 索引合并相关内容。

内容

我们先以一个表来开头:

create table index_table
(
    id   int not null auto_increment,
    key1 varchar(100),
    primary key (id),
    key idx_key1 (key1)
    key idx_key2 (key2)
) engine = InnoDB
  charset = utf8

在这个表里,我们将 id 列作为主键,建立了聚簇索引,将 key1 列作为二级索引,将 key2 列作为二级索引。
于是,对于这两种索引的 B+树,有几个点:

  • 对于聚簇索引,或是主键来说,其 B+ 树的叶子节点存储的是完整的记录
  • 对于二级索引,其 B+树的叶子节点存储的是 key1 列加主键这两个列的值(这是 MySQL 的索引扩展特性)

由于二级索引实际是 key1 列加主键,同样也有几个地方:

  • 在排序方面,会先按照 key1 列的值进行排序,如果 key1 列的值相同,就按照主键值进行排序
  • 那么所谓的为 key1 列建立索引,进一步来说就是为 key1 列和主键列建立了联合索引

(key2 列跟 key1 列是一样的,不做赘述)

有了上面的铺垫,我们来看关于索引的查找操作,在下面的这条 SQL 语句里:

select * from index_table where  key1 = 'abc' and key2 = 'science';

一般情况下, MySQL 只会为单个索引生成扫描区间,也就是说,当客户端发送上面的 SQL 语句到服务器后,MySQL 的优化器会判断 idx_key1 的扫描区间和 idx_key2 的扫描区间,哪个执行查询时的成本比较低,然后就对对应的索引执行查询。
假如说这里选取了 idx_key1 的扫描区间,那么整个过程就是:

  • 通过 idx_key1 对应的 B+ 树定位到扫描区间 [‘abc’,‘abc’]中的第一条二级索引记录
  • 对该记录执行回表操作*(注意是每一条二级索引记录都执行回表操作)*,再判断是否满足 key2 = ‘science’,如果是就返回给客户端,否则就丢弃
  • 继续下一条二级索引记录如上的操作

(反之如果选取的是 idx_key2 的扫描区间,也是一样)

那么这里有个地方,因为刚好对于 idx_key1 和 idx_key2 来说,它们的扫描区间其实左右闭区间的值是一样的,意味着它们的列的值是同一个,那么按照前面所说,此时的二级索引的记录,实际是按主键值来排序的。
那么由于都是按主键值来排序,那么这里的二级索引其实可以进行索引合并(也就是 intersection 索引合并)。
这个过程就是会从这两个索引各自的扫描区间里,按照顺序(主键值的顺序)各取出一条,然后进行比较,如果是一样,则返回;不一样,则丢弃掉顺序小的,从被丢弃的索引记录的扫描空间里,再取出下一条,再进行互相比较。(其实就相当于取交集)

因此我们可以看到,intersection 索引合并对应的其实是一种比较特殊的情况,即二级索引的列值相等且按照主键值排序这个条件。
这样的要求主要考虑到两个地方

  • 从两个有序集合中取交集比在两个无序集合中取交集容易(对于有序无序我想主要也是基于扫描区间的单向链表来说)
  • 在一般情况下,某个扫描区间中的二级索引记录的主键值是无序的,这样就导致每次执行回表操作,都是随机读取一个聚簇索引页面;而在intersection 索引合并里,由于已经是按照主键值进行了排序,因此就不会有前面的随机 IO 操作,也就提高了效率。

所以,如果我们把 SQL 语句改成如下:

select * from index_table where  key1 > 'abc' and key2 = 'science';

此时 idx_key_1 的扫描区间的列值不再相同,而是(‘abc’, +∞),那么此时就不适合 intersection 索引合并了。

参考

Use of Index Extensions

你可能感兴趣的:(数据库,mysql,数据库)