本文主要详细介绍Mysql索引基本原理,相信读了这篇文章,对于索引不止停留在课堂老师讲的图书馆图书索引,会更加深入理解索引的本质。这样对于怎么优化Mysql,就不止停留在背那几条规则,而是更理解规则之后的原理。
一、重新定义索引
1、索引的定义
索引:实际是一种数据结构。帮助mysql高效获取数据,为什么高效,因为创建索引的数据是按照一定的数据结构排序进行存储的。
索引的数据结构:
数据结构 | 优缺点 |
---|---|
二叉排序树 | 对于单增的数据,和查全表是一样的复杂度。(例如:1,2,3,4)![]() |
红黑树 | 实际就是二叉树,大数插叶子的右端,小数插叶子的左端,当数据变多时,毕竟是二叉树,就会出现level太高,查询速度慢的问题 |
Hash表 | 1、对索引的key进行一次hash计算就可以定位数据存储的位置 。2、很多时候Hash索引要比B+树索引更加高效。3、只能满足“=”,“IN” ,不支持范围查找,因为没有排序 ![]() |
B-tree树:bulb: | 实际使用的是B+树,解决以上几种数据结构的问题。相比上面的二叉树,这个树是N叉树,存储更多的索引,且查询更高效。也可顺序查询。 |
2、B树、B+树
B树:
节点存放创建index的数据(这里假设为ID),这里存储的时候是K-V存储。K指的是ID,V指的是ID对应行所在磁盘文件的地址。
B树的特点是(对于Mysql):
- 叶子节点都有相同的深度,也叶节点的指针为空
- 所有的索引元素不重复
- 节点的数据索引从左到右递增排列
所以的索引元素不重复
B+树:
非叶子节点只存储index的数据,作为冗余节点,来进行对叶子节点进行范围划分。而索引真正存储在叶子节点上,并且也是K-V存储,V存储的是对应行所在磁盘文件的地址。
B+树的特点(对于mysql):
- 实际是B树的变种
- 非叶子节点不存储data,只存储索引,这样可以放更多的索引
- 叶子节点包含所以索引字段
- 叶子节点使用双指针连接,可以访问自己双向节点,提高区间访问的性能
现在你肯定还有一个疑问就是,为什么不用B树,要使用B+树
答案就是:
- 因为为了省出空间存储索引。每一页存储的数据越多,其树的高度就低,查找就会更快。
- 对于范围查找来说,B+树只需遍历叶子节点链表即可,B树却需要重复地中序遍历
面试官:说一下B树与B+树的区别(咳咳,结合索引我觉得你肯定很清楚2种树的区别)
3、查找一次数据真正的流程
如上图,若查找索引K=10的数据是怎样的一个流程
- 所有根节点(2,20),从磁盘load在内存上去
- 把查找的key(10)放在内存进行比对(时间可以忽略不计)
- 查找到对应范围(在2~20范围内),把第二层的节点(3,9,15)从磁盘load到内存,再进行比对(因为节点都是排序好的,所以可以进行二分查找),范围是(9-15)
- 最终定位到叶子节点(10,13),找到K对应的value,也就是磁盘存储的地址,这样就找到对应的数据行
二、innoDB引擎
存储引擎都是对于表而言的。每个表格的存储引擎可以不同。
1、innoDB存储引擎索引实现
对于一个表使用innoDB引擎,它在磁盘存储的文件有2个。
一个是.frm,.ibd。.frm文件存储的就是表的架构,主要组成元素。.ibd存储的是数据和索引。没错,innodb是将数据和索引放在一个文件的。
innoDB索引实现:现在叶子节点的Value值存储的不是对应磁盘文件地址,而是存储的是其他列的值。这就是把值和索引共同存的方法,这样查找到K,对应的行所有值都会被找到,大大节省了查询的效率
- 表数据文件本身就是按照B+树组织的一个索引结构文件
- 聚集索引-叶子节点包含了完整的数据记录
- 建议innodb表必须建组件,并推荐使用整型的自增主键
- 非主键索引结构叶子节点存储的是主键值
聚集索引又是神马?
聚集索引:叶节点包含完整的数据记录。 主键索引也即聚集索引
对于主键索引。若数据和索引放在一起的就是聚集索引(innodb引擎),数据和索引放在不同文件就是非聚集索引。(MyISAM引擎)
那么innodb有没有非聚集索引?
有。对于二级索引,其叶子节点的value值,存储的不是一行数据其他值,而是存储的是主键的值。 当使用二级索引的时候,还需要回表从主键进行查找。效率会慢一点。
为什么建议InnoDB表必须建主键,不建可以吗?
因为有了主键就有聚集索引,查找效率会更高。 若不建可以吗,若不建的话,mysql底层会自动维护一个主键列,这样浪费mysql资源,增加mysql的负担。所以一张表有且只有一个聚集索引。
为什么建议使用整型的自增主键:
- 因为会有大小比较,这样整形比字符串比较肯定会快
- 非自增可能会出现重新组合节点的值,出现分裂,若影响树的平衡还会调整树的平衡,影响效率。自增的情况,会自动加入叶子节点的右边,若没有空间,会自动开辟空间
2、联合索引
联合索引,是将多个值作为索引进行查找。当然了,底层还是B+树。K对应的值有多个(ID,Name),对应的value值是对应行剩下的所有信息。如下图
那对于联合索引是怎么排序的,这里排序要符合最左前缀原则。最左前缀原则就是查询从索引的最左列开始并且不跳过索引中的列,进行排序查询。所以在叶子节点,发现ID一致,然后排序就按照Name进行排序。
若查询的时候不按照顺序建立的K值(ID,Name),也就是先查找Name再查ID,则引擎会自动不选择使用index。因为此时没有任何的排序规则。
三、MySql优化
每一次查找都是在和磁盘做IO操作,优化Mysql也就是想办法减少IO操作。
1、EXPLAIN
使用EXPLAIN可以分析查询性能,EXPLAIN只能分析SELECT语句
对于EXPLAIN需要关注哪些字段呢?
type
关键字 备注 ALL 这样说明执行了全表查询。最差的情况 index 执行全表查询,并且可以通过索引完成结果扫描并且直接从索引中取的想要的结果数据,也就是可以避免回表,比ALL略好,因为索引文件通常比全部数据要来的小 index_merge 利用index merge特性用到多个索引,提高查询效率 const 基于主键或唯一索引唯一值查询,最多返回一条结果 system 查询对象表只有一行数据,这是最好的情况 Extra
关键字 备注 Using filesort 将用外部排序而不是按照索引顺序排列结果,数据较少时从内存排序,否则需要在磁盘完成排序,代价非常高,需要添加合适的索引 Using temporary 需要创建一个临时表来存储结果,这通常发生在对没有索引的列进行GROUP BY时,或者ORDER BY里的列不都在索引里,需要添加合适的索引 Using index 表示MySQL使用覆盖索引避免全表扫描,不需要再到表中进行二次查找数据,这是比较好的结果之一。注意不要和type中的index类型混淆 Using where 通常是进行了全表/全索引扫描后再用WHERE子句完成结果过滤,需要添加合适的索引 rows
该参数要代表了本次查询遍历了多少行数据
2、索引优化的方法
- 如果索引了多列,要符合左前缀法则
- 不在索引做任何的操作(计算、函数、类型转换等),这样会导致索引失效而转向全表扫描
- 尽量使用访问索引的查询,索引列包含查询列,减少select *语句
- mysql在使用不等于的时候,不能使用索引导致全表扫描
- like以通配符(%abc)开始,索引会失效。解决:查询条件是覆盖索引
总之最主要的就是尽可能的使用索引值,避免建立索引而没有使用的情况。
参考文档