当前位置: 首页 > news >正文

MySQL高频八股—— MySQL的存储引擎及索引结构

大家好,我是钢板兽!

这篇文章包含了Java后端面试中关于MySQL八股中的两个常问的问题:(1)为什么MySQL选用innoDB作为存储引擎?,这个问题在面试的时候可能换一种问法:“innoDB和MyISAM的区别”。(2)“Mysql的索引结构你了解吗?”,这个问题也可能换一种问法:“为什么MySQL索引结构用B+树?”。接下来让我们深入理解这两个问题。

1.为什么MySQL选用innoDB作为存储引擎?

MySQL 的插件式存储引擎架构,这种架构允许我们在创建数据表时,指定不同的存储引擎来处理数据的存储和管理方式,常见的 MySQL 存储引擎主要包括 InnoDB、MyISAM、Memory、Archive、CSV、NDB Cluster 等。不同的存储引擎在事务支持、锁机制、性能优化等方面各有优劣,适合不同的业务场景。MySQL5.5版本之后默认的就是innodb存储引擎,我们通常拿MyISAM来和innodb进行比较,那么Mysql为什么用innoDB作为存储引擎,而不是MyISAM?

innoDB相对于MyISAM有5个优势:

  • **事务管理:**InnoDB完整地支持ACID特性,它的Redo Log用于崩溃恢复,确保事务持久性,UndoLog用于事务回滚和MVCC,保证事务的一致性、原子性和隔离性。

    而MyISAM不提供事务支持。

  • **外键约束:**InnoDB 支持外键约束,在处理多表关联时,可以避免出现数据不一致的问题。

    MyISAM 不支持外键约束,易出现脏数据。

  • 行级锁粒度:InnoDB 支持行级别的锁粒度,在进行数据增删查改时,只锁定特定的数据行,能够显著提升并发性能。

    MyISAM只支持表级别的锁粒度。

  • **底层结构:**InnoDB 每个表都有一个聚簇索引(主键索引),聚簇索引使用B+树构建,主键索引的叶子节点直接存储完整的数据行,查询主键时仅需一次I/O。

    MyISAM的主索引和辅助索引都使用非聚簇索引,叶子节点保存数据地址,查询主键时需要两次I/O。

  • **缓存机制:**InnoDB 采用Buffer Pool(缓冲池)作为核心的缓存机制,可以将热点数据页和索引页缓存在内存中,显著减少I/O次数。

    MyISAM基于磁盘I/O,没有内存缓存机制。

InnoDB的事务管理与外键约束保障了数据的一致性、持久性、隔离性,而行级锁粒度、底层结构和缓存池机制保障了MySQL在高并发场景下的性能的稳定性。

这其中涉及到的Redo Log、UndoLog、行级锁、Buffer Pool(缓冲池)等概念我将会在后面的几篇文章里详细讲到。

2.为什么MySQL的索引结构用B+树?

之前我很容易把Mysql的底层数据结构Mysql的索引结构搞混,简单来说,Mysql的底层数据结构是MySQL 用来存储和管理数据的具体形式和方法,它包括索引的存储方式、数据的组织形式、磁盘管理策略等,不同的存储引擎(如 InnoDB 和 MyISAM)采用不同的底层数据结构。

底层数据结构包括以下内容:

  • 索引结构:B+树
  • 数据存储:聚簇索引与非聚簇索引
  • 数据页与磁盘管理策略:每次读取一个数据页,Buffer Pool缓冲机制
  • 日志机制:Redo Log & Undo Log

我们这里只探究索引结构。

MySQL5.5版本之后默认使用InnoDB存储引擎,对于InnoDB来说,可以用作索引结构的数据结构有很多,比如二叉树、红黑树、Hash表、B树、B树+,为什么MySQL的innoDB使用B+树呢?

在数据库系统中查询数据,我们需要磁盘I/O操作,但是I/O操作很消耗性能和时间,所以要减少单次I/O的时长或者I/O的次数,除此之外,所选择的数据结构可要高效处理范围查询的情况,所以我们可以从减少I/O次数和范围查询来答这个问题。

(1)对于二叉树和红黑树来说,它们的每个节点最多只有两个子节点,这使得树的高度容易过高,这意味着访问一个数据需要更多次磁盘 I/O(每个节点访问一次磁盘)。

MySQL 采用页(Page)作为磁盘管理的基本单位,每页默认大小为 16KB。B+ 树的节点大小也设计成与页大小相匹配,这种设计使得一个节点的读取恰好对应一次磁盘 I/O,避免了读半页或跨页读取的问题。

