数据结构与算法-13高级数据结构_树论(BtreeB+Tree)

news2025/2/23 20:19:48

Btree&B+Tree

1 btree-定义

在这里插入图片描述

B-树是一种自平衡的树形数据结构,它能够保持数据的有序性,并允许搜索、顺序访问、插入和删除操作都在对数时间内完成。与二叉树不同,B-树的每个节点可以拥有多于两个的子节点,这取决于树的阶(order)或度数(degree)。阶为M的B-树意味着:

  • 每个节点最多有M个子节点。
  • 每个非根节点至少有⌈M/2⌉个子节点(向上取整),根节点至少有两个子节点(除非它是叶子节点)。
  • 每个节点中的关键字数量比其子节点数量少一。即,如果节点有N个子节点,那么它将包含N-1个关键字。
  • 所有叶子节点都位于同一层,并且不包含任何子节点或关键字之间的指针,但它们可能包含指向实际数据记录的指针。
  • 关键字在节点内以升序排列,并且每个子树中的所有关键字都小于其父节点中分隔该子树的关键字,而大于分隔其左侧子树的关键字(如果存在的话)。

2 btree特性

  1. 多路搜索:B-树通过允许多个分支(子节点)来减少树的高度,这使得搜索过程更加高效。在每次搜索中,B-树可以排除掉更多的分支,从而更快地定位到目标关键字。
  2. 自平衡性:B-树在插入和删除操作时通过分裂和合并节点来保持树的平衡。这种平衡调整确保了树的高度始终保持在一个合理的范围内,从而保证了操作的效率。
  3. 磁盘友好:由于B-树的节点可以包含多个关键字和子节点,因此每个节点可以映射到磁盘上的一个单独块(block)。这使得B-树特别适合用于存储在磁盘上的大量数据,因为它可以显著减少磁盘I/O操作的次数。
  4. 范围查询:B-树的节点按关键字顺序排列,这使得范围查询变得非常高效。通过简单地遍历叶子节点或内部节点,B-树可以快速地返回指定范围内的所有关键字。
  5. 支持大量数据:由于B-树的高度较低且每个节点可以包含大量关键字,因此它能够高效地处理大量数据。这使得B-树成为数据库索引、文件系统和操作系统中常用的数据结构。
  6. 动态数据结构:B-树是动态数据结构,可以随着数据的插入和删除而增长和缩小。通过适当的分裂和合并操作,B-树可以保持其平衡性,同时保持其高效性。
  7. 支持重复键值:虽然B-树通常用于存储唯一的键值,但它也可以轻松地适应包含重复键值的情况。在这种情况下,可以在节点中存储相同关键字的多个实例,或者在叶子节点中使用额外的结构(如链表)来存储具有相同关键字的记录。

3 b+tree-定义

在这里插入图片描述

B+树是一种n叉排序树,每个节点通常有多个孩子。一棵B+树包含根节点、内部节点和叶子节点。B+树是B树的一种变形形式,但与B树相比,它在结构上有所优化。阶为M的B+树意味着:

(1)每个节点最多有m个子节点

(2)除根节点外,每个节点至少有m/2个子节点,注意如果结果除不尽,就向上取整,比如5/2=3。 3/2=ceil(1.5)=2

(3)根节点要么是空,要么是独根,否则至少有2个子节点

(4)有k个子节点的节点必有k个关键码:就是 有m个数据就有m个叉叉;

(5)叶节点的高度一致:这个的 好处是什么?

  • 所有叶节点具有相同的高度。这一特性带来的好处包括:
    • 简化查询操作:由于所有叶节点位于同一层,查询操作在到达叶节点之前所经过的路径长度是固定的,这有助于简化查询算法的实现。
    • 优化范围查询:由于叶节点之间通过指针相连,且所有叶节点位于同一层,因此可以非常高效地执行范围查询操作,只需遍历叶节点链表即可。
    • 提高缓存效率:由于树的高度较低且叶节点位于同一层,因此可以更有效地利用缓存来存储树的结构和数据,从而提高数据访问速度。

