当前位置: 移动技术网 > IT编程>数据库>Mysql > MySQL Execute Plan--Index Merge特性

MySQL Execute Plan--Index Merge特性

2020年01月09日  | 移动技术网IT编程  | 我要评论

index merge特性

在mysql 5.5之前版本中,查询或子查询被限制在一个表只能使用一个索引(回表查询除外)。

假设表tb1001上c1和c2列分别有单列索引,如对下面查询:

select * from tb1001 where c1='xxx' or c2='xxx';

单独使用任一索引都无法获取到所有满足条件的数据,因此查询只能使用全表扫描。

在mysql 5.5版本中引入index merge特性,允许:
查询对一个表上多个索引进行范围扫描并将多个扫描结果进行合并(union/intersect)。

index merge三种合并算法:

1、index merge intersect:对多个结果集求交集
2、index merge union:对多个结果集求union集合(无需对结果集排序)
3、index merge sort-union:对多个结果集先排序再求union集合

 

 

index merge intersect算法

当查询过滤条件(where部分)上使用and关联多个不同key的过滤条件时,如:

# 表tb1001有主键索引primary key(id)
# 表tb1001有辅助索引idx_c1(c1) 和辅助索引idc_c2(c2)

select * from tb1001 where c1='xxx' and c2='xxx';

不使用index merge intersect算法时执行计划伪代码为:

select * from tb1001
where id in (
select id from tb1001 where c1='xxx')
and c2='xxx';

使用index merge intersect算法时执行计划伪代码为:

select t2.* from (
select id from tb1001 where c1='xxx'
intersect
select id from tb1001 where c2='xxx'
) as t1
inner join tb1001 as t2
on t1.id=t2.id;

 

操作成本假设1:

假设:
满足c1='xxx'的记录有10000行:索引idx_c1上每个数据页存放500行索引记录,满足条件数据:
    a、"顺序存放"在索引idx_c1上"连续"的20个索引页中。
    b、"分散存放"在主键上"随机"的2000个数据页中。
满足c2='xxx'的记录有20000行,索引idx_c2上每个数据页存放500行索引记录,满足条件数据:
    a、"顺序存放"在索引idx_c2上"连续"的40个索引页中。
    b、"分散存放"在主键上"随机"的4000个数据页中。
同时满足c1='xxx' and c2='xxx'的记录有200行,满足条件数据:
    a、"分散存放"在主键上"随机"的40个数据页中

那么:
1、不使用index merge intersect算法需要"顺序读取"20个idx_c1索引页+"随机读取"2000个主键索引数据页
2、使用index merge intersect算法需要"顺序读取"20个idx_c1索引页+"顺序读取"40个idx_c2索引页+"随机读取"40个主键索引数据页
针对上面情况,使用index merge intersect算法能有效降低对主键的回表查找次数和随机读取次数(从2000次下降至40次)。


操作成本假设2:

假设:
满足c1='xxx'的记录有20行:索引idx_c1上每个数据页存放500行索引记录,满足条件数据:
    a、"顺序存放"在索引idx_c1上"连续"的1个索引页中。
    b、"分散存放"在主键上"随机"的20个数据页中。
满足c2='xxx'的记录有200000行,索引idx_c2上每个数据页存放500行索引记录,满足条件数据:
    a、"顺序存放"在索引idx_c2上"连续"的400个索引页中。
    b、"分散存放"在主键上"随机"的40000个数据页中。
同时满足c1='xxx' and c2='xxx'的记录有19行,满足条件数据:
    a、"分散存放"在主键上"随机"的19个数据页中

那么:
1、不使用index merge intersect算法需要"顺序读取"1个idx_c1索引页+"随机读取"20个主键索引数据页
2、使用index merge intersect算法需要"顺序读取"1个idx_c1索引页+"顺序读取"400个idx_c2索引页+"随机读取"19个主键索引数据页
针对上面情况,使用index merge intersect算法需要额外读取400个idx_c2索引页才能降低1次主键的回表查询和随机读取,显然性能更差。


index merge intersect算法和index condition pushdown特性

在mysql官方文档中,index merge intersect算法可以应用在分别使用主键和二级索引的查询中,如:

select *
from innodb_table
where primary_key < 10
and key_col1 = 20;

 

在未引入icp特性的早期mysql版本中,主键上过滤条件(primary_key < 10)不会"下推"到查询满足key_col1 = 20条件的过程中,因此可以使用index merge intersect算法来减少回表查找次数。

在引入icp特性的mysql版本中,由于辅助索引的索引记录中都包含主键列数据,因此主键上过滤条件(primary_key < 10)可以"下推"到查询满足key_col1 = 20条件的过程中,无需再使用index merge intersect算法。

## 在mysql 5.7版本中测试
select *
from tb001
where c1=10
and id<100;
## 执行计划为:
*************************** 1. row ***************************
           id: 1
  select_type: simple
        table: tb001
   partitions: null
         type: ref
possible_keys: primary,idx_c1
          key: idx_c1
      key_len: 5
          ref: const
         rows: 1
     filtered: 33.33
        extra: using where; using index
## 执行计划extra部分没有index merge相关信息

 

index merge intersect性能问题优化

 

在部分场景中,使用index merge intersec算法会带来严重的性能问题,dba可以通过mysql参数optimizer_switch来关闭该特性。

对于通过index merge intersec算法受益的查询,可以考虑使用组合索引或覆盖索引来替换单列索引。

如对上面查询,可以将索引idx_c1(c1)调整为idx_c1_c2(c1,c2),其查询性能更佳。

 

 

index merge union算法
当查询过滤条件(where部分)上使用or关联多个不同key的过滤条件时,如:

# 表tb1001有主键索引primary key(id)
# 表tb1001有辅助索引idx_c1(c1) 和辅助索引idc_c2(c2)
select * from tb1001 where c1='xxx' or c2='xxx';

其操作步骤为:

1、使用idx_c1索引获取到满足条件的[c1,id]记录,记录默认按照id排序
2、使用idx_c1索引获取到满足条件的[c1,id]记录,记录默认按照id排序
3、将已经按照id排序的步骤1和步骤2的数据进行合并去重id。
4、按照id回表查找并返回

伪代码为:

select t2.* from (
select id from tb1001 where c1='xxx'
union
select id from tb1001 where c2='xxx'
) as t1
inner join tb1001 as t2
on t1.id=t2.id

在创建索引idx_c1(id)时,其等价为idx_c1(c1,id),相同c1值的记录按id值排序,因此union操作的两个中见结果集在id上时有序的。

 

index merge sort-union算法

当查询过滤条件(where部分)上使用or关联多个不同key的过滤条件时,如:

# 表tb1001有主键索引primary key(id)
# 表tb1001有辅助索引idx_c1(c1) 和辅助索引idc_c2(c2)
select * from tb1001 where c1>'xxx' or c2<'xxx';

其操作步骤为:

1、使用idx_c1索引获取到满足条件的[c1,id]记录,再按照id进行排序
2、使用idx_c1索引获取到满足条件的[c1,id]记录,再按照id进行排序
3、将步骤1和步骤2的已按id排序后数据进行合并去重id。
4、按照id回表查找并返回

伪代码为:

select t2.* from (
select id from tb1001 where c1>'xxx'
order by id
union
select id from tb1001 where c2>'xxx'
order by id
) as t1
inner join tb1001 as t2
on t1.id=t2.id

在创建索引idx_c1(id)时,其等价为idx_c1(c1,id),对c1列进行范围查询返回数据的数据按照c1+id排序,在id列上是无序的,因此union操作前需先对两个中间结果集排序。


index merge union相关优化
在禁用index merge特性时,可以通过sql将or操作改写为union all操作,使查询同时使用多个索引。

如上面使用index merge union算法的查询,可以改写为:

#改写前:
select * from tb1001 where c1='xxx' or c2='xxx';

# 改写后
select t2.* from (
select id from tb1001 where c1='xxx'
union all
select id from tb1001 where c2='xxx' and (c1<>'xxx' or c1 is null)
) as t1
inner join tb1001 as t2
on t1.id=t2.id

ps: 将idx_c2(c2)改写为idx_c2_c2(c1,c2)能在union操作前避免回表查询。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网