目录

  • 索引是什么
  • `mysql` 为何使用 `B+ Tree` 作为索引的数据结构
    • 二叉树为什么不可行
    • 平衡二叉树为什么不可行
    • `B Tree`
    • 为什么说 `B Tree` 能够解决平衡二叉树存在的问题呢
    • `B+ Tree`
    • `B Tree` 和 `B+ Tree` 区别
  • `mysql` 为什么最终要去选择 `B+ Tree`
  • `Innodb` 引擎的索引实现
    • 聚集索引(主键索引)
    • 普通索引(非主键索引)
    • 聚集索引与普通索引示例
      • 建表
      • 索引 `B+ Tree` 的存储结构
      • 普通索引(非主键索引)的 `B+ Tree` 的存储结构
      • 聚集索引查找过程
      • 普通索引查找过程
        • 普通索引查找过程第一步
        • 普通索引查找过程第二步
    • 回表查询
    • 普通索引存在的意义

索引是什么

索引是为了加速对表中数据的检索的一种数据结构,是 B+ Tree的一种数据结构

索引保存数据的方式一般有两种:

  • 数据区保存主键 id 对应行数据的所有数据具体内容
  • 数据区保存的是真正保存数据的磁盘地址

其工作机制如下图:

上图中,如果现在有一条 sql 语句

select * from user where id = 40
  • 如果没有索引的条件下,我们要找到这条记录,我们就需要在数据中进行全表扫描,匹配 id = 40 的数据
  • 如果有了索引,我们就可以通过索引进行快速查找,如上图中,可以先在索引中通过 id = 40 进行二分查找,再根据定位到的地址取出对应的行数据

mysql 为何使用 B+ Tree 作为索引的数据结构

关于数据结构之树详情查看:https://blog.csdn.net/weixin_38192427/article/details/112973493

二叉树为什么不可行

对数据的加速检索,首先想到的就是二叉树,二叉树的查找时间复杂度可以达到 O(log2(n))

下面看一下二叉树的存储结构:

二叉树搜索相当于一个二分查找。二叉查找能大大提升查询的效率,但是它有一个问题:二叉树以第一个插入的数据作为根节点,如上图中,如果只看右侧,就会发现,就是一个线性链表结构。如果我们现在的数据只包含1, 2, 3, 4,就会出现

如果我们要查询的数据为 4,则需要遍历所有的节点才能找到 4,即相当于全表扫描,就是由于存在这种问题,所以二叉查找树不适合用于作为索引的数据结构

平衡二叉树为什么不可行

为了解决二叉树存在线性链表的问题,会想到用平衡二叉查找树来解决

下面看看平衡二叉树是怎样的:

平衡二叉查找树定义为:节点的子节点高度差不能超过 1,如上图中的节点 20,左节点高度为 1,右节点高度 0,差为 1,所以上图没有违反定义,它就是一个平衡二叉树。保证二叉树平衡的方式为左旋,右旋等操作

如果上图中平衡二叉树保存的是 id 索引,现在要查找 id = 8 的数据,过程如下:

  • 把根节点加载进内存,用 810 进行比较,发现 810 小,继续加载 10 的左子树
  • 5 加载进内存,用 85 比较,同理,加载 5 节点的右子树
  • 此时发现命中,则读取 id8 的索引对应的数据

到这里,平衡二叉树解决了存在线性链表的问题,数据查询的效率好像也还可以,基本能达到 O(log2(n)), 那为什么 mysql 不选择平衡二叉树作为索引存储结构,他又存在什么样的问题呢

  • 搜索效率不足:一般来说,在树结构中,数据所处的深度,决定了搜索时的 IO 次数(mysql 中将每个节点大小设置为一页大小,一次 IO 读取一页或一个节点)。如上图中搜索 id = 8 的数据,需要进行 3IO。当数据量到达几百万的时候,树的高度就会很恐怖
  • 查询不稳定。如果查询的数据落在根节点,只需要一次 IO,如果是叶子节点或者是支节点,会需要多次 IO 才可以
  • 存储的数据内容太少。没有很好利用操作系统和磁盘数据交换特性,也没有利用好磁盘 IO 的预读能力。因为操作系统和磁盘之间一次数据交换是以页为单位的,一页大小为 4K,即每次 IO 操作系统会将 4K 数据加载进内存。但是,在二叉树每个节点的结构只保存一个关键字,一个数据区,两个子节点的引用,并不能够填满 4K 的内容。幸幸苦苦做了一次的 IO 操作,却只加载了一个关键字。在树的高度很高,恰好又搜索的关键字位于叶子节点或者支节点的时候,取一个关键字要做很多次的 IO