4 b+tree特性

  1. 所有值都在叶子节点,且叶子节点相连
    • B+树的所有数据记录都存储在叶子节点上,而非叶子节点仅作为索引存在,不存储实际的数据记录。这种设计使得非叶子节点能够容纳更多的关键字,从而减少了树的高度,提高了查找效率。
    • 叶子节点之间通过指针相连,形成了一个有序链表。这种结构使得B+树在进行范围查询和顺序访问时非常高效,因为可以直接在叶子节点之间移动,而无需回溯到上层节点。
  2. 内部节点仅包含索引信息
    • B+树的内部节点(非叶子节点)仅包含其子节点中的最大(或最小)关键字作为索引,这些索引用于指导查找过程。由于内部节点不包含数据记录,因此它们可以更加紧凑地存储索引信息,进一步减少树的高度。
  3. 节点分裂与合并保持树的平衡
    • 当叶子节点中的关键字数量超过一定限制时,该节点会进行分裂操作,将部分关键字和子节点指针提升到父节点中,并创建一个新的叶子节点来存储剩余的关键字和子节点指针。这样可以保持树的高度稳定,避免因为单个节点过大而导致的性能下降。
    • 类似地,当因为删除操作导致叶子节点中的关键字数量过少时,可能会与相邻的节点进行合并操作,以维持树的平衡。
  4. 自底向上的插入和删除操作
    • 在B+树中插入新元素时,通常是从叶子节点开始进行的。首先找到应该插入新元素的叶子节点,并在其中插入新元素。如果插入后叶子节点的关键字数量超过了限制,则会进行分裂操作,并可能触发上层节点的调整。
    • 删除操作也是类似的,首先找到包含要删除关键字的叶子节点,并从中删除该关键字。如果删除后叶子节点的关键字数量过少,则可能会与相邻的节点进行合并操作。
  5. 稳定的对数时间复杂度
    • 由于B+树的高度较低且保持平衡,因此其插入、删除和查找操作都具有稳定的对数时间复杂度。这意味着无论树的大小如何变化,这些操作的时间开销都保持在较低的水平。
  6. 磁盘I/O操作的优化
    • B+树的设计特别考虑了磁盘I/O操作的优化。由于非叶子节点不存储数据记录且节点分裂和合并操作较为频繁地发生在叶子节点之间,因此可以更加有效地利用磁盘的块(block)大小来减少I/O操作的次数。
    • 此外,由于叶子节点之间通过指针相连且有序排列,因此可以更加高效地进行顺序访问和范围查询操作。

5 b+tree&数据库

5.1 B+Tree的阶数与数据库

阶数的重要性

B+Tree的阶数(即节点中可存储的键值对数)对数据库的查找效率和性能有着至关重要的影响。阶数决定了每个节点能够存储的键值对数量,进而影响到树的深度和查询时需要遍历的节点数。

阶数的设定

  • 影响要素
    • 页大小:MySQL中B+Tree的阶数主要由页大小决定。MySQL的InnoDB存储引擎默认页大小为16KB。即b+tree一个节点可以存储的数据未16KB
    • 数据类型:索引字段的数据类型也会影响每个节点能存储的键值对数量。例如,bigint类型的主键字段占用8字节,加上指针(通常也占用一定空间,如4字节),共同决定了每个节点能存储的键值对数量。
  • 计算示例
    • 单个键值对占用的空间:假设主键为bigint类型(8字节),加上指针(4字节),则每个键值对占用8B + 4B = 12B
    • 一页能存储的键值对数量16KB / 12B ≈ 1333(实际计算中可能因页内其他开销而略有不同,此处为简化计算)。但考虑到对齐和页内其他信息(如页头、页尾等),实际可存储的键值对数量会小于这个值。为了简化说明,我们可以假设为1024(即1K)。
    • 计算一下高度为3,阶数为4的b+tree可以存放多少个索引?1024 * 1024 * 1024

总结

​ 在MySQL中,B+Tree的阶数由页大小数据类型共同决定。通过合理设定页大小和选择合适的数据类型,可以优化B+Tree的阶数,进而提高数据库的查询效率和性能。尽管实际中阶数可能受到多种因素的影响而难以达到理论最大值,但MySQL的InnoDB存储引擎通过其高效的索引结构和算法,确保了即使在大量数据的情况下也能保持较高的查询效率。

5.2 从b+tree看复合索引

什么是复合索引?

在MySQL中,复合索引(也称为联合索引)是由两个或更多列组成的索引。这种索引结构对于优化涉及多个列的查询非常有效,因为它能够同时利用多个列的值来快速定位数据。

