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

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

但是我今天的问题是,两个不同的二级索引树,会同时生效吗?理论上来说,应该是可以同时生效的,不然这个 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,一经查实,立即删除!

相关文章

ThreadLocal夺命11连问

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

Spring Boot 优雅配置多数据源

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

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

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

Spring 夺命 35 问!

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

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

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

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个文档的集…

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

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

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

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

Java常用类:7000字一次性帮你总结好啦!

来源:cnblogs.com/lwtyyds/p/15678152.html常用类概述内部类内部类的分类:1.成员内部类(非静态内部类)2.局部内部类4.匿名内部类Object类Object类常用方法:1.equals方法2.hashCode方法3.toString方法4.finzlize方法包装…

CentOS6.4系统启动失败故障排查

转:http://www.centoscn.com/CentosBug/osbug/2014/1028/4011.html 操作系统启动失败如下图报错: 故障现象: 从图中可以看到,操作系统启动的过程中,fsck在执行文件系统检测时出现了错误,并且是在检查/dev/m…

Linux内存管理--物理内存分配【转】

转自:http://blog.csdn.net/myarrow/article/details/8682819 1. First Fit分配器 First Fit分配器是最基本的内存分配器,它使用bitmap而不是空闲块列表来表示内存。在bitmap中,如果page对应位为1,则表示此page已经被分配&#xf…

JDK的一个Bug,监听文件变更要小心了

背景 在某些业务场景下,我们需要自己实现文件内容变更监听的功能,比如:监听某个文件是否发生变更,当变更时重新加载文件的内容。看似比较简单的一个功能,但如果在某些JDK版本下,可能会出现意想不到的Bug。本…

推荐 17 个压箱底的常用类库

前言在java的庞大体系中,其实有很多不错的小工具,也就是我们平常说的:轮子。如果在我们的日常工作当中,能够将这些轮子用户,再配合一下idea的快捷键,可以极大得提升我们的开发效率。今天我决定把一些压箱底…

02、django中的上下文

2019独角兽企业重金招聘Python工程师标准>>> 1、譬如设置网站的名称,setting中设置变量: # setting.py SITE_NAME "我的小站"2、在view中写函数将该变量转换成字典,做返回值 from django.conf import settings def site_key(request):# 这里使…

实战:10 种实现延迟任务的方法,附代码!

作者 | 磊哥来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)这篇文章的诞生要感谢一位读者,是他让这篇优秀的文章有了和大家见面的机会,重点是优秀文章&#xff…

面渣逆袭:Redis连环五十二问!三万字+八十图详解!

基础1.说说什么是Redis?Redis图标Redis是一种基于键值对(key-value)的NoSQL数据库。比一般键值对数据库强大的地方,Redis中的value支持string(字符串)、hash(哈希)、 list(列表&…

EasyExcel太方便易用了,强烈推荐!

背景 系统中经常要导出大量的数据,格式基本上都是Excel,然而每次导表都是对系统内存的一次挑战。在Java领域,生成或解析Excel的框架比较有名的当属Apache的poi和jxl了。但使用它们,会面临着严重的内存损耗问题。如果系统的并发量还…

【端午】送3本书!

白天在公司搬砖,晚上到家赶紧给小伙伴们安排一波福利,这次送的书是 H 大新出的《深入理解Java核心技术:写给Java工程师的干货笔记(基础篇)》。书中介绍了普通Java工程师必须要学习的相关知识点,包括面向对象…