阳春三月,春暖花开!又到了投简历的季节!金三银四么,许多小伙伴已经安耐不住自己激动的心脏,一张张纸片简历刷刷刷的四下纷飞!
简历是不敢投了,越看书感觉越怂啊~,回想起之前面试的经历,联想几下自己面试的回答,心里只有一万匹 奇怪的 动物奔驰而过!
如果硬要表达的话,估计能用一张图来展示自己的回答了。。
说多了都是泪啊!所以呢,鸡汤还是要喝的,哪怕是黑鸡汤,起码可以让你明白自身的定位不是?
有句话怎么说来着? 遇到困难的时候暂时放一放,第二天的时候,就再也想不起来了。
所以,我又来了,码点字数防止明天真的啥也想不起来了!
数据库,简而言之,就是存储数据的,提供数据读写的系统。我们最常见的就是关系型数据库(MySQL)和非关系型数据库(Redis),今天我们就来简单的认识下MySQL中数据中的一个关键点——索引!
数据的读写无处不在,java代码中,我们会将一些临时数据存入栈中,进行入栈和出栈,或者是存在堆中、方法区中,在需要的时候去获取。当然,这些都只是少量数据的存取,稍微多一点的数据,我们可能会再加一层封装,将数据放到数组或者List中(最终也是存在堆中),在需要的时候去获取数据。
少量的数据存取,速度自然是比较快的,当然,不考虑大文件和系统IO哦!这个我们就不多说了。
那么数组或者说集合的是如何保存(修改)和查看的呢?我们知道,大多数的数据都是查看多于保存(修改),我们先来看下,集合是如何进行查看的,当然我们是不知道数据对应下标的,就好像不是每次查询数据库都知道这是哪一条数据。
画了一张简单的图示:
如上图,我们想要找到数据为60的数据。
对于一个无序的集合,我们只能从第一个数据,也就是6,开始遍历查询,直到对比了16次之后,找到了我们需要的数据,也就是60,这样,我们的一次查找就结束了,时间复杂度是O(n)。这种查找最简单,最通用,但是性能也就一般,若是数据量非常大,查询的数据又很多,那将会十分耗时。
对于一个有序的集合,首先,我们也是可以进行顺序查找。对比16次之后,同样可以找到我们需要的数据,当然效率不是很高就是了。
那如果我们换一种方式查找呢?比如上图中的二分查找?可以看到,我们只用了5次就查询到了60这个数据,比顺序查找的次数少了11次,这还是数据比较少的情况,若是数据多呢?二分查找对比顺序查找的性能更会急剧提升。
对于查找算法,大家如果不太熟悉的话可以去看下相关的数据结构。
可以看到,二分查找的性能对比顺序查找性能已经相当高。但是二分查找还有个缺点,那就是每次都要计算中位值,然后进行对比。那么这一步能不能省掉呢?
想要直接省掉,当然不行,但是如果在写入的时候做一种特殊的操作,组装一种特殊的结构,中位值这个计算也是完全可以省掉的。
没错,也许大家已经想到了,这种结构就是二叉树,或者更具体一点,二叉查找树!
如图所示,二叉查找树的左子树小于父节点,父节点小于有节点,这样搜索数据的,根据树的结构直接进行查询,速度更快。
在插入的时候,如果数据比父节点大,就往左边遍历,直到找到这个节点的位置,如果比父节点大,就往右边遍历,最终确认数据的位置。
但是,如果新增的数据一直比上一个数据大呢?那么最终的二叉查找树又变成了什么样子?
没错!最终的二叉查找树变成了类似链表的结构!链表查询,从头结点,一个一个向下遍历,若是没有结构优化,性能和顺序查询相差不大。因此,二叉树很显然不是我们想要的结构。不过呢,二叉查找树上面还有一种二叉平衡查找树!就是你在每次增加一个新的数据的时候,整个树的结构会发生转变,让左子树和右子树数据个数接近,好像是一杆秤一样,左右‘平衡’,是二叉查找树的又一进阶。上面的类似链表的二叉查找树,依次添加进入二叉平衡查找树,结构转换后,如下图:
这样查询起来是不是就简单多了?
哪位兄弟如果对树的结构不太熟悉,可以去了解下数据结构中的树。给大家一个链接,演示树状结构起来,超级方便哦!
数组的查询,我们先看到这里,下面我们来看下数据库的查询。上面已经说了,平衡二叉查找树查询起来很快,那MySQL为什么不用呢?
我们看一下平衡二叉查找树,根节点是一个数据,子节点是两个数据,再下面是四个数据。那么,假设数据库有1024*1024 = 2^20 =1,048,576个数据(百万级).那么整个树的结构就要有20层,也就是说,哪怕查询大多数数据,我们都要遍历十几乃至20层。而且如果查询多个数据,而且数据比较分散,那么查询的次数还会翻倍。
要知道,数据库查询的时候,不是一眼过去就知道哪个大哪个小了,这些数据都是要加载到内存里面进行比较的。而且每次磁盘加载到内存,都是一次IO,而且IO是最耗时间的操作!如果一次查询,需要进行的IO次数非常多,那么查询的速度一定不可能会快。
而且在保存的时候,20层的二叉树,在进行平衡的时候,绝对是个让人头疼的操作,如果转换的节点多的话,速度估计会非常慢!
那么数据究竟是怎么设计的,怎么来保证高效的查询的呢?让我们走进索引看一看!
在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。
索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的SQL语句执行得更快,可快速访问数据库表中的特定信息。
当表中有大量记录时,若要对表进行查询,第一种搜索信息方式是全表搜索,是将所有记录一一取出,和查询条件进行一一对比,然后返回满足条件的记录,这样做会消耗大量数据库系统时间,并造成大量磁盘I/O操作;第二种就是在表中建立索引,然后在索引中找到符合查询条件的索引值,最后通过保存在索引中的ROWID(相当于页码)快速找到表中对应的记录。
MySQL的索引底层结构有两种,hash和B+ tree ,我们来简单的看一看。小提一下,本文中关于MySQL的内容,针对的是5.7版本及以上,底层针对的是innerDB
1.Hash结构
1、介绍:hash索引,有点类似于hashMap。对数据表的某个字段创建hash索引的时候,MySQL数据库会根据这个字段生成一个唯一的hash值,存储起来,并添加一个指向这条数据的指针。如果存在hash冲突的话,hash索引使用的也是链式存储,这点和hashMap很类似。例:比如,你要在图书馆存一本书《java从入门到放弃》,针对本书直接生成了一个唯一编码88888888,然后这个编码指向了这本书所在的图书室、书架、第几层、第几本,这样你想要拿这本书的时候,是不是就可以直接找到了?
2、创建索引语句
-- -- 添加hash结构索引
ALTER TABLE 'table-name'
ADD INDEX 'ind-name'('column') USING HASH COMMENT '备注';
3、优点
结构简单,查询速度快。hash查询,时间复杂度接近O(1),只要确认了这个数据的hash值,直接就能定位到数据,比树状结构查询还要快。
4、不足
只能查询单条数据,或者是in语句,不能进行范围查询,也不能进行排序,而且如果哈希碰撞很多的话,性能也会变得很差。
2.B+ Tree
1、认识B Tree
MySQL的索引的一种使用的是B+ Tree,不过在了解B+ Tree之前,我们先来看下B Tree。
B-tree中,每个结点包含:
1、本结点所含关键字的个数;
3、关键字;
4、指向子结点的指针;
对于一棵m阶B-tree,每个结点至多可以拥有m个子结点。各结点的关键字和可以拥有的子结点数都有限制,规定m阶B-tree中,根结点至少有2个子结点,除非根结点为叶子节点,相应的,根结点中关键字的个数为1~m-1;非根结点至少有[m/2]([],向上取整)个子结点,相应的,关键字个数为[m/2]-1~m-1。
可以看到,B-Tree中每个节点可以存储多个数据,这些数据是有序的,一个节点中的数据可以直接拿出来,对比之后,然后取第二层,然后继续对比。如果,第一个节点存储1000个数据的话,那么百万级别的数据,只需要存储三层就够了,而且数据都是排好序的,即使要获取多个数据也远比平衡二叉查找树简单许多。
B-Tree如果建立索引的话,就是每个数字下面都相当于存储了一个数据,针对上面1-16 这16个数字的B-Tree,如果要创建,下面我画出部分示意图:
可以看到,每个索引(数字)都带着对应数据的信息,想要查询数据的话,找到对应的索引,就能定位到对应的位置,这样查询效率还是比较高的。当然,也有局限性,比如每次加载数据都要加载索引下面对应的data,会增加数据块的容量,增加IO次数,其次,如果查找的数不在同一层的同一段,比如上图的11,12,13这三个数据,查询的时候可能要加载三次,这个也会消耗一定的性能。
2.认识B+ Tree
B+ Tree与B Tree类似,不过B+ Tree 在叶子节点,保存了所有的数据节点,并且用链表关联起来,如图:
整体看起来结构似乎有所区别,主要是因为保存了所有节点到叶子节点的缘故。这样的话,如果创建索引,直接把数据挂放到最底层的叶子节点。查找的时候,只是获取索引到内存,定位到数据之后,在最后一层,也就是叶子节点获取对应的数据,中途查找过程中,系统的IO无疑会轻松无比,而且,由于最后一层是根据索引排好序的,若是进行范围查找,直接就能根据链表查询数据,无序再次进行树遍历,明显会节省不少性能滴。
3.创建B+Tree索引语句
-- -- 添加B+ Tree结构索引
ALTER TABLE 'table-name'
ADD INDEX 'ind-name'('column') USING BTREE COMMENT '备注';
4.优缺点
对比B-Tree,可以看到B+Tree ,将数据存储在最底层的叶子节点,每次进行数据查询的时候,仅仅把节点数据加载到内存中, 无疑可以加载更多数据,也就是,可以减少IO的次数,提高查询的效率,同时最后一层存放数据使用链表关联,进行范围查找的话无疑会简洁许多。当然,B-Tree也有自己的优点,若是进行单个数据的查询,B-Tree查询数据的层级明显会比B+Tree少一些,因为B+Tree的数据都放在了最后一层的叶子节点,对比上文的两张图,也可以看到B+Tree明显多了一层。不过,相对适用性而言,B+Tree明显更适合于数据库查询结构选择,毕竟数据的查询不会每次只查询一条,而且数据库中经常会用到排序,范围查询等操作。
先到这里吧,下面的接着来~ 如有不足,望请指出【抱拳】。
No sacrifice,no victory~