为何使用B+树作为索引的数据结构

为何使用B+树作为索引的数据结构

1)二叉树与B/B+树具有相同的查找时间复杂度O(logn),所以时间复杂度不是选择B树的原因
2)二叉树未能很好的应用磁盘的预读功能(每次磁盘预读的数据都是无用的),会造成较多的磁盘I/O操作;B /B+树都很好的运用了磁盘的预读功能,磁盘页的大小与节点的大小一致(InnoDB为16kb),减少了磁盘的I/O操作。

3)其中,对于B+树,又通过指针将叶子结点链接了起来,对于范围查找十分有效
4)B+ /B 树节点的个数也较二叉树有所增多,可以减少节点的查找次数

二叉树:

二叉搜索树的特点:每个节点的左儿子小于父节点,父节点又小于右儿子。时间复杂度是O(logn)。
多叉搜索树的特点:每个节点有多个儿子,儿子之间的大小保证从左到右递增。

二叉树搜索效率高,但是实际上大多数的数据库存储却并不使用O(n),其原因是

  1. 索引不止存在内存中,还要写到磁盘上。树存储在磁盘中的,访问每个节点,都对应一次磁盘I/O(假设每个节点大小< 操作系统的最小读写单位块大小),即树的高度=每次查询时磁盘I/O操作次数,树越高越影响性能,而数据越多,树高不断增长,磁盘I/O越多,性能越差。

    一棵100万节点的平衡二叉树,树高20。一次查询可能需要访问20个数据块。在机械硬盘时代,从磁盘随机读一个数据块需要10 ms左右的寻址时间。对于一个100万行的表,单独访问一行可能需要 20×10 ms的时间。
    为了让一个查询尽量少地读磁盘,就必须让查询过程访问尽量少的数据块。即使用多叉树,减少树高。

  2. 二叉查找树存在一个极端的情况:每次插入都是二叉查找树中最大的元素,二叉查找树就会退化成一条链表,查找数据的时间复杂度变为O(n)

自平衡二叉树:

在二叉查找树基础上增加了约束:每个节点的左子树和右子树的高度差不能超过1。即增加元素时它会维持自平衡,节点的左子树和右子树仍然为平衡二叉树,查询的时间复杂度维持在O(logn)。还有很多自平衡二叉树,比如红黑树,它的操作就比较复杂了。

无论平衡二叉树还是红黑树,每个节点只能有2个子节点,随着元素的增加,树高都会随之增加,意味着磁盘I/O操作次数随之变多,查询效率变低。

BTree:

节点:指树中的一个元素(图中的一个框)

节点的度:节点拥有的子树的个数,二叉树的度不大于2

叶子节点:度为0的节点,也称之为终端结点

高度:叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高

BTree又叫自平衡多叉查找树,也称B-树。允许M个子节点(M>2),从而降低树高。

B树的每个节点最多可以包括M个子节点,M称为B数的阶,所以B树是个多叉树。

例如:

一个3阶B树,每个节点最多2个数据(M-1个,方便比较大小,以此找到对应子节点),和最多3个子节点(M个),超过就会分裂节点。

  1. 每个节点的组成:
    1)索1) 索引:索引值(如id)
    2)数据:索引值对应的data
    3)指针:该层没有指定索引,通过指针到下一层寻找

  2. 每个节点的大小:索引大小(如5B) + 数据大小(如95B)

  3. 每个节点的大小设置为一个页的大小,这样每个节点只需要一次IO就可以完全载入。

  4. 度比较大,每个节点可以存储多个索引值和数据,度越大,树的高度越低,磁盘IO的次数就越少
    度 = 页的大小/(索引大小+数据大小)

  5. 叶子节点具有相同的深度,叶节点的指针为空,查询效率比较稳定

BTree和二叉树相比,查询数据的效率更高,因为对于相同的数据量来说,层级结构小,因此搜索速度快。

B+Tree

B+ 树在B树的基础上做了升级,MySQL索引的数据结构就采用的B+ 树。