那有没有一种结构能够解决二叉树的这种问题呢?有,那就是多路平衡查找树

B Tree

B Tree 是一个绝对平衡树,所有的叶子节点在同一高度

如下图所示:

上图为一个 2-3 树(每个节点存储 2 个关键字,有 3 路),多路平衡查找树也就是多叉的意思,从上图中可以看出,每个节点保存的关键字的个数和路数关系为:关键字个数 = 路数 – 1

假设要从上图中查找 id = X 的数据,B TREE 搜索过程如下:

  • 取出根磁盘块,加载 4060 两个关键字
  • 如果 X 等于 40,则命中;如果 X 小于 40P1;如果 40 < X < 60P2;如果 X = 60,则命中;如果 X > 60P3
  • 根据以上规则命中后,接下来加载对应的数据,数据区中存储的是具体的数据或者是指向数据的指针

为什么说 B Tree 能够解决平衡二叉树存在的问题呢

  • B Tree 能够很好的利用操作系统和磁盘的交互特性:mysql 为了很好的利用磁盘的预读能力,将页大小设置为 16K,即将一个节点(磁盘块)的大小设置为 16K,一次 IO 将一个节点(16K)内容加载进内存。这里,假设关键字类型为 int,即 4 字节,若每个关键字对应的数据区也为 4 字节,不考虑子节点引用的情况下,则上图中的每个节点大约能够存储(16 * 1000)/ 8 = 2000 个关键字,共 2001 个路数。对于二叉树,三层高度,最多可以保存 7 个关键字,而对于这种有 2001 路的 B 树,三层高度能够搜索的关键字个数远远的大于二叉树

这里顺便说一下:在 B Tree 保证树的平衡的过程中,每次关键字的变化,都会导致结构发生很大的变化,这个过程是特别浪费时间的,所以创建索引一定要创建合适的索引,而不是把所有的字段都创建索引,创建冗余索引只会在对数据进行新增,删除,修改时增加性能消耗

B Tree 确实已经很好的解决了问题,我先这里先继续看一下 B+Tree 结构,再来讨论 B TreeB+ Tree 的区别

B+ Tree

B+ TreeB Tree 的一个变种,在 B+ Tree 中,B Tree 的路数和关键字的个数的关系不再成立了,数据检索规则采用的是左闭合区间,路数和关键个数关系为 1:1

具体如下图所示:

如果上图中是用 id 做的索引,如果是搜索 X = 1 的数据,搜索规则如下

  • 取出根磁盘块,加载 1,28,66 三个关键字
  • X <= 1P1,取出磁盘块,加载 1,10,20 三个关键字
  • X <= 1P1,取出磁盘块,加载 1,8,9 三个关键字
  • 已经到达叶子节点,命中 1,接下来加载对应的数据,图中数据区中存储的是具体的数据

B TreeB+ Tree 区别

  • B Tree一个节点里存的是数据,而 B+ Tree 存储的是索引(地址),所以 B Tree 里一个节点存不了很多个数据,但是 B+ Tree 一个节点能存很多索引,B+ Tree 叶子节点存所有的数据
  • B+ Tree 的叶子节点是数据阶段用了一个链表串联起来,便于范围查找 通过 B TreeB+ Tree 的对比我们看出,B+ Tree 节点存储的是索引,在单个节点存储容量有限的情况下,单节点也能存储大量索引,使得整个 B+ Tree 高度降低,减少了磁盘 IO
  • B+ Tree 的叶子节点是真正数据存储的地方,叶子节点用了链表连接起来,这个链表本身就是有序的,在数据范围查找时,更具备效率。因此 mysql 的索引用的就是 B+ TreeB+ Tree 在查找效率、范围查找中都有着非常不错的性能

mysql 为什么最终要去选择 B+ Tree

  • B+TreeB TREE 的变种,B TREE 能解决的问题,B+TREE 也能够解决(降低树的高度,增大节点存储数据量)
  • B+Tree 扫库和扫表能力更强。如果我们要根据索引去进行数据表的扫描,对 B TREE 进行扫描,需要把整棵树遍历一遍,而 B+TREE 只需要遍历他的所有叶子节点即可(叶子节点之间有引用)
  • B+TREE 磁盘读写能力更强。他的根节点和支节点不保存数据区,所以根节点和支节点同样大小的情况下,保存的关键字要比 B TREE 要多。而叶子节点不保存子节点引用,能用于保存更多的关键字和数据。所以 B+TREE 读写一次磁盘加载的关键字比 B TREE 更多
  • B+Tree 排序能力更强。上面的图中可以看出,B+Tree 天然具有排序功能
  • B+Tree 查询性能稳定。B+Tree 数据只保存在叶子节点,每次查询数据,查询 IO 次数一定是稳定的。当然这个每个人的理解都不同,因为在 B TREE 如果根节点命中直接返回,确实效率更高

