为什么MySQL要用B+树?聊聊B+树与硬盘的前世今生

上一节,我们聊到数据库为了让我们的查询加速,通过索引方式对数据进行冗余并排序,这样我们在使用时就可以在排好序的数据里进行快速的二分查找,使得查询效率指数提升。但是我在结尾同样提到一个问题,就是内存大小一般是很有限的,不可能把一个表所有的数据都加载到内存中,那么我们该如何解决这个问题呢?在解决这个问题之前,需要先简单了解一下硬盘知识

硬盘知识简介

由于机械硬盘的高耐久,低成本,现在仍然是数据存储的主流,所以这里着重讨论机械硬盘,下面是一个机械硬盘结构图

机械硬盘的数据都存放在盘片中,当我们从硬盘读取数据时,我们需要提供一个地址,然后硬盘通过前后移动磁头寻址,最后把地址对应数据返回。

这里有两个过程很重要,一个是寻址,一个是读取数据。以目前机械硬盘的速度,如果我们要从机械硬盘读取一条1KB的数据大概只需要0.01ms(100MB/s),而寻址却平均在10ms左右。通常我们把读取一段连续的数据,不需要多次寻址的操作叫做顺序读,而读取不连续的数据需要多次寻址的操作叫做随机读,用来区分它们之间的性能差距。

为了充分利用机械硬盘的性能,通常把相关数据连续保存,这样就可以一次加载更多的数据,减少磁头的的移动次数。操作系统有很多对此的优化,例如Linux ext3文件系统默认块大小就是4kb。还有linux预加载能力,即当你频繁访问一块数据时,系统会帮你把相邻的数据也加载进来。

MySQL InnoDB与硬盘

了解完机械硬盘的基本知识,现在回到MySQL,MySQL InnoDB引擎也会把数据进行分块存储,默认是16KB。所以我们上一节中的索引结构图在硬盘中的存储就是每16KB为一个块,当一个块快存放快满的时候开辟一个新的块来存放。

以books表为例

create table books(
id int not null primary key auto_increment,
name varchar(255) not null,
author varchar(255) not null,
created_at datetime not null default current_timestamp,
updated_at datetime not null default current_timestamp on update current_timestamp,
index idx_books_name(name)
)engine=InnoDB;

该表name字段的索引idx_books_name在硬盘中的存放就如下图

当块越来越多的时候,我们可能无法一次把所有的块都加载到内存,此时就要对每个块再进行索引,如下图:

 

每个块的上一级都存放着一条指向该块首记录的记录。这样只需要加载顶部的第一块,然后通过区间判断就可以找到下一块的地址。

例如我们查询一条name=name n+1的记录,过程如下:

  1. 先从左边顶部块a开始查找,发现"name n+1"在"name 1"到"name m"记录之间

  2. 加载"name 1"对应的下一级块b

  3. 发现"name n+1"在块b二条记录第三条记录之间,所以需要加载第二条记录对应的下一级块d

  4. 加载块d

  5. 在块d中找到"name n+1"的那条记录。

如果把上图旋转一个,可以发现,整个图就是一个树,这其实就是B+树。B+树通过对数据块进行索引,使得当数据量很大,无法一次全部加载到内存时,可以先加载一个表的顶部数据块,然后根据数据所在区间再加载下一级的数据块。这样既保证了我们的快速搜索,又减少了内存使用。

MySQL InnoDB的聚簇索引和二级索引

了解了B+树,现在就可以很容易区分MySQL的聚簇索引和二级索引。

聚簇索引就是用主键生成B+树,在叶子节点存放这条记录的完整信息

二级索引就是用索引行生成B+树,在叶子节点只存放索引行和该行对应的主键信息

下面是聚簇索引和二级索引的区分图

  

了解上面的知识,对于一个查询,我们就可以大概想象出他的执行步骤

select * from books where name = "name400";

例如上面sql的执行步骤如下:

  1. 在二级索引idx_books_name索引中查找name="name400"的字段所对应的主键id

  2. 通过主键id在聚簇索引找到此id所对应的记录

  3. 返回记录中的所有字段

当我们select的字段在二级索引上不存在时,都需要使用聚簇索引回表查询剩余字段。所以聚簇索引,也就是我们所说的id列,占用空间越小越好, 这样就可以在一个节点中存放更多的id值,减少树的层级,加速查询效率。一般推荐主键使用int或者bigint而不是字符串。同时最好保证插入的id值为递增的,这样就不会造成在一个已经满的节点中插入一条记录造成页分裂,降低查询效率。

小结

这节我们先了解了硬盘的基础知识,知道了机械硬盘的顺序读与随机读的巨大性能差距,以及操作系统为了优化磁盘性能而把数据进行按块存储。然后又学习了MySQL通过使用B+树,把存放索引的多个数据块进行索引,解决了我们上一节使用二分搜索需要先把所有数据都加载到内存的问题。最后,我们了解了聚簇索引和二级索引的区别,以及其中的使用建议。

下一节,我们会聊一聊如何创建一个好的索引,判断一个索引的好坏标准有哪些。