结构差异

  1. 叶子节点才会存放实际数据(索引+记录),非叶子节点只存放索引。

  2. 所有索引都会在叶子节点中出现,叶子节点间构成一个有序的链表。

  3. 非叶子节点的索引会同时存在于子节点中,且是子节点中最大或最小的索引。

  4. 非叶子节点有多少个子节点,其中就有多少个索引。

由于 B+Tree只有叶子节点保存data,因此查询任何key都要走到叶子节点。

性能区别

  1. 单点查询

B树所有节点都可以映射数据,B+ 树只有叶子节点可以映射数据。B树查询最快O(1),平均时间比B+稍快,但因为非叶子节点即存数据又存索引,查询可到达叶子节点和飞叶子节点,波动大。相比于B树,B+树非叶子节点仅存放索引,可以存储更多的索引,因此B+树可以更矮胖,查询底层节点的磁盘I/O操作次数更少。

  1. 插入、删除效率

B+ 树有大量的冗余节点(所有非叶子节点都是冗余索引),比如删除一个节点的时候,可以直接从叶子节点中删除,甚至可以不动非叶子节点。这样删除非常快。B 树则不同,B 树没有冗余节点,删除节点的时候非常复杂。比如删除根节点中的数据,可能涉及复杂的树的变形。

B+ 树的插入也是一样,有冗余节点,插入可能存在节点的拆分(如果节点饱和),但是最多只涉及树的一条路径。而且 B+ 树会自动平衡,不需要更多复杂的算法,类似红黑树的旋转操作等。

因此,B+ 树的插入和删除效率更高。

  1. 范围查询

B+ 树所有叶子节点间有一个链表进行连接,这种设计对范围查找非常有帮助。而B树没有,只能通过树遍历来完成范围查找,涉及多个节点的磁盘I/O操作。范围查询效率不如B+树。

InnoDB中的B+Tree

  • B+树的叶子节点间以双向链表连接,即能向左也能向右遍历。

  • 节点内容是数据页,数据页中存放用户记录以及各种信息,每个数据页默认大小16KB。

主键索引也被称为聚簇索引(clustered index),也叫作聚集索引。其余都称呼为非主键索引也被称为二级索引(secondary index),也叫作辅助索引。

非叶子节点:存储了索引和子树节点的指针

叶子节点:叶子节点之间通过指针相互连接,存储了索引、数据、链表指针和指向记录的指针

索引分类:

按存储方式区分:B树索引,哈希索引

按逻辑区分:1.普通索引 2.少数索引 3.主键索引 4. 空间索引 5.全文索引

按实际使用区分:单列索引,多列索引

索引中保存了数据列的值和指向它们所在行的指针,所以在回表查询时,能快速定位到表中的特定行,从而提高查询效率。

为什么索引建立太多会导致写入和删除的效率下降?

  1. 索引过多会增加写入操作的开销

    进行写操作时,数据库需要维护索引的更新,索引过多会导致维护开销变得非常大,使得写入操作变慢。

  2. 索引过多会占用大量磁盘空间

    在创建索引时,数据库会为每个索引分配磁盘空间,就会占用大量的磁盘空间,导致磁盘空间不足,影响数据库的正常运行。

  3. 索引过多会降低查询效率

    虽然索引能够提高查询效率,但查询时需要扫描所有的索引,而索引过多会导致扫描的时间变长,从而降低查询效率。

  4. 索引过多会导致缓存失效

    数据库会将经常使用的数据缓存到内存中,以加快查询速度。但缓存的空间有限,索引过多就会挤占数据缓存的空间,导致数据缓存失效,从而降低查询效率。

  5. 索引过多会导致锁竞争

    在进行查询操作时,数据库会对相关的数据进行锁定,以保证数据的一致性。但是索引过多会导致锁竞争,因为每个索引都需要进行锁定,从而导致锁的竞争变得激烈,影响数据库的性能。

  6. 索引过多并且设置成联合索引会造成数据重复的交换

    例如:当同一张表中ID字段为唯一字段,card和name为不唯一,同时将ID、card和name设置为联合主键,当card和name相同,而之前推送过数据下次推送就会因为联合索引的查询不同,造成数据的重复推送!

综上所述,创建索引时需要谨慎考虑,避免过多的索引导致数据库性能下降。