⭐️写在前面
这里是允谦的学习之路
如果对你有帮助,给博主一个免费的点赞以示鼓励把QAQ
博客主页 允谦的学习小屋
⭐️更多文章请关注允谦主页
文章发布日期:2022.02.23
java学习之路!
欢迎各位点赞评论收藏⭐️
冲冲冲
MySQL官方对索引的定义:索引是帮助和MySQL搞笑获取数据的数据结构。
索引的本质:索引是数据结构,你可以简单理解为“排好序的快速查找数据结构”,满足特定查找算法。这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法
索引是在存储引擎中实现的,因此每种存储引擎的索引不一定完全相同,并且每种存储引擎不一定支持所有索引类型。同时,存储引擎可以定义为每个表的最大索引数和最大索引长度。所有存储引擎支持每个彪啊至少16个索引,总索引长度最少为256字节。
先看一个精确匹配的例子:
select [列名列表] from 表名 where 列名 = xxx;
假设目前表中的记录比较少,所有的记录都可以被存放在一个页中,在查找记录的时候可以根据搜索条件的不同分为两种情况:
可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录。
因为在数据页中并没有对非主键列建立所谓的页目录,所以我们无法通过二分法快速定位相应的槽。这种情况下 只能从最小记录开始依次遍历单链表中的每条记录,然后对比每条记录是不是符合搜索条件。很显然,这种查找 的效率是非常低的。
大部分情况下我们表中存放的记录都是非常多的,需要好多的数据页来存储这些记录。在很多页中查找记录的话可以分为两个步骤:
1、定位到记录所在的页
2、从所在页内中查找相应的记录
在没有索引的情况下,不论是根据主键列或者其他列的值进行查找,由于我们并不能快速的定位到记录所在的页,所以只能从第一个页沿着双向链表一直往下找,在每一个页中根据我们上面的查找方式去查找指定的记录。因为要遍历所有的数据页,所以这种方式显然是超级耗时的。如果一个表有一亿条记录呢?此时索引应运而生。
我们在根据某个搜索条件查找一些记录时为什么要遍历所有的数据页呢?因为各个页中的记录并没有规律,我们并不知道我们的搜索条件匹配哪些页中的记录,所以不得不依次遍历所有的数据页。所以如果我们想快速的定位到需要查找的记录在哪些数据页中该咋办?我们可以为快速定位记录所在的数据页而建立一个目录,建这个目录必须完成下边这些事:
下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值。
假设:每个数据页最多能存放3条记录(实际上一个数据页非常大,可以存放下好多记录)。有了这个假设之后我们向index_demo表插入3条记录:
insert into index_demo values(1,4,'u'),(3,9,'d'),(5,3,'y');
那么这些基类已经按照主键值的大小串联成一个单向链表了,如图所以:
从图中可以看出来,index_demo表中的3条记录都被插入到了编号为10的数据页中了。此时我们再来插入—条记录:
insert into index_demo values(4,4,'a');
因为页1日最多只能放3条记录,所以我们不得不再分配一个新页:
注意,新分配的数据页编号可能并不是连续的。它们只是通过维护着上一个页和下一个页的编号而建立了链表关系。另外,页10中用户记录最大的主键值是5,而页28中有一条记录的主键值是4,因为5>4,所以这就不符合下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值的要求,所以在插入主键值为4的记录的时候需要伴随着一次记录移动,也就是把主键值为5的记录移动到页28中,然后再把主键值为4的记录插入到页10中,这个过程的示意图如下:
这个过程表明了在对页中的记录进行增删改操作的过程中,我们必须通过一些诸如记录移动的操作来始终保证这个状态一直成立:下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值。这个过程我们称为页分裂。
给所有的页建立一个目录项
由于数据页的编号可能是不连续的,所以在向index_demo表中插入许多条记录后,可能是这样的效果:
因为这些16KB的页在物理存储上是不连续的,所以如果想从这么多页中根据主键值快速定位某些记录所在的页,我们需要给它们做一个目录,每个页对应一个目录项,每个目录项包括下边两个部分:
以页28为例,它对应目录项2,这个目录项中包含着该页的页号28以及该页中用户记录的最小主键值5。我们只需要把几个目录项在物理存储器上连续存储(比如:数组),就可以实现根据主键值快速查找某条记录的功能了。比如:查找主键值为20的记录,具体查找过程分两步:
1.先从目录项中根据二分法快速确定出主键值为20的记录在目录项3中(因为 12<20 < 209),它对
应的页是页9。
2.再根据前边说的在页中查找记录的方式去页9中定位具体的记录。
索引按照物理实现方式,索引可以分为2种:聚簇和非聚簇索引。我们也可以吧非聚簇索引称为二级索引或者辅助索引
聚簇索引并不是一种单独的索引类型,而是一种数据存储方式(所有的用户记录都存储在叶子结点),也就是所谓的索引即数据,数据即索引。
术语“聚簇”表示数据行和相邻的键值聚簇的存储在一起
特点:
1、使用记录主键值的大小进行记录和页的顺序,这包括三个方面的含义:
2、B+树的叶子结点存储的是完整的用户记录。
所谓完整的用户记录,就是指这个基类中存储了所有列的值(包括隐藏列)。
我们把具有这两种特性的B+树称为聚簇索引,所有完整的用户记录都存放在这个聚簇索引的叶子节点处。这种聚簇索引并不需要我们在MySQL语句中显式的使用INDEX语句去创建,InnoDB存储引擎会自动的为我们创建聚簇索引。
优点:
缺点:
限制:
聚簇索引和非聚簇索引的区别:
我们也可以同时以多个列的大小作为排序规则,也就是同时为多个列建立索引,比方说我们想让B+树按照c2和c3列的大小进行排序,这个包含两层含义:
从MySQL的角度讲,不得不考虑一个现实问题就是磁盘IO。如果我们能让索引的数据结构尽量减少硬盘的Io操作,所消耗的时间也就越小。可以说,磁盘的I/0操作次数对索引的使用效率至关重要。
查找都是索引操作,一般来说索引非常大,尤其是关系型数据库,当数据量比较大的时候,索引的大小有可能几个G甚至更多,为了减少索引在内存的占用,数据库索引是存储在外部磁盘上的。当我们利用索引查询的时候,不可能把整个索引全部加载到内存,只能逐一加载,那么MySQL衡量查询效率的标准就是磁盘Io次数。
Hash本身是一个函数,又被称为散列函数,它可以帮助我们大幅提升检索数据的效率。
Hash算法是通过某种确定性的算法(比如MD5、SHA1、SHA2、SHA3)将输入转变为输出。相同的输入永远可以得到相同的输出,假设输入内容有微小偏差,在输出中通常会有不同的结果。
加快查找速度的数据结构,常见的有两类:
采用Hash进行检索效率非常高,基本上一次检索就可以找到数据,而B+树需要自顶向下依次查找,多次访问节点才能找到数据,中间需要多次I/O操作,以从效率来说 Hash 比 B+树更快。
Hash结构效率高,那为什么索引结构要设计成树型呢?
原因1: Hash索引仅能满足(=)(<>)和IN查询。如果进行范围查询,哈希型的索引,时间复杂度会退化为O(n);而树型的“有序"”特性,依然能够保持O(log2N)的高效率。
原因2:Hash索引还有一个缺陷,数据的存储是没有顺序的,在ORDER BY的情况下,使用Hash索引还需要对数据重新排序。
原因3∶对于联合索引的情况,Hash值是将联合索引键合并后一起来计算的,无法对单独的一个键或者几个索引键进行查询。
原因4∶对于等值查询来说,通常Hash索引的效率更高,不过也存在一种情况,就是索引列的重复值如果很多,效率就会降低。这是因为遇到Hash冲突时,需要遍历桶中的行指针来进行比较,找到查询的关键字,非常耗时。所以,Hash索引通常不会用到重复值多的列上,比如列为性别、年龄的情况等。
如果我么利用二叉树作为索引结构,那么磁盘的IO次数和索引数的高度是相关的。
1、二叉搜索树的特点
2、查找规则
我们先来看下最基础的二叉搜索树((Binary Search Tree),搜索某个节点和插入节点的规则一样,我们假设搜索插入的数值为key:
上面第二棵树也属于二分查找树,但是性能上已经退化成了一条链表,查找数据的时间复杂度变成了0(n)。你能看出来第一个树的深度是3,也就是说最多只需3次比较,就可以找到节点,而第二个树的深度是7,最多需要7次比较才能找到节点。
为了提高查询效率,就需要减少磁盘IO数。为了减少磁盘IO的次数,就需要尽量降低树的高度,需要把原来"瘦高"的树结构变的“矮胖”,树的每层的分叉越多越好。
为了解决上面二叉查找树退化成链表的问题,人们提出了平衡二叉搜索树(Balanced Binary Tree),又称为AVL树(有别于AVL算法),它在二叉搜索树的基础上增加了约束,具有以下性质:
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树
这里说一下,常见的平衡二叉树有很多种,包括了平衡二叉搜索树、红黑树、数堆、伸展树。平衡二叉搜索树是最早提出来的自平衡二叉搜索树,当我们提到平衡二叉树时一般指的就是平衡二叉搜索树。事实上,第一棵树就属于平衡二叉搜索树,搜索时间复杂度就是0( log2n)。
数据查询的时间主要依赖于磁盘I/o的次数,如果我们采用二叉树的形式,即使通过平衡二叉搜索树进行了改进,树的深度也是o(log2n),当n比较大时,深度也是比较高的,比如下图的情况:
每访问一次节点就需要进行一次磁盘工/0操作,对于上面的树来说,我们需要进行5次I/O操作。虽然平衡二叉树的效率高,但是树的深度也同样高,这就意味着磁盘I/o操作次数多,会影响整体数据查询的效率。
针对同样的数据,如果我们把二叉树改成M叉树(M>2)呢?当M=3时,同样的1个节点可以由下面的三叉树来进行存储:
你能看到此时树的高度降低了,当数据量N大的时候,以及树的分叉数M大的时候,M叉树的高度会远小于二叉树的高度(M>2)。所以,我们需要把树从“瘦高"变“矮胖”。
B树的英文是Balance Tree,也就是多路平衡查找树。简写为B-Tree(注意横杠表示这两个单词连接起来的意思,不是减号)。它的高度远远小于平衡二叉树
B树的结构如下图所示:
B树作为多路平衡查找树,它的每一个节点最多可以包括M个子节点,`M称为B树的阶|。每个磁盘块中包括了关键字和子节点的指针。如果一个磁盘块中包括了x个关键字,那么指针数就是x+1。对于一个100阶的B树来说,如果有3层的话最多可以存储约100万的索引数据。对于大量的索引数据来说,采用B树的结构是非常适合的,因为树的高度要远小于二叉树的高度。
小结:
B+树也是一种多路搜索树,基于B树做出了改进,主流的DBMS都支持B+树的索引方式,比如MySQL。相比B-Tree,B+Tree适合文件索引系统。
B+树的中间结点并不直接存储数据,B+树的查询效率更高,在查询范围上,B+树的效率比B树高。
过自动调整节点的位置来保持数的自平衡
2. 关键字集合分布在整棵树中,即叶子结点和非叶子结点都存数据。搜索有可能在非叶子节点结束。
3. 其搜索性能等价于在关键字全集中做一次二分查找。
B+树也是一种多路搜索树,基于B树做出了改进,主流的DBMS都支持B+树的索引方式,比如MySQL。相比B-Tree,B+Tree适合文件索引系统。
B+树的中间结点并不直接存储数据,B+树的查询效率更高,在查询范围上,B+树的效率比B树高。