(2)Hash表的查找效率很高,但是只支持等值查询(如 =IN),不支持范围查询(如 BETWEEN 和排序)。而且Hash 表的散列函数导致数据随机分布,无法利用磁盘的顺序读写性能,导致 I/O 开销大。

(3)相对于红黑树,B树的一个节点可以有多个子节点,而B树的所有节点既存储索引,也存储实际的数据(data),这导致单个非叶子节点的大小变大,能容纳的子节点指针数变少,树的高度也随之增加,也就导致了更多的I/O次数。

​ 而且在B 树中,范围查询需要进行中序遍历,这意味着要频繁地跨节点访问,导致大量随机 I/O。

(4)而B+树的非叶子节点只存储索引,不存储实际的数据,所有的数据都存储在叶子节点,这使得非叶子节点能容纳更多的子节点指针,B+ 树的扇出率(即每个节点的子节点数量)大幅提高

​ 其次B+树的所有的叶子节点都通过双向链表连接,使得范围查询和顺序扫描的效率极高,只要找到范围查询的起点,之后就可以顺序遍历叶子节点的链表,无需返回上层节点进行重新搜索。

上面两张图我们还不太能看出来B树和B+树在扇出率和层级上的差别,这是因为数据量很小。我们可以举个例子,假设我们有一个 100 万条数据的表,现在用 B+ 树和 B 树来构建索引。

  • 节点大小: 每个节点的大小是 16KB(InnoDB 的默认配置)。
  • 索引指针大小: 每个索引指针是 8 字节。
  • 数据项大小: 假设每条数据的大小是 1KB(B 树节点要存储数据)。

我们可以来计算一下B树和B+树的性能

(1)每个节点能容纳多少个节点?

B树的每个节点存储一个数据项加上索引指针,大约需要 1KB + 8 字节 ≈ 1KB,保守估计,16KB 的节点最多可以容纳15个节点。

而B+树的每个非叶子节点只存储索引指针,16KB的节点最多可以容纳:16*1024/8=2048个索引指针,保守估计B+树可以容纳2000个子节点。

B+树 K.O. B树!

(2)100万条数据需要多少层?

B树每个节点只能容纳15个子节点,设B树需要n层才能覆盖100万条数据,通过计算15^(n-1)>100万,可以得到 n=7,也就是B树需要7层才能存储100万条数据。

B+树每个节点可以容纳2000个子节点,假设第1层根节点指向2000个子节点。第2层每个子节点再指向 2000 个子节点,所以第三层可以容纳:2000*2000=400万条数据,也就是说B+树三层节点就可以存储400万条数据。

(3)查询时间

每层节点访问一次磁盘 I/O。假设每次 I/O 花费 10ms,那么B+ 树3 层只需要3 次 I/O,总耗时约为 30ms。B 树7层需要7 次 I/O,总耗时约为 70ms。

所以,我们可以看到B+树通过更高的扇出率、更少的层级,显著减少了 I/O 次数,而其叶子节点的双向链表结构提升了顺序读取的性能,很适合范围查询。这两个优点让B+树在大规模数据场景下提供更高效的查询能力。

如果你觉得这篇文章对你有帮助,欢迎点赞、留言、转发

相关文章:

  • 【深入解析 epoll 的底层实现原理】
  • Java高频面试之集合-08
  • STM32上跑SimpleFOC,电流环、速度环、位置环、棘轮软硬件全开源
  • WPF在特定领域的应用:打造一款专业的图像编辑工具
  • 检索增强生成(RAG)、微调(Fine-tuning)与知识蒸馏(Knowledge Distillation):核心差异与技术选型指南
  • 管理网络安全
  • python collections库速查
  • 订单回款自动化,实现高效运营
  • git常用操作
  • 重磅推出四合一镜像站,免废使用
  • 人形机器人---越来越像人了
  • C++ String类
  • 华为机试牛客刷题之HJ14 字符串排序
  • 通过数据集微调LLM后怎么调用
  • 手写 Promise 的实现
  • Redis7系列:设置开机自启
  • 贪心算法三
  • 3月09日奇怪的Incorrect datetime value
  • git worktree的使用
  • c语言笔记 内存管理之栈内存
  • 昆明市委:今年起连续三年,每年在全市集中开展警示教育
  • 韩国总统选举白热化进行中,中韩青年民间交流促两国友好往来
  • 中方是否支持或参加俄乌谈判?外交部:支持一切有利于和平的努力
  • 钱进已任外交部新闻司副司长
  • 重庆一男大学生掉进化粪池死亡,重庆对外经贸学院:以学校通报为准
  • 技术派|台军首次试射“海马斯”火箭炮,如何压制这种武器?