查询中,有没有可能多个索引一起用呢?

其实我们之前所讲的回表,就是两个索引树同时使用,先在二级索引树中搜索到对应的主键值,然后在再去主键索引树中查询完整的记录。

但是我今天的问题是,两个不同的二级索引树,会同时生效吗?理论上来说,应该是可以同时生效的,不然这个 MySQL 也太笨了。不过根据日常开发经验,这种事情最好能够避免,如果发生了同时搜索两棵索引树的事情,大概是你的索引设计有问题,此时就要去检查一下索引的设计是否合理。

加粗的是实践经验,但是对于两个索引同时生效的知识点,我们还是要懂,一起来看下。

1. 索引合并

例如我有如下一张表结构:

CREATE TABLE `user` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`username` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`address` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`password` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`email` varchar(16) COLLATE utf8mb4_unicode_ci DEFAULT NULL,PRIMARY KEY (`id`),KEY `username` (`username`),KEY `address` (`address`)
) ENGINE=InnoDB AUTO_INCREMENT=100001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

这个表里边有 username 和 address 两个索引,注意是两个索引,每个索引中有一个字段,这不是联合索引。

现在我的查询 SQL 如下:

select * from user where username='1' or address='1';

搜索条件有两个,username 和 address,这是两个索引,分属于两棵不同的索引树。那么它在搜索的时候会两棵索引树都去搜索吗?还是只搜索一颗索引树,再用另一个搜索条件过滤第一棵树搜索出来的结果?

我们来看下数据库执行计划:

974980210ae95ab199232d4e968bc1be.png

大致上瞥一眼这个执行计划,大家也能猜出来,这里其实两个索引都用到了,在这个执行计划中有几个新面孔:

  • type 为 index_merge

  • Extra 为 Using union(username,address); Using where

这个 type 中的 index_merge 就是索引合并。

2. 旧版玩法

当然这个 index_merge 并不是一开始就有的,这是从 MySQL5.0 开始引入的东西。虽然大家现在基本山不会再用到 MySQL5.0 之前的版本了,但是我这里还是说一下,加深大家对 MySQL 的理解。在 MySQL5.0 之前,对于我们上面给出的查询 SQL,是不会走索引的,会全表扫描。在那个年代,如果你想实现上面这个查询,但是又想走索引,你的 SQL 得这样写:

select * from user where username='1' union all select * from user where address='1' and username!='1'

不过这种写法很明显有点笨拙。

所以,从 MySQL5.0 开始,在查询中可以自动使用多个索引进行扫描,并将结果进行合并,也就是我们前面所说的索引合并(index_merge)。

3. 三种情况

索引合并这种算法有三个变种,我们分别来看。

3.1 union

这是求两个索引的并集。

我们来看如下 SQL:

select * from user where username like '1%' or address like '1%';

这个 SQL 在执行的过程中就会涉及到两个索引,需要去两棵索引树中进行搜索,再对搜索结果求并集,我们来看一下该 SQL 的执行计划:

8f99fe34f7782c3b81d4c1d80e44a35b.png

可以看到,这个执行计划中已经发生了索引合并(看 type 、key、Extra)。

那么是不是只要是两个索引查询就总会发送索引合并呢?我们再来看一个栗子:

select * from user where username>'a' or address='1';
a63013befe93145ed5bc9f2f792faa2c.png

大家看一下,只是搜索条件变了一下而已,这里就没用索引合并了,而变成了全表扫描,这是为什么呢?这就引出来索引合并的一个条件,即:每个索引对应的搜索条件,搜到的主键必须是有序的,如果搜到的主键是无序的,抱歉,索引合并用不了。在二级索引中,数据按照二级索引的顺序进行排序,结构类似下面这样:

username主键
a20
b30
c9
c10
c18
d1
d5

当 username 相同的时候,主键是有序的,当 username 不同的时候,就不能保证主键有序了,如果获取到的主键无序,就无法实现索引合并了。

这又引出来一个问题,为什么获取到的主键有序才能发生索引合并呢?因为只有当主键是有序的,将来去重(union、sort-union)亦或者求交集(intersect),效率都要高一些。

从 MySQL5.0 开始,索引合并默认是开启的,当然你也可以选择关闭,关闭 union 索引合并方式如下:

SET optimizer_switch = 'index_merge_union=off';

关闭之后再来看执行计划:

25b6f45dc6c7538eafbc05d8c8dfcb7a.png

大家看到,依然发生了索引合并,但是这次不是 union,而是 sort_union 了,那我们接下来就来看下什么是 sort_union。

