“html
索引是面试必不可少的内容,接下来带你进入MySQL索引的世界。面试
”sql
- 1. Btree解析
- 2. B+Tree解析
- 3.Hash索引
- 4. B+Tree跟BTree区别
- 5. B+Tree适合作索引的缘由
- 1. 回表
- 2. 覆盖索引
- 3. 最左匹配
- 4. 索引下推
前言
你是否是对于MySQL索引的知识点一直都像大杂烩,好像什么都知道,若是进行深究的话可能一个也答不上来。数据库
假如你去面试,面试官让你聊一下对索引的理解,然而你对索引的理解仅限于,检索数据就是快,是一种数据结构这个层面,那你就只能回家等通知了。服务器
为了不这种尴尬的事情发生,咔咔用时两天将索引的内容在本身理解的范围内进行整理,如整理的不全面能够在评论区进行补充和提建议。数据结构
1、MySQL索引究竟是什么
相信大多数伙伴都买过技术类的书籍,看完没看完不知道,可是目录确定看的次数最多。函数
看目录有没有本身目前的痛点,若是有就会根据目录对应的页码用最快的速度翻阅到相应内容位置。性能
那么在MySQL中一样也是这样的一个道理,MySQL的索引就是存储引擎为了快速找到数据的一种数据结构
优化
一样在MySQL索引中又分了几种类型,分别为B-tree索引、哈希索引、空间索引、全文索引。网站
下文全部内容均在Innodb的基础上讨论。
2、为何要使用索引
索引能够加快数据检索速度
,这也是使用的索引的最主要缘由。
索引自己具备顺序性,在进行范围查询时,获取的数据已经排好了序,从而避免服务器再次排序和创建临时表的问题
。
索引的底层实现自己具备顺序性,经过磁盘预读使得在磁盘上对数据的访问大体呈顺序的寻址,也就是将随机的I/O变为顺序I/O
。
这几点不理解就暂时先放着,继续看下文便可,会给你一个满意的解释。
任何事物都存在双面性,既然能提供性能的提高,天然在其它方面也会付出额外的代价。
索引是跟数据共存,所以会占用额外的存储空间。
索引建立和维护须要时间成本,这个成本随着数据量的增大而增大。
索引建立会下降数据的增、删、改的性能,由于在修改数据的同时还须要修改索引数据。
3、Innodb为何使用B+Tree而不使用BTree
聊到这个问题那就必须得分清楚BTree、B+tree的区别,首先来看一下BTree
1. Btree解析
先来看一下BTree的数据结构是怎么样的,这里咔咔给提供一个网站地址https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
,能够看到关于数据结构的一些实现过程。
数据结构实现过程
先来看BTree的数据结构,下图是咔咔已经将数据填充进去的。
BTree数据结构
这里有一个陌生区关于Max. Degree
,这个你能够理解为阶,也能够理解为度。
例如如今这个值设置的是4,那么在一个节点中最多就能够存储三条数据,设置为5那就能够最多放4条记录。
如今能够看到目前只插入了三条数据。
插入了三条数据
那么再加一条数据,节点就会进行分裂,这个也就验证了当阶设置为n时,一个节点可存n-1条数据。
节点分裂
那接着再来插入几条数据看看。
插入了十条数据
想要达到快速检索数据,那就须要知足俩个特性,一个是有序,另外一个就是平衡。
从下图中能够看到BTree是有必定的顺序性的,平衡性更知足,能够看上文中生成的第一张图。
顺序性
那么在BTree中找一个值是怎么找呢!
例如如今要找一个值9
,看一下寻找过程。
首先看到的数据是4,9是大于4的,因此会往4的右节点寻找。
继续找到范围在6到8的节点,9又大于8,因此还须要往右节点寻找。
最有一步就找到了数据9,这个过程就是BTree数据结构查找数据的执行过程。
寻找过程
了解到了BTree的数据结构后,咱们在来看看在MySQL中关于BTree是如何存储的。
在下图中P表明的是指针,指向的是下一个磁盘块。
在第一个节点中的1六、24就是表明咱们的key值是什么。
date就是这个key值对应的这一行记录是什么。
在MySQL中BTree存储结构
那么此时想要寻找key为33的这条记录应该怎么找。
33在16和34中间,因此会去磁盘3进行寻找。
在磁盘3中进行判断,指针指向磁盘8。
在磁盘8中便可获取到数据33,而后将data返回。
那么在这个过程当中到底读取了多少条数据呢!
在计算以前须要先了解一些知识点。
从MySQL5.7开始,存储引擎默认为innodb,而且innodb存储引擎用于管理数据的最小磁盘单位就是页。
这个页的类型也分为好几种,分别为数据页,Undo页,系统页,事物数据页。
通常说到的页都是数据页。默认的页面大小为16kb,每一个页中至少存储2条或以上的行记录。
那么根据BTree数据查找的过程当中能够得知一共读取了三个磁盘,那么每一个磁盘的大小就是16kb。
而目前的给的案例寻找了三层,那么三层存储的数据就是16kb * 16kb * 16kb = 4096kb。
若是按照一条记录所需内存1kb,那么这三层的BTree就能够存储4096条记录。
各位数据库的数据少则几百万,多则几千万数据,那么BTree的层级就会愈来愈深,相对的查询效率也会愈来愈慢。
这个时候是否是应该思考一个问题,那就是为何在Btree中48kb的内存怎么就只能存储4000多条记录
问题就出如今data上,要知道在计算数据大小时指针地址和key的内存都是没有计算在内的,单单就计算了data的内存。
由于在BTree结构中,节点中不只存储的有key、指针地址还有对应的数据,因此就会形成单个磁盘存储的数据相对不多的缘由。
为了解决单个节点存储数据量小的问题,因而就演变出另外一种结构,也就是下文提到了B+Tree
2. B+Tree解析
依然如初看一下B+Tree的数据结构。
为了方便对比,将BTree和B+Tree的数据结构放到了一块儿。
数据结构对比
那么能够看到在B+Tree中叶子节点是存放了全量的数据,而非叶子节点只存储了key值。
咦!这不是就很好的解决了BTree带来的问题吗?可让每一个节点存储更多的数据。每一个节点存储的数据越多,那么相对的就是树的深度就不会过深。
了解到了B+Tree的数据结构后,咱们在来看看在MySQL中关于B+Tree是如何存储的。
B+Tree数据结构
从上图很明显就能够看到俩点不一样。
第一点:B+Tree全部的数据都存储在叶子节点上。
第二点:B+Tree全部的叶子节点之间是一种链式环结构
那么在这个过程当中到底读取了多少条数据呢!
若是说B+Tree读取数据的深度跟B-Tree的深度同样,都是三层,那么一样的道理每一个磁盘的大小为16kb。
那在B+Tree中非叶子节点能够存储多少数据呢!通常来讲咱们每一个表都会存在一个主键。
根据三层来计算,第一层跟第二层存储的是key值,也就是主键值。
都知道int类型所占的内存时4Byte(字节),指针的存储就给个6Byte,一共就是10Tybe,那么第一层节点就能够存储16 * 1000 /10 = 1600。
同理第二层每一个节点也是能够存储1600个key。
第三层是叶子节点,每一个磁盘存储大小一样安装BTree的计算同样,每条数据占1kb。
那么在B+Tree中三层能够存储的数据就是1600 * 1600 * 16 = 40960000
从这点来看B+Tree存储的数据跟BTree存储的数据根本就不是一个级别。
因此能够得出结论:
B+Tree能保证检索的数据量相对BTree是最多的,并且存储的数据量也是最多的
B+Tree选择索引时尽可能选择所占内存空间小的类型,好比int类型。
key所占内存越小,在节点中存储的范围就越多。
3.Hash索引
先来建立一个hash索引alter table user add index hash_gender using hash(gender);
存储引擎使用的是innodb。
hash索引
会发现name的索引类型仍是为Btree,在innodb上建立哈希索引,被称之为伪哈希索引,和真正的哈希索引不是一回事的,这点必定要明白。
在Innodb存储引擎中有一个特殊的功能叫作,自适应哈希索引,当索引值被使用的很是频繁时,它会在内存中基于BTree索引之上再建立一个哈希索引,那么就拥有了哈希索引的一些特色,好比快速查找
哈希索引就是基于哈希表实现的,假设对 name 创建了哈希索引,则查找过程以下图所示,哈希表是根据键值对进行访问的数据结构,它让检索的数据通过哈希函数映射到散列表的对应位置,查找效率很是高。
哈希索引数据结构
哈希索引存储的是哈希值和行指针,没有存储key值、字段值,但哈希索引多数是在内存完成的,检索数据是很是快的,因此对性能影响不大。
哈希索引不是按照索引值排序的,因此也就没法排序。
哈希索引只支持等值操做,不支持范围查找,在MySQL中只能只用 =、in 、<>
哈希索引在任什么时候候都不能避免表扫描
哈希索引在遇到大量哈希冲突时,存储引擎必须遍历链表的全部行指针,逐行比较。
4. B+Tree跟BTree区别
通过了特别漫长的计算、画图如今基本对俩者的区别有必定认识了吧!
咔咔在这里进行总结一下。
-
B+Tree叶子节点上存储的是全量数据(key+data),而非叶子节点只存储key
-
B+Tree在一样的深度下存储的数据是远远大于BTree的。
-
B+Tree每一个叶子节点都有指向下一个叶子节点的连接。这样的好处在于,咱们能够从任意一个叶子节点开始遍历,获取接下来全部的数据。
5. B+Tree适合作索引的缘由
B+Tree树非叶子节点只存储key值,所以相对于BTree节点能够存储更多的数据,每次读入内存的key值就更多,相对来讲I/O就下降
B+Tree树查询效率稳定,任何数据的查找都是必须从叶子节点到非叶子节点,因此说每一个数据查找的效率几乎都是相同的。
B+Tree树的叶子节点存储的是全量数据,而且是有序的,因此说只须要遍历叶子节点就能够对全部的key进行扫描,在范围查找时效率更高。
以上就是关于Innodb存储引擎为何使用B+Tree做为索引的解析。
4、聚簇索引、非聚簇索引区别
聚簇索引、非聚簇索引也被称之为主索引、二级索引。
那么如何区分聚簇索引和非聚簇索引呢!
首先看一下Innodb引擎下,建立表生成的文件,能够看到有俩个ibd文件。
数据表文件
看到这里不知道你们有没有疑问,为何看有的文章中也会有frm文件呢!可是在这里怎么没有呢!
缘由是在MySQL8.0以后将源数据都存储到了表空间中,因此也就不存在frm文件喽!
都知道这个idb文件会存储数据信息和索引信息。
那再来看一下Myisam存储引擎建立表生产的文件。
myisam存储引擎建立表生成的文件
从图中能够看到建立一个表会生成三个文件,扩展名分别为MYD、MYI、sdi。
MYD:是表数据文件(保存数据的文件)
MYI :是表索引文件(保存索引的文件)
那么就能够得出一个结论
只要数据跟索引存储在一个文件里,那就是聚簇索引,不然就是非聚簇索引。
这个时候就会有人问了,表中有主键的时候,idb文件中存储的是主键+数据,那么当没有设置主键时怎么办呢!
记住这一句话,在Innodb中,数据插入时必须跟一个索引值进行绑定,若是没有主键那就选择惟一索引,若是没有惟一索引就会选择一个6Byte的rowid。
5、表中存在多个索引数据是如何存储的
看了上文的解释,有没有产生过一丝疑问,在Innodb存储引擎下,若是存在多个索引,是否是会产生多个idb文件。
在Innodb中数据只会保存一份,若是有多个索引,会维护多个B+Tree
例如:表字段 id,name,age,sex。
id设置为主键索引(聚簇索引),name设置为普通索引,那么数据到底会存储几份呢!
无论一个表中设置多少个索引,数据只会存储一份,可是这张表会维护多个B+Tree。
按照这个案例中id为主键索引,name为普通索引,那么在这张表中就会维护俩颗B+Tree。
id主键索引跟数据存储在一块儿,name索引所在的B+Tree中叶子节点存储的是主键id的值。
对应的图就是如下俩幅图,能够好好的看一下。
主键索引对应的B+Tree
name索引对应的B+Tree
最后给你们总结一个点:在Innodb中,必定有聚簇索引,其它索引都是非聚簇索引。
这里简单提一下myisam中只有非聚簇索引。
6、索引的几个技术名词
在面试中每每会问这几个关键词,分别为回表、覆盖索引、最左侧原则、索引下推,必定要知道哈!
1. 回表
网上对回表的解释各类各样,咔咔给你说种简单易懂的,但前提是你须要把聚簇索引、非聚簇索引区分清楚。
仍是用上边的案例,id为主键索引,name为普通索引。
此时查询语句为select id,name,age from table where name = 'kaka'
那么这条语句会先在name的这颗B+Tree中寻找到主键id,而后在根据主键id的索引获取到数据而且返回。
其实这个过程就是从非聚簇索引跳转到聚簇索引中查找数据,被称为回表
,也就是说当你查询的字段为非聚簇索引,可是非聚簇索引中没有将须要查询的字段所有包含就是回表。
在这个案例中,非聚簇索引name的叶子节点只有id,并无age,因此会跳转到聚簇索引中,根据id在查询整条记录返回须要的字段数据。
2. 覆盖索引
覆盖索引,根据名字都能理解的差很少,就是查询的全部字段都建立了索引!
此时查询语句为select id,name from table where name = 'kaka'
那么这条语句就是使用了覆盖索引,由于id和name都为索引字段,查询的字段也是这俩个字段,因此被称为索引覆盖。
也就是说当非覆盖索引的叶子节点中包含了须要查询的字段时就被称为覆盖索引
3. 最左匹配
最左匹配原则是在组合索引中存在的。
仍是用以前表信息:表字段 id,name,age,sex。
此时给name,age设置成组合索引。
如下语句中那个不符合最左侧原则。
select * from table where name = ? and age = ?
select * from table where name = ?
select * from table where age = ?
select * from table where age= ? and name= ?
能够自行作一下测验哈!是只有第三条语句不会用到索引,其它的三条语句都会符合最左侧原则。
关于这个最左侧原则远远不止这么简单的,一试就是一个坑,关于这部份内容咔咔后期会在优化文章中提到。
4. 索引下推
仍是使用这条sql语句。
select * from table where name = ? and age = ?
索引下推是在MySQL5.6及之后的版本出现的。
以前的查询过程是,先根据name在存储引擎中获取数据,而后在根据age在server层进行过滤。
在有了索引下推以后,查询过程是根据name、age在存储引擎获取数据,返回对应的数据,再也不到server层进行过滤。
当你使用Explain分析SQL语句时,若是出现了Using index condition
那就是使用了索引下推,索引下推是在组合索引的状况出现概率最大的。
7、索引存储在什么地方
索引的数据文件是存储在磁盘中的,也是须要进行持久化操做。
可是当使用索引时会把数据从磁盘读取到内存中,读取方式为分块读取。
这时就要涉及到操做系统的概念,操做系统在磁盘中获取数据,假设如今要取的数据大小是1kb,但操做系统并不会只取出你须要的这1kb,而是会取出4kb的数据。
为何会是4kb,由于在操做系统中一页的数据就是4kb。
那又为何只须要1kb而取出整页的数据呢!
那就又会涉及到另外一个概念那就是局部性原理
:数据和程序都有汇集成群的倾向,在访问了一条数据以后,在以后有极大的可能再次访问这条数据和这条数据的相邻数据。
因此说MySQL的Innodb存储引擎,在读取数据时也会采起这种局部性原理,每次读取的数据是16kb。
在Innodb存储引擎下每页的大小默认为16kb,这个参数也能够进行调整,参数为innodb_page_size。
最后一点:
既然标题问的是索引数据存储在什么地方,在第一句就直接回答了索引是存储在磁盘中,而且以页为单位进行从磁盘往内存读取。
那为何不直接存储在内存中呢!你有没有这个疑问呢!
若是索引数据只存储在内存中,那么当电脑关机,服务器宕机以后,就须要从新生成索引,这种的效率是十分低的。
8、总结
以上就是咔咔对索引的理解,在尽最大的可能将知识点说全面。
若是还有遗漏,或者文章中有错误的地方还请各位能给出提议。