MySQL 查询优化之 Index Merge

Index Merge Intersection 访问算法

Index Merge Union 访问算法

Index Merge Sort-Union 访问算法

参考文档

索引合并访问方法可以在查询中对一个表使用多个索引,对它们同时范围扫描,并且合并结果(intersects/unions/unions-of-intersections)。 此访问方法合并来自单个表的索引扫描; 它不会将扫描合并到多个表中。

使用索引合并的示例查询:

SELECT * FROM tbl_name WHERE key1 = 10 OR key2 = 20;

SELECT * FROM tbl_name
WHERE (key1 = 10 OR key2 = 20) AND non_key = 30; SELECT * FROM t1, t2
WHERE (t1.key1 IN (1,2) OR t1.key2 LIKE 'value%')
AND t2.key1 = t1.some_col; SELECT * FROM t1, t2
WHERE t1.key1 = 1
AND (t2.key1 = t1.some_col OR t2.key2 = t1.some_col2);

【注意】
索引合并优化算法有以下已知的缺陷:

  • 如果您的查询具有深度AND或OR嵌套的复杂WHERE子句,并且MySQL不选择最佳计划,请尝试使用以下标识转换来分配条件:

    (x AND y) OR z => (x OR z) AND (y OR z)
    (x OR y) AND z => (x AND z) OR (y AND z)
  • 索引合并不适用于全文索引。

EXPLAIN输出中,索引合并方法在type列中显示为index_merge。 在这种情况下,key列包含使用的索引列表,key_len包含这些索引的最长键部分列表。

Index Merge访问方法有几种算法,它们显示在EXPLAIN输出的Extra字段中:

  • Using intersect(...)

  • Using union(...)

  • Using sort_union(...)

Index Merge方法根据合并算法的不同分成了三种:Intersect,Union,Sort_union。它们显示在EXPLAIN输出的Extra字段中。Intersect和Union都需要使用的索引是ROR的,也就是ROWID ORDERED,即针对不同的索引扫描出来的数据必须是同时按照ROWID排序的,这里的ROWID其实也就是InnoDB的主键(如果不定义主键,InnoDB会隐式添加ROWID列作为主键)。只有每个索引是ROR的,才能进行归并排序,你懂的。 当然你可能会有疑惑,查记录后内部进行一次sort不一样么,何必必须要ROR呢,不错,所以有了Sort-union。Sort-union就是每个非ROR的索引排序后再进行Merge。MySQL至于为什么没有Sort-Intersect,就不清楚了,但是MariaDB从5.3版本开始就支持了。

1. Index Merge Intersection 访问算法

index intersect merge是多个索引条件扫描得到的结果进行交集运算。显然在多个索引提交之间是 AND 运算时,才会出现 index intersect merge。 下面两种where条件或者它们的组合时会进行 index intersect merge:

  • 条件使用到复合索引中的所有字段或者左前缀字段(对单字段索引也适用)

    key_part1 = const1 AND key_part2 = const2 ... AND key_partN = constN
  • 主键上的任何范围条件

    SELECT * FROM innodb_table
    WHERE primary_key < 10 AND key_col1 = 20; SELECT * FROM tbl_name
    WHERE key1_part1 = 1 AND key1_part2 = 2 AND key2 = 2;

示例1

mysql> show index from employees;
+-----------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| employees | 0 | PRIMARY | 1 | emp_no | A | 298936 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_name | 1 | last_name | A | 1651 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_first_name | 1 | first_name | A | 1251 | NULL | NULL | | BTREE | | |
+-----------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec) mysql> explain select * from employees.employees e where e.first_name='Tzu' and e.last_name='Terkki';
+----+-------------+-------+------------+-------------+-------------------------+-------------------------+---------+------+------+----------+-------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+-------------------------+-------------------------+---------+------+------+----------+-------------------------------------------------------+
| 1 | SIMPLE | e | NULL | index_merge | idx_name,idx_first_name | idx_name,idx_first_name | 66,58 | NULL | 1 | 100.00 | Using intersect(idx_name,idx_first_name); Using where |
+----+-------------+-------+------------+-------------+-------------------------+-------------------------+---------+------+------+----------+-------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