3.2 sort_union

sort_union 基本上和 union 一样,只是多了一个排序的能力。

因为前面我们说,如果获取到无序的主键,就不会发生索引合并,可能最终会直接上全表扫描。因此 MySQL 里边又搞了一个 sort_union,就是先在 username 索引树和 address 索引树中同时进行搜索,分别拿到主键值之后先进行排序,排序完了再进行去重,然后回表拿完整的数据。

和 union 相比主要是多了加粗的那一步。

那我们继续,关闭 sort_union,如下:

SET optimizer_switch = 'index_merge_sort_union=off';

关闭之后,再去看执行计划,如下:

9f969a718cf3eeedfc9a997a75a65386.png

此时就没有索引合并了,直接全表扫描。

3.3 intersect

这个是求两个索引的交集。

例如如下 SQL:

select * from user where username like '1%' and address like '1%';

这个 SQL 在执行的过程中就有可能出现求交集的情况。当然这并非绝对的,具体还要看优化器优化后的情况。

松哥尝试了很久,没法复现一个例子出来,主要是我的模拟数据不太对味。如果小伙伴们有现成的 Using intersect 例子欢迎留言分享(执行计划 Extra 中会出现 Using intersect 的)。

但是我把这个原理这里和大家分享下,我们来看如下一张图:

aee0ac32fb8c83ddea8fe9b0fb914a94.png
图片源自网络

假设有二级索引 S 和二级索引 T,现在交叉获取主键(这里有一点需要注意,如果我们是单独在 S 和 T 上搜索,且 S 上搜索条件是 username like '1%',T 上的搜索条件是 address like '1%',那么在搜索的过程中,各自拿到的主键 id 是有序的,这也是 intersect 的前提):

  1. 首先去二级索引 S 上去搜索,找到第一条满足条件的记录,由于二级索引的叶子结点保存的是主键值,此时拿到主键值之后,先不要急着回表。

  2. 接下来去二级索引 T 上去搜索,找到第一条满足条件的记录,并且拿到对应的主键值。

  3. 比较第一步和第二步搜索拿到的主键值:3.1 如果主键值不相等,则舍弃值小的主键,留下大的主键,下一次在 S 上搜索的时候,就拿着这个大的主键和 S 上搜索出来的主键进行比较。3.2 如果主键值相等,则说明这个主键是满足搜索条件的,那就拿着这个主键回表。

  4. 重复前三步,直到各自索引中没有满足条件的记录为止。

这就是所谓的交叉获取主键

好啦,这就是索引合并的三种情况。

4. 小结

很多小伙伴可能会说,既然有索引合并,是不是我索引就可以随便建立了?nonono!索引合并是一种不得已而为之的办法,如果发生了索引合并,大概率是你设计的索引不太合理导致的,所以我们应该去琢磨该如何优化索引。

参考资料:

  • https://dev.mysql.com/doc/refman/8.0/en/index-merge-optimization.html

  • 《MySQL 是怎么运行的》

  • 《高性能 MySQL》

  • https://www.modb.pro/db/29619

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544177.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ruby array_Ruby中带有示例的Array.sample()方法

ruby arrayArray.sample()方法 (Array.sample() Method) In this article, we will study about Array.sample() method. You all must be thinking the method must be doing something which is quite different from all those methods which we have studied. It is not as…

ThreadLocal夺命11连问

前言前一段时间,有同事使用ThreadLocal踩坑了,正好引起了我的兴趣。所以近期,我抽空把ThreadLocal的源码再研究了一下,越看越有意思,发现里面的东西还真不少。我把精华浓缩了一下,汇集成了下面11个问题&…

关于静态库、动态库的区别汇总

real framework中不可以使用类别 或 不可以不包含类文件real framework 中直接调用NSClassFromString函数会返回null 需要强制加载指定类 或 直接通过类名引用linux中静态库和动态库的区别一、不同库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行…

PHP array_pad()函数与示例

PHP array_pad()函数 (PHP array_pad() function) array_pad() function is used to pad an array to given size with a specified value and returns a new array with a specified value. array_pad()函数用于将数组填充到具有指定值的给定大小,并返回具有指定值…

Spring Boot 优雅配置多数据源

大约在19年的这个时候,老同事公司在做医疗系统,需要和HIS系统对接一些信息,比如患者、医护、医嘱、科室等信息。但是起初并不知道如何与HIS无缝对接,于是向我取经。最终经过讨论采用了视图对接的方式,大致就是HIS系统提…

