InnoDB为什么采用B+树作为索引模型

1. 二叉查找树

  1. 从算法逻辑上考虑,二叉查找树的查找速度和比较次数都是最小的。但需要考虑磁盘IO

  2. 数据库索引是存储在磁盘上的,当数据量比较大的时候,索引的大小可能有几个G甚至更多。当利用索引查询的时候,不可能把整个索引全部加载到内存,只能逐一加载每一个磁盘页,磁盘页对应着索引树的节点。

  3. 假如使用二叉树作为索引结构,假设树的高度为4,需要查找的值为10。流程如下:

    1. 第一次磁盘IO:在这里插入图片描述

    2. 第二次磁盘IO:在这里插入图片描述

    3. 第三次磁盘IO:在这里插入图片描述

    4. 第四次磁盘IO:在这里插入图片描述

  4. 可以看到最坏的情况下,磁盘IO次数等于索引树的高度。为了减少磁盘IO的次数,就要把原本”瘦高“的树变得”矮胖“。这就是B树的特征之一。

2. B树

  1. B树是一种多路平衡查找树,每个节点最多包含k个孩子,k被称为B树的阶。k的大小取决于磁盘页的大小。
  2. 一个m阶的B树具有如下几个特征:
    1. 根结点至少有两个子节点;
    2. 每个中间节点都包含k-1个元素和k个子节点,其中 m/2 <= k <= m;
    3. 每一个叶子节点都包含k-1个元素,其中 m/2 <= k <= m;
    4. 所有的叶子结点都位于同一层;
    5. 每个节点中的元素从小到大排列,节点当中k-1个元素正好是k个子节点包含的元素的值域分划;
  3. B树的比较次数不比二叉树少(尤其是一个节点中元素很多的时候),但它的磁盘IO次数减少了,相比磁盘IO的速度,内存中的比较耗时几乎可以忽略。所以只要树的高度足够低,磁盘IO次数足够少,就能提高性能。
  4. B树的插入和删除过程还是比较复杂的。
  5. B树主要应用于文件系统以及部分数据库索引。比如非关系型数据库MongoDB。而大部分关系型数据库,如MySQL,则使用B+树作为索引。

3. B+树

3.1. B+树特征

  1. B+树和B树有一些共同点,但B+树也具备一些新的特征。
    一个m阶的B+树具有如下几个特征:
    1. 有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点;
    2. 所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接;
    3. 所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素;
  2. B+树节点之间含有重复元素(每个父节点的元素都出现在子节点中,是子节点最大或最小的元素),而且叶子节点之间还用指针连在一起。
  3. 因此,根节点的最大元素(比如15)是整个B+树的最大元素。所以无论插入或删除元素的时候,都要保持最大元素在根节点中。
  4. 叶子节点包含了全量元素信息,并且每一个叶子节点都带有指向下一个节点的指针,形成一个有序链表。
  5. B+树还有一个特点,就是“卫星数据”的位置。所谓卫星数据,指的是索引元素指向的数据记录,比如数据库中的某一行。
    1. B树中,无论是中间节点还是叶子节点都带有卫星数据;
    2. B+树中,只有叶子节点带有卫星数据,中间节点只是索引,与数据没有关联;
  6. 在数据库的聚簇索引(Clustered Index)中,叶子节点直接包含卫星数据。在非聚簇索引(Non-Clustered Index)中,叶子节点带有指向卫星数据的指针。
  7. B+树相对于B树的优点体现在查询性能上:单行查询、范围查询。

3.2. 单行查询

  1. B+树的中间节点没有卫星数据,所以同样大小的磁盘页能容纳更多节点元素,数据量相同的情况下,B+树更“矮胖”,因此磁盘IO次数更少
  2. B+树必须查找到叶子节点,因为数据在叶子节点上。而B树只要匹配到节点即可,不论是中间节点还是叶子节点。因此,B+树比B树的查找性能更稳定

3.3. 范围查询

  1. B树的范围查询:先自顶向下找到范围的下限,然后中序遍历,比较繁琐。
  2. B+树的范围查询:先自顶向下找到范围的下限,然后遍历链表即可,范围查询十分方便
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
-多个线程的几种实现方式包括:承Thread类,实现Runnable接口,实Callable接口,使用线程池。 - Java中的线程池是通过ThreadPoolExecutor类实现的。线程池维护了一个线程队列,可以复用线程,减少线程的创建和销毁开销,提高了性能。 - 不建议直接使用Executors工具类创建线程池是因为它使用的是默认的线程池配置,可能导致线程数量过多,耗尽系统资源。OOM(Out of Memory)是由于创建过多的线程导致内存不足而发生的错误。 - Java内存模型(JMM)是一种规范,定义了多线程程序中各个变量的访问方式。它包括主内存和工作内存,通过控制变量的可见性和原子性来保证线程间的通信与同步。 - 并发编程可能会发生的问题包括:竞态条件、死锁、活锁、饥饿等。可见性问题指一个线程对共享变量的修改对其他线程是否可见,原子性问题指一个操作是否可以被中断或者同时执行。 - 并发编程下会出现原子性问题是因为多个线程同时修改同一个共享变量时,可能会导致不一致的结果。有序性问题是指程序执行的顺序与预期不符。可以使用synchronized关键字、Lock锁等来解决原子性和有序性问题。加上volatile关键字可以保证可见性,禁止指令重排序。 - 内存屏障是通过编译器和处理器来实现的,用于控制指令的执行顺序和内存的可见性。synchronized关键字会在进入和退出临界区时加上内存屏障。 - 单线程指令重排在不影响单线程执行结果的前提下进行优化,但可能会影响多线程的正确性。双重校验锁中使用volatile是为了禁止指令重排,确保多线程环境下的正确性。 - InnoDB索引是通过B+树实现的。B+树具有树高度低、查询效率高、支持范围查询等优势。 - 聚簇索引与非聚簇索引的区别在于数据的存储方式。聚簇索引将数据行存储在叶子节点中,非聚簇索引则将叶子节点指向数据行。不是所有情况都需要取回表的数据,可以通过覆盖索引来避免回表操作。 - 最左前缀匹配指在使用联合索引时,只有从左到右使用索引的前缀部分才能发挥索引的作用。将区分度高的字段放在最左边可以提高索引的效率。唯一索引与普通索引的区别在于是否允许重复值。 - 排查慢SQL可以通过查看慢查询日志、使用性能分析工具(如EXPLAIN、SHOW PROFILE)、优化查询语句等方法。 - MySQL的锁包括行锁和表锁。行锁在并发性能上更好,但需要更多的系统资源,适合处理并发访问较高的场景。表锁在资源消耗上较少,但并发性能相对较差,适合处理并发访问较低的场景。 - FOR UPDATE语句会对查询到的行加上行锁。 - 悲观锁是指在操作数据时始终假设会发生并发冲突,因此会将数据加锁以阻止其他事务的访问。乐观锁是指不加锁,而是通过版本号或时间戳等机制来判断是否发生冲突,减少了加锁的开销。悲观锁适用于并发冲突较多的场景,乐观锁适用于并发冲突较少的场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kuo-Teng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值