复合索引的组成原理

  • B+树是一种自平衡的树数据结构,它保持数据排序,允许搜索、顺序访问、插入和删除操作都在对数时间内完成。在复合索引中,B+树的每个节点都会包含索引列的值,这些值按照索引列在索引定义中的顺序进行排序。
  • 索引列的排序:复合索引中的列按照索引创建时指定的顺序进行排序。这意味着,B+树中的每个节点都会首先根据第一列的值进行排序,如果第一列的值相同,则根据第二列的值进行排序,以此类推。
  • 最左前缀原则:复合索引的一个重要特性是遵循最左前缀原则。这意味着,如果查询条件中包含了复合索引的最左边的列(或这些列的前缀),那么MySQL就可以利用这个复合索引来加速查询。然而,如果查询条件跳过了索引的最左列,那么索引可能不会被使用,除非查询优化器能够找到其他利用索引的方式。
  • 索引的覆盖:如果复合索引包含了查询需要的所有列,那么查询就可以直接从索引中检索数据,而无需回表查询数据行。这种查询方式称为索引覆盖扫描,可以显著提高查询效率。

在这里插入图片描述

复合索引的失效

  1. 查询条件不符合最左前缀原则:如前所述,如果查询条件没有包含复合索引的最左列(或这些列的前缀),那么索引可能不会被使用。
  2. 索引列参与了计算或函数操作:如果在查询条件中对索引列进行了计算或使用了函数,那么MySQL可能无法有效利用索引。例如,如果索引列是age,而查询条件是YEAR(CURDATE()) - age > 18,那么索引可能不会被使用。
  3. 数据类型不匹配:如果查询条件中的数据类型与索引列的数据类型不匹配,那么MySQL可能无法将查询条件与索引列进行有效比较,从而导致索引失效。
  4. 索引列的数据分布不均匀:如果复合索引中的某个列的数据分布非常不均匀,那么该索引的效果可能会大打折扣。因为索引的主要目的是减少需要扫描的数据量,如果某个列的值非常集中,那么索引可能无法有效过滤掉大部分数据。
  5. 查询优化器的选择:MySQL的查询优化器会基于成本估算来选择执行计划。在某些情况下,即使存在复合索引,优化器也可能认为使用全表扫描或其他索引更为高效。这可能是由于统计信息不准确、索引选择性差或其他查询条件的影响。
  6. 多表连接查询:在涉及多个表的连接查询中,复合索引的使用可能会受到限制。如果连接条件没有直接利用到复合索引中的列,或者连接操作导致索引失效(如使用了笛卡尔积连接),那么复合索引可能无法被有效利用。
5.3 索引建立的基本原则
  1. 索引数量控制

    • 避免过多索引:因为B+Tree的插入和删除操作需要维护索引,过多的索引会显著降低数据插入速度,增加数据库的维护成本。
  2. 避免使用LIKE ‘%%’

    • LIKE '%%'的索引失效:当在查询条件中使用LIKE '%%'时,索引无法被有效利用,因为该模式匹配会扫描全表。
  3. 字段类型与大小

    • 字段类型与索引效率:索引字段类型不宜过大,字段越小,B+Tree的阶数(即节点内键值对的数量)越大,查询效率越高。例如,intbigintvarchar(10)varchar(100)textlongtext等类型,应根据实际数据量和查询需求选择。

    • 全文索引:对于大文本字段,考虑使用全文索引来提高搜索效率。

  4. 字段值的离散性

    • 避免低离散度字段索引:对于离散度很低的字段(如性别),建立索引可能效果不佳,因为索引中大量键值相同,无法有效过滤数据。
  5. 联合索引的最左匹配原则

    • 最左前缀原则:MySQL在解析查询时,会利用联合索引的最左前缀进行匹配。例如,若对(id, name, age)建立索引,则查询SELECT * FROM user WHERE name = '赵云' AND id = 1会利用索引,但查询SELECT * FROM user WHERE name = '赵云' AND age = 10虽然也使用了索引的一部分,但效率可能不如完全匹配。

    • 自动优化:MySQL查询优化器会尝试自动优化查询,以利用最合适的索引。

  6. NOT IN的使用与限制

    • NOT IN与索引NOT IN子句在包含大量值时可能不会走索引,因为优化器可能认为全表扫描更高效。

    • 值数量限制:当IN子句中的值过多时,MySQL可能会报错或性能下降,需要根据实际情况调整查询策略或优化数据库配置。

6 btree与b+tree的比较

B+树是B树的一种变型,它们在结构上存在一些差异。相比B树,B+树更适合实际应用中的数据库索引和文件索引,主要原因包括:

