一篇文章带你搞懂InnoDB的索引|结合样例
关注公众号【程序员白泽】,带你走进一个不一样的程序员/学生党
前言
前阵子面试的时候,在第三面问到了MySQL索引相关的知识点,并且给出了一些SQL语句分析索引的执行情况。所以今天这篇文章给大家讲讲索引,结合一些案例分析一下一个SQL查询走索引时涉及到的最左前缀原则。
在讲解最左前缀原则之前,先复习一下MySQL索引的重要基础知识(下面都将基于InnoDB存储引擎下的索引规则)
索引类型
主键索引
InnoDB存储引擎使用B+树建立索引,主键索引的非叶子结点存放主键字段的值,通过主键中的字段构建B+树,叶子结点存放对应主键的整一条记录的信息(因此主键索引也称为聚集索引),每张表只能建立一个主键索引(聚集索引) 。
辅助索引
辅助索引(Secondary Index),也叫做二级索引,也是通过B+树建立,与主键索引的唯一不同之处在于,叶子结点存放的是对应行的主键值,而不是行数据 (因此也叫做非聚集索引,获取主键值之后,需要再次去主键索引表中查询该主键对应的记录,获取其叶子结点存储的记录内容,相当于要搜索两张索引表)
举个例子
这里给出一张表,id字段为主键索引,age字段为普通索引,然后插入一些数据,然后给出InnoDB为其维护的两个逻辑上的索引文件结构。
create table T(
`id` int primary key,
`name` varchar(11) not null,
`age` int not null,
index(age)
) # 5.5以后默认是InnoDB存储引擎
# 插入了四条数据:(1, 小明, 15)、(2, 小红, 20)、(3, 小兰, 16)、(4, 小金, 18)
下面给出两个查询语句并分析索引执行情况
select * from T where id = 1 # 按照左侧主键索引搜索树,搜索到id为1的叶子结点,获取其中的记录数据
select * from T where age = 15 # 先按照右侧age建立的辅助索引树找到age=15对应记录主键id值等于1,然后再去左侧主键索引搜索树搜索id=1的这条记录
通过分析第二条SQL,我们得出结论,对于走辅助索引的查询,必然会二次查询主键索引树(当然有特殊情况,下面讲) ,一张表只有一个主键索引,但是可以建立很多的辅助索引,且辅助索引的叶子结点里存放着主键值,那么如果主键是字符串类型或者长度很长,那么必然会导致辅助索引占用的空间增加,所以自增主键往往是一个常用的选择。
覆盖索引
那么所有使用辅助索引的SQL查询语句都必须两次回表吗?当然有特殊情况,如果辅助索引树的叶子结点中的字段,已经覆盖了需要查询的所有字段,则不需要回表(回表的目的是获取辅助索引树中没有的字段数据),覆盖索引我更愿意称之为索引覆盖,它还是归属于辅助索引。
select id from T where age = 15 # 对于这个查询,将查询的字段只要求id,则在搜索完右侧age的辅助索引树之后,即可获得到id=1,无需回表
联合索引
联合索引依旧是辅助索引的一种情况 (不是主键索引就都归属于辅助索引) ,辅助索引可以在多个字段之间建立,如果第一个字段相同则比较第二个字段,依次类推建立索引搜索树结点之间的先后关系,也就是说索引项按照索引定义的字段顺序排序
(后面要讲到的最左前缀原则就是在此基础上来分析的) ,下面举个例子,还是借助上面这张表,但是辅助索引不是单单age字段建立,而是name和age共同建立。
create table T(
`id` int primary key,
`name` varchar(11) not null,
`age` int not null,
key `name_age` (`name`, `age`)
) # 5.5以后默认是InnoDB存储引擎
# 插入了四条数据:(1, 小红, 16)、(2, 小红, 15)、(3, 小兰, 16)、(4, 小金, 16)
下面给出一条针对这个name_age的联合索引的查询语句
select id from T where name = '小红' and age = 15 # 通过上面学习索引覆盖的知识点,你应该能分析出这条sql只会搜索右边的联合索引树,获得到id之后不需要再去回表搜索主键索引树
最左前缀原则
概念
还是以上面的这个联合索引为例,如果我的sql语句如下:
select id from T where name = '小红' # name_age索引树在满足name有序的前提下,满足age有序,因此对单一name字段的查询也可以走这个索引,找到满足条件的第一条记录的id,然后按顺序向后遍历找到其他满足要求的记录id
select id from T where age = 15 # age字段是name_age索引的第二个字段,在name无序的前提下,age的有序是无意义的,索引无法利用这个联合索引,需要全表扫描获取满足age=15的记录
select id from T where name like '小%' # 首先name字段是name_age辅助索引的左侧第一个字段,且通配符%在右侧,因此也可以满足最左前缀原则,在查询时走这个辅助索引,定位到第一个满足name='小%'的记录的id,然后向后遍历找到其他满足条件的记录
# ps.这三个语句都是不用回表的
最左前缀原则:只要你的查询语句涉及的字段满足已有辅助索引的左侧出现顺序(或者匹配字符串的左侧n个字符),而不出现越过某个字段的情况,查询就可以走这个辅助索引,这就是最左前缀原则,查询将返回第一个满足查询条件的记录对应的主键id,根据情况看是否需要回表搜索主键索引树。
提醒:为了方便,我上面作的B+树索引树叶子结点之间的双端链表结构没有标出,这里提醒一下,因为讲最左前缀原则的例子中出现了找到第一个满足条件的记录id之后,按顺序向后遍历的情况,这是得益于B+树叶子结点相互串连的结构
联合索引字段顺序
通过上面的分析,对于一个辅助索引(a, b)
来说,不需要为a
单独再建立索引,但可以再给b
单独建立辅助索引(因为b
为查询条件不满足辅助索引的最左前缀原则),那么思考一下,如果调整联合索引的顺序为(b, a)
,那么就不用单独为b
建立辅助索引,而需要为a
建立辅助索引。此时(a, b)
、b
方案与(b, a)
、a
方案都能满足对(a,b)
、a
、b
三个字段的查询调用辅助索引,差别在于哪?
空间!这里比较好的方案是看a与b哪个字段长,则将其放在联合索引的前部,而需要额外建立辅助索引的用较短的字段,这样综合可以减少空间的使用(如果a字段长,则必有2a+b > 2b+a的空间使用)
索引失效
辅助索引会在最左前缀原则的基础上,一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配。范围列可以用到索引,但是范围列后面的列无法用到索引。举个例子:
create table T(
`id` int primary key,
`name` varchar(11) not null,
`age` int not null,
`sex` varchar(11) not null,
key `name_age` (`name`, `age`)
) # 5.5以后默认是InnoDB存储引擎
# 插入了四条数据:(1, 小红, 16, 女)、(2, 小红, 15, 女)、(3, 小兰, 16, 女)、(4, 小金, 17, 男)
分析下面这条sql的索引调用情况:首先是匹配name like '小%'
,可以走右侧辅助索引树,找到id=2
的记录,然后顺序向后扫描满足age=16
的记录,并不能继续利用联合索引中age这个部分,最终得到id=1
和id=3
的两条记录,最后需要回表搜索主键索引树,因为这个联合索引并没有完全做到索引覆盖,缺少了sex字段。
解释:因为满足name like '小%'的记录可能有多条,而age字段的有序是建立的name有序的基础之上,上图中(小红, 15) (小红, 16) (小金, 17) (小兰, 16),单独看age字段之间是无序的,因此在满足条件的name字段是多个的时候,age字段的索引就丧失功能了,只有当name字段匹配的结果唯一,age字段的有序才有意义。
select * from T where name like '小%' and age = 16
索引下推(MySQL5.6)
对于上面这个查询语句,因为sex字段是没有被联合索引覆盖,因此需要二次回表查询主键索引树,但是显然age字段的值是联合索引的一部分,且查询的是age等于16,而有些记录必然不符合匹配,那还有必要回表吗?
索引下推:在MySQL5.5
以及之前的版本中,在满足范围匹配name like '小%'
之后,并不会继续判断后面个age字段
,直接就回表了,而从MySQL5.6
开始,InnoDB存储引擎在匹配到满足name like '小%'
之后,无法继续使用最左前缀原则的字段(如本例的age)依旧在联合索引中,则会根据这些字段多做一些过滤,不满足条件的记录将不会回表查询,减少了二次搜索的次数。
索引重建
这里补充一点额外的知识,之前听闻过一个索引使用的中出现的问题案例:
有一个线上的记录日志的表,定期会删除早期的数据,经过一段时间的维护,这个表中存放的记录空间稳定在10G,但是索引占用空间有30G,一共40G空间。
原因:InnoDB存储引擎表就是索引组织表,记录数据存放在主键索引叶子结点上,这张表会被不断插入日志记录,且定期删除日志记录,会导致维护索引的B+树频繁发生页的分裂,导致页空间中出现浪费的空间,提高了索引的占用空间。
解决:可以通过重建索引的方式,删除之前的旧索引,并重新创建这个索引,因为数据已经在表中,因此重建索引的过程会将表中的数据按顺序插入,使得页面结构重新恢复紧凑(当然具体重建索引的方案需要结合更多的因素去分析,并不是定期重建索引就一定是好的,这里不多深究)
结束语
这篇文章讲解了InnoDB引擎索引相关的知识点,结合例子分析了一下联合索引的最左前缀原则,希望能给你带来帮助。
关注公众号【程序员白泽】,带你走进一个不一样的程序员/学生党,公众号回复【简历】可以获得我正在使用的简历模板,平时也会同步更新文章。希望大家都能收获心仪的offer~
一篇文章带你搞懂InnoDB的索引|结合样例的更多相关文章
- 五分钟学Java:一篇文章带你搞懂spring全家桶套餐
原创声明 本文首发于微信公众号[程序员黄小斜] 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 本文思维导图 什么是Spring,为什么你要学习spring? 你第一次接触spring框架是在 ...
- 一篇文章带你搞懂 SpringBoot与Swagger整合
Swagger使用由于不喜欢csdn的markwoen编辑器,对代码样式支持不好,看着不舒服,对审美要求比较高的同学移步github:https://github.com/itguang/swagge ...
- 一篇文章带你搞懂DEX文件的结构
*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 DEX文件就是Android Dalvik虚拟机运行的程序,关于DEX文件的结构的重要性我就不多说了.下面,开练! 建议:不要只看 ...
- 一篇文章带你搞懂 etcd 3.5 的核心特性
作者 唐聪,腾讯云资深工程师,极客时间专栏<etcd实战课>作者,etcd活跃贡献者,主要负责腾讯云大规模k8s/etcd平台.有状态服务容器化.在离线混部等产品研发设计工作. etcd ...
- 一篇文章带你搞定 ElasticSearch 术语
这篇文章主要介绍 ElasticSearch 的基本概念,学习文档.索引.集群.节点.分片等概念,同时会将 ElasticSearch 和关系型数据库做简单的类比,还会简单介绍 REST API 的使 ...
- 一篇文章让你搞懂如何通过Nginx来解决跨域问题
Nginx跨域实现 首先大家要搞清楚什么是跨域,为什么会有跨域情况的出现.哪些情况属于跨域? 跨域:由于浏览器的同源策略,即属于不同域的页面之间不能相互访问各自的页面内容 注:同源策略,单说来就是 ...
- 一篇文章教你搞懂日志采集利器 Filebeat
关注「开源Linux」,选择"设为星标" 回复「学习」,有我为您特别筛选的学习资料~ 本文使用的Filebeat是7.7.0的版本,文章将从如下几个方面说明: Filebeat是什 ...
- 一篇文章让你搞懂 SSL 证书
关于结婚这件事 那天和同事讨论到底什么才算是真正的「结婚」?这种话题本来是极其不应该存在的.传统意义的领个证书,办个婚礼.吃吃喝喝,但随着社会各族人民身心发展进化,原本那些繁琐流程简直是反人类,貌似现 ...
- 一篇文章带你看懂AWS re:Invent 2018大会,揭秘Amazon Aurora
本文由云+社区发表 | 本文作者: 刘峰,腾讯云NewSQL数据库产品负责人.曾职于联想研究院,Teradata北京研发中心,从事数据库相关工作8年.2017年加入腾讯数据库产品中心,担任NewSQL ...
随机推荐
- linux内存(一) 内核空间与用户空间
来自如下网站 https://www.cnblogs.com/sparkdev/p/8410350.html 内核空间和用户空间 对 32 位操作系统而言,它的寻址空间(虚拟地址空间,或叫线性地址空间 ...
- 记录java中常用的英文单词01
专业缩写 POJO(plain ordinary java object)--简单的java对象 Spring-jdbc--为了使JDBC更加易于使用,spring在JDBC API上定义了一个抽象层 ...
- sql高级手工注入
非常重要:首先在网站找到管理入口,否则,呵呵就算有用户名和密码,找不到入口,也是白玩.. 注入时,注意通过改变大小写.编码.转换等方式躲过系统检查,顺利执行语句!!! (一)数字型注入 正常步骤: 1 ...
- Linux部署Nacos
此处演示Nacos在Linux(CentOS7)环境中单机版部署,此处演示1.3.0版本. 一.官网下载压缩包 https://github.com/alibaba/nacos/releases 二. ...
- 一个 Spring Bean 定义 包含什么?
一个Spring Bean 的定义包含容器必知的所有配置元数据,包括如何创建一个bean,它的生命周期详情及它的依赖.
- Redis的集群搭建(四)
1.redis-cluster架构图 2.redis-cluster投票:容错 架构细节: (1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽. (2) ...
- 学习Kvm(三)
虚拟化(将一个物理硬件平台虚拟成多个) vmware(模拟出一堆硬件设备,每一个硬件设备都是独立平台) 虚拟化要解决的问题(硬件之上的OS,有用户空间.内核空间:vmware虚拟机所模拟出的多个硬件平 ...
- 判断集合中存在String字符串 或 判断集合中不存在String字符串
一.使用场景 用于集合中有多个相近的字符,无法使用包含判断 如: 这里如果我想判断以上集合中是否包含"信封件-DE"就会被"信封件-DE2"影响到 毕竟:&qu ...
- HMS Core 分析服务 6.4.1版本上线啦,快来看看更新了哪些内容。
更新概览 支持转化事件回传至华为应用市场商业推广,便捷归因,实时调优. 卸载分析模型支持用户卸载前事件和路径分析,深度剖析卸载根因. 实时漏斗体验开放,灵活定位异常流失. 详情介绍 更新一:全面开放深 ...
- 所有用CSS3写的3D特效,都离不开这些知识
起因 昨晚在做慕课网的十天精通CSS3课程,其中的综合练习是要做一个3D导航翻转的效果.非常高大上. 以往这些效果我都很不屑,觉得网上一大堆这些特效的代码,复制粘贴就好了,够快.但是现实工作中,其实自 ...