Mysql进阶之路(二)----Mysql底层索引原理

思考:

1.什么是数据库索引?

数据库查询是数据库的主要功能之一,最基本的查询算法是顺序查找(linear search)时间复杂度为O(n),显然在数据量很大时效率很低。优化的查找算法如二分查找(binary search)、二叉树查找(binary tree search)等,虽然查找效率提高了。但是各自对检索的数据都有要求:二分查找要求被检索数据有序,而二叉树查找只能应用于二叉查找树上,但是数据本身的组织结构不可能完全满足各种数据结构(例如,理论上不可能同时将两列都按顺序进行组织)。所以,在数据之外,数据库系统还维护着满足特定查找算法的数据结构。这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构就是索引。

2.索引的优点

第一,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
第二,可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
第三,可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
第四,在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
第五,通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

3.索引的缺陷?

一:创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。

二:索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。

三:当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。

4.不应该创建索引的的列具有特点

一:对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。

二:对于那些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。

三:对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。

四:当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该创建索引。

5. 数据库中的索引类型:普通索引、聚集索引、非聚集索引

普通索引 。。。。。

聚集索引 在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引。如果某索引不是聚集索引,则表中行的物理顺序与键值的逻辑顺序不匹配。与非聚集索引相比,聚集索引通常提供更快的数据访问速度。

非聚簇索引 叶子节点上的data是主键(即聚簇索引的主键),而不是记录所在地址,因为记录所在地址并不能保证一定不会变,但主键可以保证。定位流程第一步,由普通索引找到PK,第二步,由PK找到行记录。

索引数据结构的选择 (二叉树,红黑树,Hash,B+树详解)

引言: 当我们创建索引的时候,我们需要选择索引方法(默认B+树)

在这里插入图片描述

思考:上文说过,二叉树、红黑树等数据结构都可以用来实现索引,但是那为什么MySQL只为我们提供了BTree和HASH这两种方法?当我们创建索引时该选择哪种索引方法?(重点)

分析开始

我们都知道评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。

对树索引结构选择的分析

1.为什么不用二叉树?

eg: 当数据库插入的数据有序(2,3,5,6,7,8)时,把2作为头结点时,就变成了斜树,这样在查询的时候就相当于链表一样,做一次全表扫描(相当于索引失效)。
在这里插入图片描述

2. 我们都知道红黑树可以自动平衡,这样就解决了斜树的问题。但是为什么不用红黑树呢?

当我们插入大量数据的时候,会导致的树的高度过高,I/O渐进复杂度为O(h)

eg(1~14)
如果要查找14,就需要查询5次,所以还是没有有效的减少查找过程中磁盘I/O的存取次数

在这里插入图片描述

3.为什么使用B树

B-树是一种多路自平衡的搜索树 它类似普通的平衡二叉树,不同的一点是B-树允许每个节点有更多的子节点。B-Tree 相对于 AVLTree 缩减了节点个数,使每次磁盘 I/O 取到内存的数据都发挥了作用,从而提高了查询效率。I/O渐进复杂度为O(h)

eg(1~14)
如果要查找14,就需要查询2次,虽然和红黑树的I/O渐进复杂度一样,但是红黑树结构,h明显要深得多。
在这里插入图片描述

深度理解:

InnoDB存储引擎中有页(Page)的概念,页是其磁盘管理的最小单位。InnoDB存储引擎中默认每个页的大小为16KB,可通过参数innodb_page_size将页的大小设置为4K、8K、16K,在MySQL中可通过如下命令查看页的大小:

 mysql> show variables like 'innodb_page_size';

而系统一个磁盘块的存储空间往往没有这么大,因此InnoDB每次申请磁盘空间时都会是若干地址连续磁盘块来达到页的大小16KB。InnoDB在把磁盘数据读入到磁盘时会以页为基本单位,在查询数据时如果一个页中的每条数据都能有助于定位数据记录的位置,这将会减少磁盘I/O次数,提高查询效率。

B-Tree结构的数据可以让系统高效的找到数据所在的磁盘块。为了描述B-Tree,首先定义一条记录为一个二元组[key, data] ,key为记录的键值,对应表中的主键值,data为一行记录中除主键外的数据。对于不同的记录,key值互不相同。

