B/B+树 (WIP)

168 阅读5分钟

彻底理解B/B+树

1.硬盘的结构

机械硬盘

如何存储

下图展示硬盘的主要结构:盘片、磁头、磁头臂,盘片的功能是存储数据,磁头可以读取存储在盘片上的数据,通过盘片的旋转和磁头臂的摆动,磁头就可以移动到盘片上的任何位置,也就能读取到盘片上存储的所有数据。

注意:盘片的两面都可以存储数据。

image.png

再来仔细看看盘片的构造,可以看出盘片是由多个同心圆组成,将同心圆之间割出的轨道称之为磁道(Track),数据就存储在这一条条磁道之上;

image.png

通过直径对盘片进行切分,划分又被划分成为一个个几何扇区

image.png

通过扇区和磁道,盘片就被进一步切分为一个个磁道扇区,磁道扇区是磁盘的基本存储单位,通常为512B或4KB。

image.png

通过上述内容,我们就能知道

  • 盘面的容量 = 磁道数量 * 几何扇区数量 * 磁道扇区的容量
  • 盘片的容量 = 2 * 盘面容量
  • 硬盘的容量 = 盘片数量 * 盘片容量

如何读写

How

  • 磁头臂摆动
  • 盘片转动

方式

  • 随机
  • 顺序

固态硬盘

  • 颗粒
  • 电压表示(00,01,10,11)

2.数据是如何存储在硬盘之上的

下图可知,RDB中的数据行是以较为规整的方式存储在磁盘上的一个个块当中,假设当前存储一条记录需要25B,一个数据块(512B)中最多能存储20条记录,存储2000条记录就需要100个Block,若此时需要查询出id为1002的记录,就需要遍历这100个Block,若有20万条数据呢?Block的数量也会增加10000,若依然使用遍历这种方式进行搜索,效率将十分低下;如何能摆脱暴力遍历,提升查询的效率呢?

image.png

3.索引/多级索引

我们已经知道id为1002的记录是存储在Block50上,按照遍历的方式进行搜索,需要遍历无关的Block0-Block49,而这又是一笔极大的开销,有没有一种搜索方式能让跳过无关的数据块,答案就是引入索引,索引的作用和目录类似,通过目录上章节信息,读者就能快速定位到他感兴趣的内容,按照这种思路,可以将数据块中第一条记录的主键作为“章节”,数据块的地址指针视为“页数”,通过对比“章节”我们就能快速定位到我们想要查询数据的块,当前的索引仅包含主键、指针,假设存储一条索引需要10B,那么一个块就可以存储大约51个块的索引,2000条记录的索引仅需要2个块就可以存储。

回过头来,借助索引再来尝试一下查询id为1002的记录,首先遍历索引块,发现id=1002存储在Block50上,通过指针再去访问Block50,不难发现,原本遍历需要扫描51个Block,加上索引之后只需要扫描2个Block,效率可谓是大大提升,即使是最坏情况,我们也只用遍历 2(索引) + 1(数据) = 3个数据块。

image.png

假设数据量激增到200万条,此时索引的数量也随之增加到2000条,也就需要40个Block来存储索引数据,此时查询一条数据最多需要访问40+1=41个Block,查询效率又不及预期了;此时可以之上索引数据再加一层索引,也就是构建多级索引,在现有的索引数据(40个Block)上构建的索引只需要一个数据块足矣,此时查询一条记录的路径就变成了:查询一级索引 => 查询二索引 => 查询原始数据

不难看出多级索引其实是典型的树状结构。

4.多路搜索树

  • 二叉搜索树
  • 多路搜索树

存在的问题

  • 降级为链表,复杂度O(N)

5.B树

特点

  • 构建方式:自下而上
  • 插入/删除/修改数据:自调整

存在的问题:

  • 非叶子结点会存储数据

6.B+树

对于MySQL的存储引擎InnoDB而言,数据是以页为单位存储的,一个页的大小是16KB(8个Block),其数据的存储方式和2-3节中描述的内容大体上相似,上述的查询性能问题,InnoDB当然也会有;而InnoDB选择用B+树来作为一种索引方案。

特点

  • 特殊的多路搜索树
  • 构建方式:自下而上
  • 插入/删除/修改数据:自调整(结点上升分裂、合并)
  • 只有叶子结点存储数据
  • 叶子结点构成双向链表

优点

  • 叶子结点构成链表:范围查询有不错的表现
  • 非叶子结点不保存数据,只存指针,索引占用的存储空间更小,查询索引所花费的时间也就更少
  • 查询性能稳定,查询路径一定是从根结点走向叶子结点