6.1 数据存储结构
  • BTree
    • 数据项(或记录的一部分)既可以存储在非叶子节点,也可以存储在叶子节点中。
    • 这种结构使得BTree在查询时可能在非叶子节点就找到所需的数据项,但在某些情况下也可能需要遍历到叶子节点。
  • B+Tree
    • 所有数据项(或记录)都存储在叶子节点中,形成一个有序链表,非叶子节点仅作为索引存在,不存储实际的数据记录。
    • 这种设计确保了所有查找操作最终都会落在叶子节点上。同时,非叶子节点不存储数据,减少了磁盘I/O次数。
    • 查询效率稳定:在B+树中,任何关键字的查询都必须从根节点走到叶子节点,查询路径长度相同,这保证了查询效率的稳定性。
6.2 叶子节点的连接性
  • BTree:叶子节点之间通常没有直接的连接,它们是相互独立的。
  • B+Tree:所有叶子节点通过指针相互连接,形成一个双向链表。这种设计优化了范围查询的性能,因为可以顺序遍历叶子节点链表而无需回溯。
6.3 磁盘I/O效率
  • BTree:由于数据项可能存储在非叶子节点,BTree在某些情况下可以减少磁盘I/O次数。但随着树深度的增加,特别是在处理大量数据时,I/O成本可能会显著增加。
  • B+Tree:由于所有数据都存储在叶子节点,并且叶子节点之间形成了有序链表,B+Tree通常具有更低的树高,从而减少了磁盘I/O次数。这使得B+Tree在处理大量数据时具有更高的效率。
6.4 节点分裂与合并
  • 两者相似:在节点分裂和合并方面,BTree和B+Tree都遵循类似的规则来保持树的平衡。当节点中的数据项数量超过或低于某个阈值时,会进行相应的分裂或合并操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1954463.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

* 快速排序的深入优化探讨

在算法设计领域,快速排序因其卓越的平均性能与广泛的应用场景而备受推崇。自1960年Tony Hoare提出以来,它已成为许多编程语言标准库中的核心排序方法。然而,随着数据规模的不断扩大和计算需求的日益复杂化,对快速排序进行更深入的…

docker环境下的verdaccio设置权限并配置域名.md

