MySQL 查询优化之 Index Merge
MySQL 查询优化之 Index Merge
索引合并访问方法可以在查询中对一个表使用多个索引,对它们同时范围扫描,并且合并结果(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的更多相关文章
- MySQL 查询优化之 Index Condition Pushdown
MySQL 查询优化之 Index Condition Pushdown Index Condition Pushdown限制条件 Index Condition Pushdown工作原理 ICP的开 ...
- MySQL 优化之 index merge(索引合并)
深入理解 index merge 是使用索引进行优化的重要基础之一.理解了 index merge 技术,我们才知道应该如何在表上建立索引. 1. 为什么会有index merge 我们的 where ...
- MySQL查询优化之 index 索引的分类和使用
索引的分类 主键索引 (PRIMARY KEY) 唯一的标识符, 主键不可重复, 只能有一列作为主键 唯一索引 (Unique KEY) 避免重复的列出现, 唯一索引可以重复, 多个列都可以标识为唯一 ...
- 浅析MySQL中的Index Condition Pushdown (ICP 索引条件下推)和Multi-Range Read(MRR 索引多范围查找)查询优化
本文出处:http://www.cnblogs.com/wy123/p/7374078.html(保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错误 ...
- MySQL Index Merge Optimization
Index Merge用在通过一些range scans得到检索数据行和合并成一个整体.合并可以通过 unions,intersections,或者unions-intersection运用在底层的扫 ...
- MySQL index merge
深入理解 index merge 是使用索引进行优化的重要基础之一. [ index merge] 当where谓词中存在多个条件(或者join)涉及到多个字段,它们之间进行 AND 或者 ...
- MySQL中Index Merge简介
索引合并优化 官网翻译 MySQL5.7文档 索引合并是为了减少几个范围(type中的range类型:range can be used when a key column is compared t ...
- Atitit Mysql查询优化器 存取类型 范围存取类型 索引存取类型 AND or的分析
Atitit Mysql查询优化器 存取类型 范围存取类型 索引存取类型 AND or的分析 Atitit Mysql查询优化器 存取类型 范围存取类型 索引存取类型 AND or的分析1 存 ...
- MySQL查询优化之explain的深入解析
在分析查询性能时,考虑EXPLAIN关键字同样很管用.EXPLAIN关键字一般放在SELECT查询语句的前面,用于描述MySQL如何执行查询操作.以及MySQL成功返回结果集需要执行的行数.expla ...
随机推荐
- Mybatis分页中遇到的坑3
Mybatis Mapper.xml 配置文件中 resultMap 节点的源码解析 相关文章 Mybatis 解析配置文件的源码解析 Mybatis 类型转换源码分析 Mybatis 数据源和数 ...
- Mybatis分页中遇到的坑2
站在巨人的肩膀上 http://crocutax.com/blog/mybatis-one-to-many-nestes-query-and-page-query Mybatis一对多嵌套查询和 ...
- python操作json来存储简单的数据,pickle来操作复杂的数据
json作为不同语言间进行数据交互的媒介,在当下已经渐渐取代了之前的xml,看一波python操作json # coding = ascii import json import pickle imp ...
- win 7启动tensorboard的详尽步骤
TensorBoard是TensorFlow下的一个可视化的工具,能够帮助我们在训练大规模神经网络过程中出现的复杂且不好理解的运算.TensorBoard能展示你训练过程中绘制的图像.网络结构等. 1 ...
- 19 标签:xml或者html
1 标签:xml或者html 1.1 使用XmlSlurper解析xml groovy处理xml非常容易.XmlSlurper 类用来处理xml.在处理xml方面,还有其他的处理方式,但 ...
- Java微服务 进程间通信
目录 进程间通信 同步调用 异步调用 实现方式 进程间通信 同步调用 同步调用比较简单,一致性强,但是容易出调用问题,出现单点故障,因为之间相互依赖,比如RPC必须要依赖的模块上线可用,己方才能调用, ...
- 洛谷 P1873 砍树
砍树 二分答案,难度较低. #include <iostream> #include <cstdio> #include <algorithm> using nam ...
- 使用 Visual Studio 2017 部署 Azure 应用服务的 Web 应用
本快速入门介绍了如何使用 Visual Studio 2017 创建并部署 Azure Web 应用.在本教程中完成的所有操作均符合1元试用条件. 本快速入门介绍了如何使用 Visual Studio ...
- MySQL常用函数使用示例
#从指定字符中,随机生成12位字符select substring('0123456789abcdefghijklmnopqrstuvwxyz',floor(0+RAND()*36),12); #显示 ...
- Flask应用运行流程
当我们运行项目后,Flask内部都经历了什么 1.app.run()启动项目,ctrl点进源码 app.py: 1)执行了run_simple() 2)注意第三个参数,这里是Flask实例化的对象,在 ...