为什么MySQL要用B+树?聊聊B+树与硬盘的前世今生【宇哥带你玩转MySQL 索引篇(二)】的更多相关文章

  1. 【宇哥带你玩转MySQL】索引篇(一)索引揭秘,看他是如何让你的查询性能指数提升的

    场景复现,一个索引提高600倍查询速度? 首先准备一张books表 create table books( id int not null primary key auto_increment, na ...

  2. MySQL如何创建一个好索引?创建索引的5条建议【宇哥带你玩转MySQL 索引篇(三)】

    MySQL如何创建一个好索引?创建索引的5条建议 过滤效率高的放前面 对于一个多列索引,它的存储顺序是先按第一列进行比较,然后是第二列,第三列...这样.查询时,如果第一列能够排除的越多,那么后面列需 ...

  3. B树和B+树对比,为什么MySQL数据库索引选择使用B+树?

    一 基础知识 二叉树 根节点,第一层的节点 叶子节点,没有子节点的节点. 非叶子节点,有子节点的节点,根节点也是非叶子节点. B树 B树的节点为关键字和相应的数据(索引等) B+树 B+树是B树的一个 ...

  4. MySQL索引(二)B+树在磁盘中的存储

    MySQL索引(二)B+树在磁盘中的存储 回顾  上一篇文章<MySQL索引为什么要用B+树>讲了MySQL为什么选择用B+树来作为底层存储结构,提了两个知识点: B+树索引并不能直接找 ...

  5. MySQL:InnoDB存储引擎的B+树索引算法

    很早之前,就从学校的图书馆借了MySQL技术内幕,InnoDB存储引擎这本书,但一直草草阅读,做的笔记也有些凌乱,趁着现在大四了,课程稍微少了一点,整理一下笔记,按照专题写一些,加深一下印象,不枉读了 ...

  6. B树、B-树、B+树、B*树【转】,mysql索引

    B树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: B ...

  7. MySQL数据库中索引的数据结构是什么?(B树和B+树的区别)

    B树(又叫平衡多路查找树) 注意B-树就是B树,-只是一个符号. B树的性质(一颗M阶B树的特性如下) 1.定义任意非叶子结点最多只有M个儿子,且M>2: 2.根结点的儿子数为[2, M]: 3 ...

  8. Mysql为什么使用b+树,而不是b树、AVL树或红黑树?

    首先,我们应该考虑一个问题,数据库在磁盘中是怎样存储的?(答案写在下一篇文章中) b树.b+树.AVL树.红黑树的区别很大.虽然都可以提高搜索性能,但是作用方式不同. 通常文件和数据库都存储在磁盘,如 ...

  9. 浅谈算法和数据结构: 十 平衡查找树之B树

    前面讲解了平衡查找树中的2-3树以及其实现红黑树.2-3树种,一个节点最多有2个key,而红黑树则使用染色的方式来标识这两个key. 维基百科对B树的定义为“在计算机科学中,B树(B-tree)是一种 ...

随机推荐

  1. c strncpy 容易出错的地方

    使用strncpy的是注意两点,目的是数组和目的是指针 .目的是数组: ] = "abcde"; // ] = "; strncpy(dest,src,N); dest[ ...

  2. 【Win10】我们无法更新系统保留的分区

      前言 笔者是一个萌新,这个方案也是慢慢摸索出来的,有更好的方案欢迎大家提出 前段时间用公司电脑发现win10新版本还行,回家升级自己的电脑却提示“我们无法更新系统保留的分区”.(O_o)?? 笔者 ...

  3. 从零搭建Spring Cloud Gateway网关(一)

    新建Spring Boot项目 怎么新建Spring Boot项目这里不再具体赘述,不会的可以翻看下之前的博客或者直接百度.这里直接贴出对应的pom文件. pom依赖如下: <?xml vers ...

  4. 用纯Python实现循环神经网络RNN向前传播过程(吴恩达DeepLearning.ai作业)

    Google TensorFlow程序员点赞的文章!   前言 目录: - 向量表示以及它的维度 - rnn cell - rnn 向前传播 重点关注: - 如何把数据向量化的,它们的维度是怎么来的 ...

  5. centOS7python版本升到3

    我们使用Python源代码编译的方式安装python3. 一.安装必要工具 yum-utils ,它的功能是管理repository及扩展包的工具 (主要是针对repository) $ sudo y ...

  6. Mybatis(一)Mybatis相关概念

    1.1 传统的JDBC实现 public static void main(String[] args) { Connection connetion = null; PreparedStatemen ...

  7. 网络安全从入门到精通 (第二章-4) 后端基础PHP—简介及基本函数-上

    本文内容 什么是PHP PHP的基础语法 运算符 条件分支语句 1,什么是PHP? PHP(超文本预处理器)是一种通用开源语言,(是动态语言中的一种,动态语言还有ASP,ASPX,JSP). PHP语 ...

  8. Bugku流量分析题目总结

    flag被盗 题目链接:https://ctf.bugku.com/files/e0b57d15b3f8e6190e72987177da1ffd/key.pcapng 解题思路: 这个题目是比较基本的 ...

  9. Journal of Proteome Research | iHPDM: In Silico Human Proteome Digestion Map with Proteolytic Peptide Analysis and Graphical Visualizations(iHPDM: 人类蛋白质组理论酶解图谱的水解肽段分析和可视化展示)| (解读人:邓亚美)

    文献名:iHPDM: In Silico Human Proteome Digestion Map with Proteolytic Peptide Analysis and Graphical Vi ...

  10. JS中iframe子页面与父页面之间通信

    iframe子页面与父页面通信根据iframe中src属性是同域链接还是跨域链接,通信方式也不同. 一.同域下父子页面的通信 父页面parent.html <html> <head& ...