权限配置 一个管理员叫admin,可以读也可以发布一个普通用户叫qiuye,只可以读,不可以发布添加账号就自行创建添加即可,只需要更改config文件的配置项即可 packages:*/*: access: admin qiuyepublish: admin unpublish: admin **:access: admin qiuyepublish: admin unpublish…

数据结构系列-插入排序和希尔排序

🌈个人主页:羽晨同学 💫个人格言:“成为自己未来的主人~” 排序的概念 常见的排序算法: 插入排序 直接插入排序是一种简单的插入排序法,其基本思想是: 把待排序的记录按其关键码值的大小逐个插入到…

如何合规与安全地利用专业爬虫工具,构建企业数据竞争优势

摘要: 本文深入探讨了在当今大数据时代,企业如何通过合规且安全的方式运用专业爬虫工具,有效收集并分析海量信息,进而转化为企业独有的数据优势。我们不仅会介绍最佳实践,还会讨论关键技术和策略,帮助企业…

virtuoso tran仿真中如何画出temperature的瞬态曲线

virtuoso tran仿真中如何画出温度的瞬态曲线? 在tran仿真中如果加入了瞬态热效应,设置Dynamic parameter,即时间随温度变化,如何plot temp vs. time曲线? 1.电路中加一根线和变量名相同 2.ADE L/XL Test Editor->Ou…

如何在安卓设备上运行Linux(使用termux+图形界面)加上换源等优化

我学生嘛,喜欢讲故事,你看看我大部分文章开头,都会有"事情的起因"一类话 当然这次也不例外哦 我最新获得了一个新平板,华为的matepad air,很喜欢。想捣鼓,不太懂,但好像鸿蒙不能直接…

MyBatis中的赋值语句:#{}和${}的区别差异(常见面试题)

我们开始先总结他们的差异,后面再使用代码展示差异 1.0.#{}和${}的差异 (1)${}可能存在sql注入的安全问题 (2)${}是即时sql(参数直接拼接),不能进行缓存;#{}是预编译sq…

基于单文档的MFC图像增强

目录 function.h ColorEnhanceDib.h ColorEnhanceDib.cpp Dib.h Dib.cpp FrequencyFilterDib.h FrequencyFilterDib.cpp GrayTransformDib.h GrayTransformDib.cpp HistogramDib.h HistogramDib.cpp SharpenProcessDib.h SharpenProcessDib.cpp SmoothProcessDib.h Sm…

【Spring Boot】自动配置源码解析

目录 Spring-Boot-Starter一、准备配置类和 Bean 对象二、自动配置条件依赖三、Bean 的参数获取3.1 EnableConfigurationProperties 注解3.2 ConfigurationProperties 注解 四. Bean 的发现4.1 自己项目的 Bean 扫描4.2 jar 包的 Bean 扫描 五. Bean 的加载 自动配置总结 Sprin…

Linux系统编程——生产者消费者模型

目录 一,模型介绍 1.1 预备知识(超市买东西的例子) 1.2 模型介绍 1.3 CP模型特点 二,基于阻塞队列的CP模型 2.1 介绍 2.2 阻塞队列的实现 2.3 主函数实现 2.4 效果展示 三,POSIX信号量 3.1 信号量原理 3…

Date已不再推荐?为什么我们需要新的 Java 日期时间 API?(LocalDate、LocalDateTime、LocalTime 、Instant)

日期时间 API 是 Java 8 版本的最大功能之一。Java 从一开始就缺少一致的日期和时间方法,而 Java 8 日期时间 API 是对核心 Java API 的一个受欢迎的补充。 为什么我们需要新的 Java 日期时间 API? 在开始研究 Java 8 日期时间 API 之前,让我…

蛋仔派对S18赛季攻略

本人蛋仔名:Z周明昊帅哥(稍微自恋了一点)😎 没夺冠的心情,请看下图: 呜呜呜…… 蛋仔五小只人形: 观看视频 完结撒花^_^ BUY!!!

31-库文件的制作与使用——静态库文件与动态库文件

31-库文件的制作与使用——静态库文件与动态库文件 一、库文件介绍 库文件是在计算机编程中用来存放变量、函数和类的文件,提供给开发者开箱即用的资源。库文件分为静态库和动态库两种,二者在程序链接阶段的行为不同: 静态库:在…

HALCON如何添加外部函数

1、在HALCON菜单栏中点击【编辑】选择【参数选择】或者 点击【函数】选择【管理函数】进入到参数窗口; 2、在参数窗口下依次选择【函数】、【目录】和【添加】操作; 3、外部函数添加成功后,其他程序文件均可使用该外部函数。

自定义prometheus监控获取nginx_upstream指标

1、前言 上篇文章介绍了nginx通过nginx_upstream_check_module模块实现后端健康检查,这篇介绍一下如何自定义prometheus监控获取nginx的upstream指标来实时监控nginx。 2、nginx_upstream_status状态 支持以下三种方式查看nginx_upstream的状态 /status?formatht…

【C++】标准库:介绍string类

string 一.string类介绍二.string类的静态成员变量三.string类的常用接口1.构造函数(constructor)2.析构函数(destructor)3.运算符重载(operator)1.operator2.operator[]3.operator4.operator 4.string的四…

Kitti数据集解析

目录 一、概述 2、详细内容 1、lable标签 2、标定参数 3、点云数据 C++代码读取bin文件 python代码读取bin文件 三、功能实现 1、点云数据转成投影到图像 2、图像数据转成投影到点云 3、点云3D结果转成图像BEV鸟瞰图结果 一、概述 KITTI整个数据集是在德国卡尔斯鲁厄…

DDoS攻击:威胁与防护策略

DDoS(分布式拒绝服务)攻击是网络安全领域的一大挑战,对企业造成严重的影响。本文将深入探讨DDoS攻击的原理和防护方法。 DDoS攻击的原理 DDoS攻击通过大量请求,使目标系统无法响应正常请求。攻击者利用多台计算机发送大量请求&am…

【Unity2D 2022:Data】读取csv格式文件的数据

一、创建csv文件 1. 打开Excel,创建xlsx格式文件 2. 编辑卡牌数据:这里共写了两类卡牌,第一类是灵物卡,具有编号、卡名、生命、攻击四个属性;第二类是法术卡,具有编号、卡名、效果三个属性。每类卡的第一…

使用在UE5中使用AirSim插件Eigen库头文件引用报错,出现报错的解决方式

一、概述 如图所示&#xff0c;用红线圈出的两条头文件引用会报错&#xff0c;提示无法找到他们&#xff0c;但是可以发现的是&#xff0c;他们的路径书写是没有问题的。 // #include <Source/Airlib/deps/eigen3/Eigen/Core> // #include <Source/Airlib/deps/eigen…