索引底层结构介绍

这篇笔记目标在于认识如下问题

  1. 索引是是什么? 它有什么优劣?
  2. 它的底层是什么样的?为什么是这样的?
  3. 建立索引的规则?
  4. 简单了解下数据库的搜索引擎

1. 索引

1.1 索引是什么?

索引是帮助MySQL高效获取数据的排好序数据结构 ,即本质上是一种数据结构。

1.2 索引的优劣

创建索引的好处

①通过创建索引,可以在查询的过程中,提高系统的性能

②通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性

创建索引的坏处

①创建索引和维护索引要耗费时间,而且时间随着数据量的增加而增大

②索引需要占用物理空间,如果要建立聚簇索引,所需要的空间会更大

③在对表中的数据进行增加删除和修改时需要耗费较多的时间,因为索引也要动态地维护

2. 索引的底层实现

首先介绍几种数据节构,二叉树、红黑树、Hash表、B-Tree、B+Tree

B+Tree索引是B+Tree在数据库中的一种实现,是最常见也是数据库中使用最为频繁的一种索引。

B+Tree中的B代表平衡(balance),而不是二叉(binary),因为B+Tree是从最早的平衡二叉树演化而来的。

要想说明B+Tree,必须先了解二叉查找树、平衡二叉树(红黑树)和平衡多路查找树(B-Tree),B+树即由这些树逐步优化而来。

2.1 二叉树

二叉树具有以下性质:左子树的键值小于根的键值,右子树的键值大于根的键值。
在这里插入图片描述
对该二叉树的节点进行查找发现深度为1的节点的查找次数为1,深度为2的查找次数为2,深度为n的节点的查找次数为n,因此其平均查找次数为 (1+2+2+3+3+3) / 6 = 2.3次

二叉查找树可以任意地构造,同样是0,1,4,6,7,8这六个数字,也可以按照下图的方式来构造:
在这里插入图片描述
但是这棵二叉树的查询效率就很低了。因此若想二叉树的查询效率尽可能高,需要这棵二叉树是平衡的,从而引出新的定义——平衡二叉树。

2.2 红黑树索引

红黑树是一种自平衡二叉查找树,是一种数据结构,典型的用途是实现关联数组,存储有序的数据,它基本都是存储在内存中才会使用的数据结构。
 在这里插入图片描述

 RB Tree的五条基本性质

1) 每个结点要么是红的要么是黑的。  

2) 根结点是黑的。  

3) 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。  

4) 如果一个结点是红的,那么它的两个儿子都是黑的。  
  
5) 对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。

红黑树并不追求“完全平衡”——它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。

红黑树查找、插入、删除分析

(1) 查找代价:由于红黑树的性质(最长路径长度不超过最短路径长度的2倍),可以说明红黑树虽然不像AVL一样是严格平衡的,但平衡性能还是要比BST要好。其查找代价基本维持在O(logN)左右,但在最差情况下(最长路径是最短路径的2倍少1),比AVL要略逊色一点。

(2) 插入代价:RBT插入结点时,需要旋转操作和变色操作。但由于只需要保证RBT基本平衡就可以了。因此插入结点最多只需要2次旋转,这一点和AVL的插入操作一样。虽然变色操作需要O(logN),但是变色操作十分简单,代价很小。

(3) 删除代价:RBT的删除操作代价要比AVL要好的多,删除一个结点最多只需要3次旋转操作。

RBT 效率总结 : 

查找 
	效率最好情况下时间复杂度为O(logN),但在最坏情况下比AVL要差一些,但也远远好于BST

插入和删除
	这两种操作改变树的平衡性的概率要远远小于AVL(RBT不是高度平衡的)。
	因此需要的旋转操作的可能性要小,而且一旦需要旋转,插入一个结点最多只需要旋转2次,

删除
	最多只需要旋转3次(小于AVL的删除操作所需要的旋转次数)。虽然变色操作的时间复杂度在O(logN),
	但是实际上,这种操作由于简单所需要的代价很小。

红黑树索引

  • 磁盘读取数据介绍
    我们要获取磁盘上数据,必须先通过磁盘移动臂移动到数据所在的柱面,然后找到指定盘面,接着旋转盘面找到数据所在的磁道,最后对数据进行读写。因为I/O读写较为缓慢,而I/O读写又与树的深度有关,所以树的深度越深越影响性,因此选择较好的树结构减少树的高度,就变成衡量索引数据结构优良的重要标准。

  • 在大规模数据存储的时候,红黑树往往出现由于树的深度过大而造成磁盘IO读写过于频繁(磁盘读写较为缓慢),进而导致效率低下的情况。
    在这里插入图片描述
    参考:二叉查找树,红黑树,AVL树,B~/B+树(B-tree),伸展树——优缺点及比较

2.3 B-Tree

B-Tree是为磁盘等外存储设备设计的一种平衡查找树。因此在讲B-Tree之前先了解下磁盘的相关知识。

系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是需要什么取什么。

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值互不相同。

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取到内存的数据都发挥了作用,从而提高了查询效率。

弊端:无法解决范围查找的问题,每个索引带数据,影响查询效率。

2.4 B+Tree(B-Tree变种)

B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。

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

B+Tree相对于B-Tree有几点不同:

  • 非叶子节点只存储键值信息。
  • 所有叶子节点之间都有一个链指针。
  • 数据记录都存放在叶子节点中。