示例2

mysql> show index from employees;
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| employees | 0 | PRIMARY | 1 | emp_no | A | 298936 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_name | 1 | last_name | A | 1651 | NULL | NULL | | BTREE | | |
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec) mysql> explain select * from employees.employees e where e.emp_no>10011 and e.last_name = 'Terkki';
+----+-------------+-------+------------+-------------+------------------+------------------+---------+------+------+----------+------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+------------------+------------------+---------+------+------+----------+------------------------------------------------+
| 1 | SIMPLE | e | NULL | index_merge | PRIMARY,idx_name | idx_name,PRIMARY | 70,4 | NULL | 90 | 100.00 | Using intersect(idx_name,PRIMARY); Using where |
+----+-------------+-------+------------+-------------+------------------+------------------+---------+------+------+----------+------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

索引合并Intersection访问算法对所有使用的索引执行同时扫描,并产生从合并索引扫描中接收到的行序列的交集。

如果查询中使用的所有列都被使用的索引覆盖,则不会检索完整的表行(EXPLAIN输出包含在这种情况下在Extra字段中 Using index)。 例如:

mysql> explain select count(*) from employees.employees e where e.first_name='Tzu' and e.last_name='Terkki';
+----+-------------+-------+------------+-------------+-------------------------+-------------------------+---------+------+------+----------+--------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+-------------------------+-------------------------+---------+------+------+----------+--------------------------------------------------------------------+
| 1 | SIMPLE | e | NULL | index_merge | idx_name,idx_first_name | idx_name,idx_first_name | 66,58 | NULL | 1 | 100.00 | Using intersect(idx_name,idx_first_name); Using where; Using index |
+----+-------------+-------+------------+-------------+-------------------------+-------------------------+---------+------+------+----------+--------------------------------------------------------------------+ mysql> explain select e.emp_no,e.first_name,e.last_name from employees.employees e where e.first_name='Tzu' and e.last_name='Terkki';
+----+-------------+-------+------------+-------------+-------------------------+-------------------------+---------+------+------+----------+--------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+-------------------------+-------------------------+---------+------+------+----------+--------------------------------------------------------------------+
| 1 | SIMPLE | e | NULL | index_merge | idx_name,idx_first_name | idx_name,idx_first_name | 66,58 | NULL | 1 | 100.00 | Using intersect(idx_name,idx_first_name); Using where; Using index |
+----+-------------+-------+------------+-------------+-------------------------+-------------------------+---------+------+------+----------+--------------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

如果使用的索引未涵盖查询中使用的所有列,则仅在满足所有使用的键的范围条件时才检索完整行。

如果其中一个合并条件是InnoDB表的主键上的条件,则它不用于行检索,而是用于过滤掉使用其他条件检索的行。

2. Index Merge Union 访问算法

index uion merge就是多个索引条件扫描,对得到的结果进行并集运算,显然是多个条件之间进行的是 OR 运算。

下面几种类型的 where 条件,以及他们的组合可能会使用到index union merge算法:

  • 条件使用到复合索引中的所有字段或者左前缀字段(对单字段索引也适用)

    key_part1 = const1 AND key_part2 = const2 ... AND key_partN = constN
  • InnoDB表的主键上的任何范围条件

  • 任何符合 index intersect merge 的where条件

SELECT * FROM t1
WHERE key1 = 1 OR key2 = 2 OR key3 = 3; SELECT * FROM innodb_table
WHERE (key1 = 1 AND key2 = 2)
OR (key3 = 'foo' AND key4 = 'bar') AND key5 = 5;

示例3