Innodb 引擎的索引实现

聚集索引(主键索引)

聚集索引: InnoDB 会使用主键 ID 建立索引 B+ Tree,而其 B+ Tree 的叶子节点存储的是主键 ID 对应的数据,聚集索引的叶子节点称为数据页,聚集索引的这个特性决定了索引树中的数据也是索引的一部分

由此可见,使用聚集索引查询会很快,因为可以直接定位到行记录

  • 如果表设置了主键,则主键就是聚集索引(主键索引)
  • 如果表没有主键,则会默认第一个 NOT NULL,且唯一(UNIQUE)的列作为聚集索引(主键索引)
  • 以上都没有,则会默认创建一个隐藏的 row_id 作为聚集索引(主键索引)

普通索引(非主键索引)

普通索引:当表中创建了普通索引(非主键索引)时,InnoDB 就会建立普通索引 B+ Tree,这个普通索引 B+ Tree 的叶子节点存储的是数据记录的主键 ID,而与这个数据记录的主键 ID 所对应的就是普通索引(非主键索引)

聚集索引与普通索引示例

建表

mysql> create table user(-> id int(10) auto_increment,-> name varchar(30),-> age tinyint(4),-> primary key (id),-> index idx_age (age)-> )engine=innodb charset=utf8mb4;insert into user(name,age) values('张三',30);
insert into user(name,age) values('李四',20);
insert into user(name,age) values('王五',40);
insert into user(name,age) values('刘八',10);mysql> select * from user;
+----+--------+------+
| id | name  | age |
+----+--------+------+
| 1 | 张三  |  30 |
| 2 | 李四  |  20 |
| 3 | 王五  |  40 |
| 4 | 刘八  |  10 |
+----+--------+------+

id 字段是聚集索引,age 字段是辅助索引(普通索引)

索引 B+ Tree 的存储结构

id 是主键,所以是聚集索引,其叶子节点存储的是 id 主键对应的行记录的数据。聚集索引的这个特性决定了索引树中的数据也是索引的一部分

普通索引(非主键索引)的 B+ Tree 的存储结构

age 是普通索引(辅助索引),其叶子节点存储的是数据记录的主键 ID,而与这个数据记录的主键 ID 所对应的就是普通索引(非主键索引)

聚集索引查找过程

如果查询条件为主键(聚集索引),则只需扫描一次 B+ Tree 即可通过聚集索引定位到要查找的行记录数据

select * from user where id = 1

普通索引查找过程

如果查询条件为普通索引(非聚集索引),需要扫描两次 B+ Tree,第一次扫描通过普通索引定位到聚集索引的值,然后第二次扫描通过聚集索引的值定位到要查找的行记录数据

select * from user where age = 30;
  • 先通过普通索引 age = 30 定位到主键值 id = 1
  • 再通过聚集索引 id = 1 定位到行记录数据

普通索引查找过程第一步

普通索引查找过程第二步

回表查询

先通过普通索引的值定位聚集索引值,再通过聚集索引的值定位行记录数据,需要扫描两次索引 B+ Tree,它的性能较扫一遍索引树更低

普通索引存在的意义

  • 聚集索引:InnoDB 会使用主键 ID 建立索引 B+ 树,而其 B+ 树 的叶子节点存储的是主键 ID 对应的数据
  • 普通索引:普通索引 B+ 树 的叶子节点存储的是数据记录的主键 ID,而与这个数据记录的主键 ID 所对应的就是聚集索引(主键索引)

因为 InnoDB 需要节省存储空间。一个表里可能有很多个索引,InnoDB 都会给每个加了索引的字段生成索引树,如果每个字段的索引树都存储了具体数据,那么这个表的索引数据文件就变得非常巨大(数据极度冗余了)。从节约磁盘空间的角度来说,真的没有必要为每个字段索引树都存具体数据,通过这种看似“多此一举”的步骤,在牺牲较少查询的性能下节省了巨大的磁盘空间,这是非常有值得的