(转)新ITC提交APP常见问题与解决方法(Icon Alpha,Build version,AppIcon120x120)(2014-11-17)...

1)ICON无法上传,提示图片透明(有Alpha通道)苹果现在不接受png里的Alpha了,提交的图标带有Alpha通道就提示:简单处理:用自带的预览打开,导出时不勾选Alpha,仍保存为png格式…

python 向量取整数_随机整数向量| 使用Python的线性代数

python 向量取整数Prerequisite: 先决条件: Defining a Vector using list 使用列表定义向量 Defining Vector using Numpy 使用Numpy定义向量 Random Integer Vector is a type of vector having a random integer value at each entry. Such types of vectors ha…

Spring 夺命 35 问!

有人说,“Java程序员都是Spring程序员”,可以看出Spring在Java世界里举足轻重的作用。基础1.Spring是什么?特性?有哪些模块?Spring Logo一句话概括:Spring 是一个轻量级、非入侵式的控制反转 (IoC) 和面向切…

Android百度地图开发03之地图控制 + 定位

前两篇关于百度地图的blog写的是,一些基本图层的展示 和 覆盖物的添加地理编码和反地理编码。 接下来,这篇blog主要说一些关于地图控制方面的内容和定位功能。 百度地图提供的关于地图的操作主要有:单击、双击、长按、缩放、旋转、俯视等。 地…

软件工程需要学什么_为什么我们需要软件工程?

软件工程需要学什么Software engineering is the application of the set of pre-defined procedures while developing any project. But why do we need Software engineering? What factors made us implement these predefined set of procedures and protocols while dev…

IDEA 版 Postman 面世了,功能真心强大!

IDEA是最常用的开发工具,很多程序员都想把它打造成一站式开发平台,于是安装了各种各样的插件。最近发现了一款IDEA插件RestfulFastRequest,细节做的真心不错,说它是IDEA版的Postman也不为过,推荐给大家!Res…

DNS子域授权

转载于:https://blog.51cto.com/changeflyhigh/1697257

mongo数据库插入数据_深入研究Mongo数据库

mongo数据库插入数据More popularly known as "mongoDB". It is a no-sql based database. 俗称“ mongoDB” 。 这是一个基于无SQL的数据库。 BASIC STRUCTURE OF MONGO DB MONGO DB的基本结构 A COLLECTION IN MONGODB having 3 DOCUMENTS MONGODB中有3个文档的集…

java方法重载和重载方法_我们可以在Java中重载main()方法吗?

java方法重载和重载方法The question is that "can we overload main() method in Java?" 问题是“我们可以在Java中重载main()方法吗?” Yes, We can overload the main() method in Java. 是的,我们可以重载Java中的main()方法 。 JVM cal…

五分钟,手撸一个Spring容器!

Spring是我们最常用的开源框架,经过多年发展,Spring已经发展成枝繁叶茂的大树,让我们难以窥其全貌。这节,我们回归Spring的本质,五分钟手撸一个Spring容器,揭开Spring神秘的面纱!从什么是IOC开始…

一步一步搭建客服系统 (7) 多人共享的电子白板、画板

多人共享、同时操作的电子白板,让不同的参入者以不同的颜色来画画;可以保存当前room的内容,以让后来者可以直接加载所有内容。 在github上找到一个用html5 canvas实现的一个电子白板的例子: https://github.com/kblcuk/canvas-whi…

Spring Cloud OpenFeign 的 5 个优化小技巧!

作者 | 磊哥来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)OpenFeign 是 Spring 官方推出的一种声明式服务调用和负载均衡组件。它的出现就是为了替代已经进入停更维护状态的 Feign&am…

java的equals方法_Java Vector equals()方法与示例

java的equals方法向量类的equals()方法 (Vector Class equals() method) equals() method is available in java.util package. equals()方法在java.util包中可用。 equals() method is used to check whether this Vector is the same or equals to the given object (ob) or …

推荐几个好文章

1 cocos2dx各种行动 http://www.cnblogs.com/linux-ios/archive/2013/04/06/3001946.html 2 cocos2dx自己定义曲线 http://blog.csdn.net/ufolr/article/details/7447773 3 lua中载入cocostudio动画,触发帧事件(非常全,非常具体。还有源代码…

treeset java_Java TreeSet first()方法与示例

treeset javaTreeSet类的first()方法 (TreeSet Class first() method) first() method is available in java.util package. first()方法在java.util包中可用。 first() method is used to retrieve the first lowest element in this TreeSet. first()方法用于检索此TreeSet中…