mysql> explain select e.emp_no,e.first_name,e.last_name from employees.employees e where e.first_name='Tzu' or e.last_name='Terkki';
+----+-------------+-------+------------+-------------+-------------------------+-------------------------+---------+------+------+----------+---------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+-------------------------+-------------------------+---------+------+------+----------+---------------------------------------------------+
| 1 | SIMPLE | e | NULL | index_merge | idx_name,idx_first_name | idx_first_name,idx_name | 58,66 | NULL | 416 | 100.00 | Using union(idx_first_name,idx_name); Using where |
+----+-------------+-------+------------+-------------+-------------------------+-------------------------+---------+------+------+----------+---------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

示例4

mysql> explain select * from employees.employees e where e.emp_no>10011 or e.last_name = 'Terkki';
+----+-------------+-------+------------+-------------+------------------+------------------+---------+------+--------+----------+--------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+------------------+------------------+---------+------+--------+----------+--------------------------------------------+
| 1 | SIMPLE | e | NULL | index_merge | PRIMARY,idx_name | PRIMARY,idx_name | 4,66 | NULL | 149648 | 100.00 | Using union(PRIMARY,idx_name); Using where |
+----+-------------+-------+------------+-------------+------------------+------------------+---------+------+--------+----------+--------------------------------------------+
1 row in set, 1 warning (0.00 sec)

3. Index Merge Sort-Union 访问算法

当WHERE子句转换为OR组合的多个范围条件时,此访问算法适用,但Index Merge union 算法不适用。

SELECT * FROM tbl_name
WHERE key_col1 < 10 OR key_col2 < 20; SELECT * FROM tbl_name
WHERE (key_col1 > 10 OR key_col2 = 20) AND nonkey_col = 30;

sort-union算法和union算法之间的区别在于sort-union算法必须首先获取所有行的行ID,然后在返回任何行之前对它们进行排序。

mysql> show index from t1;
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| t1 | 1 | idx_i1_i2 | 1 | i1 | A | 5 | NULL | NULL | | BTREE | | |
| t1 | 1 | idx_i1_i2 | 2 | i2 | A | 25 | NULL | NULL | | BTREE | | |
| t1 | 1 | idx_i2 | 1 | i2 | A | 5 | NULL | NULL | | BTREE | | |
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec) mysql> explain select * from t1 force index (idx_i1_i2,idx_i2) where i1>1 or i2>5;
+----+-------------+-------+------------+-------------+------------------+------------------+---------+------+------+----------+-------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+------------------+------------------+---------+------+------+----------+-------------------------------------------------+
| 1 | SIMPLE | t1 | NULL | index_merge | idx_i1_i2,idx_i2 | idx_i1_i2,idx_i2 | 4,4 | NULL | 21 | 100.00 | Using sort_union(idx_i1_i2,idx_i2); Using where |
+----+-------------+-------+------------+-------------+------------------+------------------+---------+------+------+----------+-------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from t1 force index (idx_i1_i2,idx_i2)where (i1>4 or i2=4) and d = '2001-01-01';
+----+-------------+-------+------------+-------------+------------------+------------------+---------+------+------+----------+-------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+------------------+------------------+---------+------+------+----------+-------------------------------------------------+
| 1 | SIMPLE | t1 | NULL | index_merge | idx_i1_i2,idx_i2 | idx_i1_i2,idx_i2 | 4,4 | NULL | 10 | 10.00 | Using sort_union(idx_i1_i2,idx_i2); Using where |
+----+-------------+-------+------------+-------------+------------------+------------------+---------+------+------+----------+-------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

4. 参考文档

http://www.ywnds.com/?p=14468
https://dev.mysql.com/doc/refman/5.7/en/index-merge-optimization.html