一棵m阶的B-Tree有如下特性:

  1. 每个节点最多有m个孩子。
  2. 除了根节点和叶子节点外,其它每个节点至少有Ceil(m/2)个孩子。
  3. 若根节点不是叶子节点,则至少有2个孩子
  4. 所有叶子节点都在同一层,且不包含其它关键字信息
  5. 每个非终端节点包含n个关键字信息(P0,P1,…Pn, k1,…kn)
  6. 关键字的个数n满足:ceil(m/2)-1 <= n <= m-1
  7. ki(i=1,…n)为关键字,且关键字升序排序。
  8. Pi(i=1,…n)为指向子树根节点的指针。P(i-1)指向的子树的所有节点关键字均小于ki,但都大于k(i-1)

B-Tree中的每个节点根据实际情况可以包含大量的关键字信息和分支,如下图所示为一个3阶的B-Tree:
在这里插入图片描述
每个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为17和35,P1指针指向的子树的数据范围为小于17,P2指针指向的子树的数据范围为17~35,P3指针指向的子树的数据范围为大于35。

模拟查找关键字29的过程:

根据根节点找到磁盘块1,读入内存。【磁盘I/O操作第1次】
比较关键字29在区间(17,35),找到磁盘块1的指针P2。
根据P2指针找到磁盘块3,读入内存。【磁盘I/O操作第2次】
比较关键字29在区间(26,30),找到磁盘块3的指针P2。
根据P2指针找到磁盘块8,读入内存。【磁盘I/O操作第3次】
在磁盘块8中的关键字列表中找到关键字29。

分析上面过程,发现需要3次磁盘I/O操作,和3次内存查找操作。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率。而3次磁盘I/O操作是影响整个B-Tree查找效率的决定因素。B-Tree相对于AVLTree缩减了节点个数,使每次磁盘I/O取到内存的数据都发挥了作用,从而提高了查询效率。

4.再度优化 【 B-Tree —》升级 —》B+Tree 】

B+树索引是B+树在数据库中的一种实现,是最常见也是数据库中使用最为频繁的一种索引。B+树中的B代表平衡(balance),而不是二叉(binary)。B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构。

从上一节中的B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。

数据库B+Tree对于B-Tree优化思路有:

1. 非叶子节点data,只存储索引,可以放更多的索引。
2. 所有叶子节点之间都有一个链指针(顺序访问指针,可以提高访问的性能)。
3. 数据记录都存放在叶子节点中。

eg(1-14):
在这里插入图片描述

深度理解:

将上一节中的B-Tree优化,由于B+Tree的非叶子节点只存储键值信息,假设每个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构如下图所示:
在这里插入图片描述
通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。

可能上面例子中只有22条数据记录,看不出B+Tree的优点,下面做一个推算:

InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节)-----这里使用BIGINT来计算,指针类型也一般为6个字节,也就是说根节点一个页中大概存储16KB/(8B+6B)=1170个键值,而叶子节点的一个页(因为InnoDB存储引擎叶子节点还要存储数据)大概存储16KB/1KB(假设为1kb --索引+数据)= 16。也就是说一个深度为3的B+Tree索引可以维护1170 * 1170 * 16 = 2000多万条记录。所以当Mysql数据个数为2000多万(反正数据很大时)查询性能会急剧下降。
在这里插入图片描述

实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在2 ~ 4层。MySQL的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要1~3次磁盘I/O操作。

对Hash索引方式的分析

原理: 哈希索引用索引列的值计算该值的hashCode(hash方法),然后在hashCode相应的位置存执该值所在行数据的物理位置(指针),然后去哈希表中去找。

哈希表:存储着对应关系,
槽编号是循序的,值数据行不是
槽(Slot) 值(Value)。

优点:因为使用散列算法,因此访问速度非常快。

哈希冲突:不同的值得到了相同的哈希码,例如f(‘tao’)=2323 f(‘wang’)=2323,此时就是出现了哈希冲突
当出现哈希冲突时,相同的数据会存储在链表中,遍历链表找到符合的。

特点:
1)哈希索引只包含哈希码和指针,不存储数据字段值
2)哈希索引数据并不是按循序存储的,因此无法用于排序
3)因为要通过查询值计算确定的哈希码,所以哈希索引不支持部分匹配,不支持范围查找,只支持等值比较查询
4)当哈希冲突很多的时候,效率会降低

使用场景分析

特点 2 、3也是Mysql中为什么默认的索引方法不是Hash方法的原因。当然如果是简单的精确查询,就可以使用Hash方法了,查询效率会比BTree索引有很大的提升。

发布了45 篇原创文章 · 获赞 3 · 访问量 2336

猜你喜欢

转载自blog.csdn.net/weixin_44046437/article/details/98508857