【索引的数据结构】第1章节:B+Tree存储结构

目录结构

之前整篇文章太长,阅读体验不好,将其拆分为几个子篇章。

本篇章讲解 B+Tree 存储结构。

什么是索引

可以简单理解为索引好比一本书的目录,通过目录我们可以快速定位到我们要查看的章节。

MySQL 中的数据同样也是根据索引分类,通过索引可以快速高效的查询到我们想要的数据。

索引的优缺点

MySQL 官方对索引的定义:索引(Index)可以帮助 MySQL 高效获取数据的数据结构

索引的本质 :索引是一种数据结构。可以简单理解为索引是一组满足某种特定算法,排好序的快速查找的数据结构, 这种数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法

InnoDB 存储引擎底层默认采用 B+Tree 作为索引的数据结构

先看下二叉搜索树的结构(一个节点存放一条数据):

可以理解为 B+Tree 是从二叉搜索树的基础上演变而来的(一个节点存放多条数据)。

建立索引的目的是为了减少磁盘的 I/O 次数,加快查询效率。

索引是在存储引擎中实现的,不同的存储引擎支持的索引类型不一定相同。

存储引擎可以定义每张表的最大索引数最大索引长度。 所有的存储引擎支持每个表至少 16 个索引,一个索引的长度为 16 个字节,所以支持的最少总索引长度为 256 个字节。

优点

  • 提高数据检索效率,降低数据库磁盘 I/O 成本
  • 通过创建唯一索引,可以保证数据库中每一行的数据的唯一性
  • 加速表和表之间的连接,对于有依赖关系的子表和主表联合查询的时候,可以提高查询速度
  • 在使用分组和排序进行数据查询时,可以显著减少查询中分组和排序的时间,大大降低了 CPU 的消耗

缺点

  • 增加索引和维护索引要耗费时间,并且随着数据量的增加,所耗费的时间会越来越大
  • 除了数据表要占用空间之外,索引也需要占用磁盘空间,并且不同的存储引擎,索引和数据的存储位置可能不同,InnoDB 存储引擎是将索引和数据存放在一个以.ibd结尾的文件中,MyISAM 存储引擎将索引和数据分开存储,索引存放在以.myi为结尾的文件中,数据存放在以.myd结尾的文件中
  • 虽然索引大大提高了查询速度,但是却会降低更新表的速度。当表中的数据要进行增删改的时候,索引也要动态维护(要重新动态分组归类排序数据的存储结构),这样就降低了数据的维护速度。

InnoDB 数据存储格式

区分记录

用户记录页目录项记录页如何区分?

使用记录头里的record_type属性,各个取值的含义如下:

  • 0:普通的用户记录
  • 1:目录项记录
  • 2:最小记录
  • 3:最大记录

假设有一张数据表 test_table有四个字段:c1、c2、c3

sql 复制代码
 create table test_table (
   c1 int,
   c2 int,
   c3 char,
   PRIMARY KEY(c1)
 ) engine=InnoDB

由于页的编号可能不是物理连续的,只要求再逻辑上连续即可,向表中插入多条数据后,可能如下图所示:

但是挨个查找的话,数据量大的时候比较耗时。

为每个页建立一个目录项,每个目录项有一个 key(存储当前页最小的主键值)、page_no 存储页码,大致结构如下如下图所示:

行和行之间以单链表存储,页和页之间以双向链表存储。

记录与记录之间以单链表存储,叶子节点与叶子节点之间以双向链表存储。

迭代优化 1:目录项记录的页

为每个页新建一个目录项之后,考虑到后续数据量会越来越大,如果目录项在物理空间中连续存储,对于新增页或删除页时,目录项也会随之发生,这样就会消耗大量的时间,所以将目录项也简单理解为一个行记录,目录项之间也用单项列表形式存储,也就是为多个目录项建立一个页,这样新增或者删除时,直接改变指针指向即可,效率客观的很,如下图所示:

迭代优化 2:多个目录项记录的页

多个目录项记录页之间的关联如下图所示:

迭代优化 3:多个目录项记录页记录的页

多个目录项记录页组成的也目录页,如下图所示

B+Tree 结构

层层往上汇聚之后,最终形成了一个 B+Tree 的结构:

不论是存放用户记录的数据页,还是存放 目录项记录的数据页,最终都存放在 B+Tree 的结构中,所以我们撑这些数据页为节点。

实际用户记录都存放最下面的节点上,这些节点称为 叶子结点,其余用来存储 目录项的节点被称为 非叶子节点内节点,其中 B+Tree 最上边的节点也称为 根节点(可能会有多个根节点)。

一个 B+Tree 的结构可以分为很多层,规定最下面的层,也就是存放实际用户记录的那一层为第 0层,之后依次往上加。

上述的例子中我们假设存放实际记录的页 最多存 3 条记录,存放目录项记录的页 最多存放 4 条记录,其实真实的开发环境中存放的记录数非常大,假设存放实际记录的数据页(最下层的叶子节点)最多可以存放 100 条记录,存放目录项的数据页(上层的非叶子节点)最多可以存放 1000 调记录,那么计算方式如下:

  • 如果 B+Tree 有 1 层,也就是只有一个存放用户记录的节点,最多能存放 100 条记录
  • 如果 B+Tree 有 2 层,最多能存放 1000 * 100 = 10,0000条记录
  • 如果 B+Tree 有 3 层,最多能存放 1000 * 1000 * 100 = 1,0000,0000条记录
  • 如果 B+Tree 有 4 层,最多能存放 1000 * 1000 * 1000 * 100 = 1000,0000,0000条记录

到达 4 层的时候,就可以存放 1000,0000,00001000 亿条记录了,但是真实的环境中几乎不可能存到 1000 亿条,所以我们用到的 B+Tree 一般都不会超过 4 层。

假设我们是精确查找某一行数据,那在每一层通过二分法或者其他算法找到目录数据页,一次 I/O,然后再查找第二层的数据页,也是一次 I/O,所以精确查找某一行数据最多会经历四次 I/O,如果是范围查询就会有很多次 I/O 了。


一起学编程,让生活更随和!

如果你觉得是个同道中人,欢迎关注博主gzh:【随和的皮蛋桑】。

专注于Java基础、进阶、面试以及计算机基础知识分享🐳。偶尔认知思考、日常水文🐌。


​本文内容总结借鉴于康师傅的 MySQL 视频课:www.bilibili.com/video/BV1iq...

相关推荐
大白要努力!4 分钟前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
C++忠实粉丝5 分钟前
计算机网络socket编程(4)_TCP socket API 详解
网络·数据结构·c++·网络协议·tcp/ip·计算机网络·算法
tatasix1 小时前
MySQL UPDATE语句执行链路解析
数据库·mysql
南城花随雪。1 小时前
硬盘(HDD)与固态硬盘(SSD)详细解读
数据库
儿时可乖了1 小时前
使用 Java 操作 SQLite 数据库
java·数据库·sqlite
懒是一种态度1 小时前
Golang 调用 mongodb 的函数
数据库·mongodb·golang
天海华兮1 小时前
mysql 去重 补全 取出重复 变量 函数 和存储过程
数据库·mysql
daiyang123...2 小时前
测试岗位应该学什么
数据结构
kitesxian2 小时前
Leetcode448. 找到所有数组中消失的数字(HOT100)+Leetcode139. 单词拆分(HOT100)
数据结构·算法·leetcode
gma9992 小时前
Etcd 框架
数据库·etcd