MySQL 查询优化之 Index Merge的更多相关文章

  1. MySQL 查询优化之 Index Condition Pushdown

    MySQL 查询优化之 Index Condition Pushdown Index Condition Pushdown限制条件 Index Condition Pushdown工作原理 ICP的开 ...

  2. MySQL 优化之 index merge(索引合并)

    深入理解 index merge 是使用索引进行优化的重要基础之一.理解了 index merge 技术,我们才知道应该如何在表上建立索引. 1. 为什么会有index merge 我们的 where ...

  3. MySQL查询优化之 index 索引的分类和使用

    索引的分类 主键索引 (PRIMARY KEY) 唯一的标识符, 主键不可重复, 只能有一列作为主键 唯一索引 (Unique KEY) 避免重复的列出现, 唯一索引可以重复, 多个列都可以标识为唯一 ...

  4. 浅析MySQL中的Index Condition Pushdown (ICP 索引条件下推)和Multi-Range Read(MRR 索引多范围查找)查询优化

    本文出处:http://www.cnblogs.com/wy123/p/7374078.html(保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错误 ...

  5. MySQL Index Merge Optimization

    Index Merge用在通过一些range scans得到检索数据行和合并成一个整体.合并可以通过 unions,intersections,或者unions-intersection运用在底层的扫 ...

  6. MySQL index merge

    深入理解 index merge 是使用索引进行优化的重要基础之一. [ index merge]       当where谓词中存在多个条件(或者join)涉及到多个字段,它们之间进行 AND 或者 ...

  7. MySQL中Index Merge简介

    索引合并优化 官网翻译 MySQL5.7文档 索引合并是为了减少几个范围(type中的range类型:range can be used when a key column is compared t ...

  8. Atitit Mysql查询优化器 存取类型 范围存取类型 索引存取类型 AND or的分析

    Atitit Mysql查询优化器 存取类型 范围存取类型 索引存取类型 AND or的分析     Atitit Mysql查询优化器 存取类型 范围存取类型 索引存取类型 AND or的分析1 存 ...

  9. MySQL查询优化之explain的深入解析

    在分析查询性能时,考虑EXPLAIN关键字同样很管用.EXPLAIN关键字一般放在SELECT查询语句的前面,用于描述MySQL如何执行查询操作.以及MySQL成功返回结果集需要执行的行数.expla ...

随机推荐

  1. window git bash客户端vimrc设置tab缩进

    从开发机写的代码,弄到windows上的客户端git提交,总是显示格式对不起的问题,问题是再vimrc上tab键的缩进不等于4个空格,然后就需要设置成和linux一样的四格缩进. 安装上git bas ...

  2. sql 语句 替换字段的一些内容

    update t_table set field = replace(field,'替换内容','替换为');

  3. 使用top观察一进程的cpu历史占用情况

    #!/bin/shtop -b -n 1 -p 1975| tail -3 >>process1975.log 搞了时间节点,做个定时任务什么的就ok了

  4. C# 面向对象之3个基本特征

    C#是面向对象的语言,每个面向对象语言都有3个基本特征: *封装----把客观的事物封装成类,并将类的内部实现隐藏,以保证数据的完整性. *继承----通过继承可以复用父类的代码. *多态----允许 ...

  5. UVa-11582:Colossal Fibonacci Numbers!(模算术)

    这是个开心的题目,因为既可以自己翻译,代码又好写ヾ(๑╹◡╹)ノ" The i’th Fibonacci number f(i) is recursively defined in the f ...

  6. hdu2586 LCA带边权的Targan算法

    bryce1010模板 http://acm.hdu.edu.cn/showproblem.php?pid=2586 #include<bits/stdc++.h> using names ...

  7. Springboot的static和templates

    static和templates部分参考博客:https://blog.csdn.net/wangb_java/article/details/71775637 热部署参考博客:https://www ...

  8. 一次dbcp和Hikaricp连接池比较联想到的线程池

    最近在测试连接池dbcp和Hikaricp速度时,为了弄清楚Hikaricp速度优势的原因,阅读了二者的源码,源码不是很难,类也没有多少,联想到很多知识,现在来总结一下.

  9. Java编程基础-反射

    一.java反射 1.反射:动态获取类的信息,以及动态调用对象的方法的功能.可以理解为动态看透类的能力. 2.主要功能:在运行时判断任意一个对象所属的类:在运行时构造任意一个类的对象:在运行时判断任意 ...

  10. 6、旋转数组的最小位置------------>剑指offer系列

    题目 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转, ...