将上一节中的B-Tree优化,由于B+Tree的非叶子节点只存储键值信息,假设每个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构如下图所示:

在这里插入图片描述
通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。

下面做一个推算:

InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为〖10〗^3)。
也就是说一个深度为3的B+Tree索引可以维护10^3 * 10^3 * 10^3 = 10亿 条记录。

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

数据库中的B+Tree索引可以分为聚集索引(clustered index)和辅助索引(secondary index)。上面的B+Tree示例图在数据库中的实现即为聚集索引,聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的聚集索引键,即主键。当通过辅助索引来查询数据时,InnoDB存储引擎会遍历辅助索引找到主键,然后再通过主键在聚集索引中找到完整的行记录数据。

建立索引的规则?

数据库建立索引常用的规则如下:
1、表的主键、外键必须有索引;

2、数据量超过300的表应该有索引;

3、经常与其他表进行连接的表,在连接字段上应该建立索引;

4、经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;

5、索引应该建在选择性高的字段上;

6、索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;

7、复合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:
	A、正确选择复合索引中的主列字段,一般是选择性较好的字段;
	B、复合索引的几个字段是否经常同时以AND方式出现在Where子句中?单字段查询是否极少甚至没有?
		如果是,则可以建立复合索引;否则考虑单字段索引;
	C、如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;
	E、如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引;

8、频繁进行数据操作的表,不要建立太多的索引;

9、删除无用的索引,避免对执行计划造成负面影响

MySQL的两种存储搜索引擎

1)MyISAM存储搜索引擎:索引文件和数据文件是分离的(非聚集)在这里插入图片描述
例子:

select * from test
where Col1 = 49

执行流程:
	1)根据B+Tree查找索引49
	2)找到后获得对应数据所在的位置的指针
	3)获取数据并返回

在这里插入图片描述
采用MyISAM引擎会创建test_myisam表会生成三个文件
MYI 文件存储索引
frm 存储表结构
MYD 存储表数据
2)InnoDB索引实现(聚集):表数据文件本身就是按B + Tree组织的一个索引结构文件
在这里插入图片描述在这里插入图片描述
frm 存储表结构
ibd 索引+数据

聚集索引(节点包含了完整的数据记录,即索引和数据存储在一个文件中)

为什么InnoDB表必须有主键,并且推荐使用整型的自增主键?
1)InnoDB搜索引擎是按照B+Tree来搜索管理数据,索引必须要有一个索引,如果建表时没有创建索引,数据库会默认创建一个隐藏列来充当索引
2)因为B+Tree底层节点包含多个索引,且有序排列,采用整型便于比较大小,自增是为了扩展底层索引时少引起B+Tree进行较大结构的改动(树的分裂)

一些问题

1.)索引是怎么支持千万级别的快速搜索?

  • 底层采用了B+Tree的数据结构。

2)索引具体采用的哪种数据结构呢?

  • 常见的MySQL主要有两种结构:Hash索引和B+ Tree索引InnoDB引擎,默认的是B+树。

3)既然InnoDB使用的B+ Tree的索引模型,那么为什么采用B+ 树?这和Hash索引比较起来有什么优缺点?

  • 因为Hash索引底层是哈希表,哈希表是一种以key-value存储数据的结构,所以多个数据在存储关系上是完全没有任何顺序关系的,所以,对于区间查询是无法直接通过索引查询的,就需要全表扫描。所以,哈希索引只适用于等值查询的场景。

  • 哈希索引适合等值查询,但是无法进行范围查询 哈希索引没办法利用索引完成排序 哈希索引不支持多列联合索引的最左匹配规则 如果有大量重复键值得情况下,哈希索引的效率会很低,因为存在哈希碰撞问题

  • B+ Tree是一种多路平衡查询树,所以他的节点是天然有序的(左子节点小于父节点、父节点小于右子节点),所以对于范围查询的时候不需要做全表扫描。

4)B+ Tree的叶子节点都可以存哪些东西?

  • InnoDB的B+ Tree可能存储的是整行数据,也有可能是主键的值。

5)这两者有什么区别?

  • 在 InnoDB 里,索引B+ Tree的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。
  • 索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引。

6)聚簇索引和非聚簇索引,在查询数据的时候有区别吗?

  • 聚簇索引查询会更快,聚簇索引的叶子节点直接就是我们要查询的整行数据了。而非簇索引的叶子节点是主键的值,查到主键的值以后,还需要再通过主键的值再进行一次查询(回表)。

7)在创建索引的时候都会考虑哪些因素呢?

  • 数据量超过300的表应该有索引;
  • 经常与其他表进行连接的表,在连接字段上应该建立索引;
  • 经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;
  • 经常出现数据变动的表,不宜建立索引;

8)创建联合索引的时候,需要做联合索引多个字段之间顺序你们是如何选择的呢?

  • 把识别度最高的字段放到最前面
  • 原因:最左匹配原则
    • 在创建多列索引时,我们根据业务需求,where子句中使用最频繁的一列放在最左边
    • 因为MySQL索引查询会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。
    • 所以当我们创建一个联合索引的时候,如(key1,key2,key3),相当于创建了(key1)、(key1,key2)和(key1,key2,key3)三个索引,这就是最左匹配原则。

如有错误,欢迎指正,手动笑脸。

参考:我以为我对Mysql索引很了解,直到我遇到了阿里的面试官
参考:BTree和B+Tree详解
参考:索引 ----- 二叉树、平衡二叉树、b-tree、b+tree详解

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值