mysql的索引(一)相关推荐

  1. 面试官问:为什么MySQL的索引不采用Kafka的索引机制

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 第一眼看到这个问题,也是很迷惑的,谁没事会问这种问题.然而,事实上 ...

  2. 面试官:为什么MySQL的索引要使用B+树,而不是其它树?比如B树?

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 来源:https://dwz.cn/exC8JdQS InnoDB的一棵B+树可以存放多少行数 ...

  3. 为了把mysql的索引底层原理讲清楚,我把计算机翻了个底朝天

    来自:非科班的科班 什么是索引 概念:索引是提高mysql查询效率的数据结构.总的一句话概括就是索引是一种数据结构. 数据库查询是数据库的最主要功能之一.设计者们都希望查询数据的速度能尽可能的快,因此 ...

  4. mysql多索引结构_MySQL 索引结构

    谈到 MYSQL 索引服务端的同学应该是熟悉的不能再熟悉,新建表的时候怎么着都知道先来个主键索引,对于经常查询的列也会加个索引加快查询速度.那么 MYSQL 索引都有哪些类型呢?索引结构是什么样的呢? ...

  5. mysql repair 索引_mysql 创建索引、重建索引、查询索引、删除索引 转自:http://www.phpernote.com/mysql/942.html...

    本篇文章主要是对MySQL索引操作方法做了一下总结,包括创建索引.重建索引.查询索引.删除索引的操作.以下所列示例中中 `table_name` 表示数据表名,`index_name` 表示索引名,c ...

  6. mysql简单索引_mysql简单索引

    mysql的索引是在存储引擎实现的,而不是在服务器层,因此不是标准的. b-tree: 大部分的mysql支持b-tree索引,archive知道mysql5.1才支持,而且仅仅是支持单个auto_i ...

  7. mysql单列索引和多列索引_mysql 单列索引与多列索引

    以下的文章主要介绍的是MySQL数据库索引类型,其中包括普通索引,唯一索引,主键索引与主键索引,以及对这些索引的实际应用或是创建有一个详细介绍,以下就是文章的主要内容描述. (1)普通索引 这是最基本 ...

  8. MySQL - 践行索引优化

    文章目录 生猛干货 Pre Table Demo DB Version Case 全值匹配 最左前缀 禁止索引列上做任何操作(计算.函数.(自动or手动)类型转换) 存储引擎不能使用索引中范围条件右边 ...

  9. mysql+零时数据结构,MySql主要索引数据结构

    索引数据结构 1. 二叉搜索树(Binary Search Tree) 二叉搜索树是每个节点最多有两个子节点的树,按照右侧子节点大于本节点,左侧子节点小于本节点的规律排列,可以用作搜索,结构如下图所示 ...

  10. mysql聚集索引可以多列吗_MySQL使用单列索引和多列索引

    讨论MySQL选择索引时单列单列索引和多列索引使用,以及多列索引的最左前缀原则. 1. 单列索引 在性能优化过程中,选择在哪些列上创建索引是最重要的步骤之一.可以考虑使用索引的主要有两种类型的列:在W ...

最新文章

  1. 设计模式学习2:单例模式
  2. Codeforces Round #193 (Div. 2)
  3. server.mappath php,Server.MapPath( ) 方法的主要功能是获取文件的绝对路径。
  4. sqlalchemy query
  5. 服务器控件的使用注意事项
  6. 《数据挖掘导论》学习 | 第十章 异常检测
  7. 数据库和数据库实例的概念
  8. 每个国家对应的values语言Locale和国家代码对照表
  9. C语言统考试卷一及答案,2017年《计算机应用基础》统考试题及答案
  10. Okhttp上传图片
  11. 架构设计:负载均衡层设计方案(8)——负载均衡层总结上篇
  12. php后端上传文件,php实现文件上传方法_后端开发
  13. 邮购了正版蓝光碟《CODEnbsp;GEASS叛逆…
  14. HDU 4745 Two Rabbits——最长回文子串
  15. ubuntu 源更新
  16. 前端接入阿里云外呼SDK
  17. Django用admin开发的幼儿园薪资管理系统-2
  18. Verilog语言入门学习(3)
  19. 【大葱虽有4大治病功效】
  20. 唯品会不同阶段发展历程的技术演进

热门文章

  1. 自动驾驶 6-1: 横向车辆控制介绍 Lesson 1: Introduction to Lateral Vehicle Control
  2. 算法:Majority Element(求众数)
  3. 中山大学计算机学院官网万海,中山大学
  4. 2021-09-1311. 盛最多水的容器 数组 双指针
  5. 561. 数组拆分 I
  6. 从有序数组中找出某个数出现的次数
  7. matlab算薄板模态,基于MATLAB计算FGM薄板刚柔耦合动力学响应的仿真方法与流程
  8. Prometheus自动发现Exporter实现方案(一看就懂)
  9. 目标追踪论文之狼吞虎咽(1):VITAL算法
  10. 基础集合论笔记 目录