Innodb索引还不清楚?看这一篇就够啦

news2024/9/25 15:27:22

1. 索引是什么

1.1 初识索引

+----+---------+------+
| id | name    | age  |
+----+---------+------+
|  1 | 帅哥1   |   30 |
|  2 | 帅哥2   |   18 |
|  3 | 帅哥3   |   25 |
|  4 | 帅哥4   |   21 |
|  5 | 帅哥5   |   29 |
|  6 | 帅哥6   |   35 |
+----+---------+------+

上表中,如果要寻找到id为6的数据,最差的情况则是进行6次IO操作,才能获取到数据。

若是使用二叉树的数据结构进行存储,则只需要3次IO即可获取到数据。
在这里插入图片描述

(不使用索引,也有可能一次就会获取到数据,所以在数据量少的情况下,不建议使用索引,且使用索引是需要额外使用存储空间的,对增删改也会造成性能影响)

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

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

1.2 索引的优缺点

优点:

  1. 类似大学图书馆建书目索引,提高数据检索的效率,降低 数据库的IO成本 ,这也是创建索引最主要的原因
  2. 通过创建唯一索引,可以保证数据库表中每一行 数据的唯一性
  3. 在实现数据的参考完整性方面,可以 加速表和表之间的连接 。换句话说,对于有依赖关系的子表和父表联合查询时,可以提高查询速度。
  4. 在使用分组和排序子句进行数据查询时,可以显著 减少查询中分组和排序的时间 ,降低了CPU的消耗。

缺点:

  1. 创建索引和维护索引要 耗费时间 ,并且随着数据量的增加,所耗费的时间也会增加
  2. 索引需要占 磁盘空间 ,除了数据表占数据空间之外,每一个索引还要占一定的物理空间, 存储在磁盘上 ,如果有大量的索引,索引文件就可能比数据文件更快达到最大文件尺寸
  3. 虽然索引大大提高了查询速度,同时却会 降低更新表的速度 。当对表中的数据进行增加、删除和修改的时候,索引也要动态地维护,这样就降低了数据的维护速度。

2. 如何创建索引

3. 索引的数据结构

3.1 行格式

CREATE TABLE index_demo(
 c1 INT,
 c2 INT,
 c3 CHAR(1),
 PRIMARY KEY(c1)
 ) ROW_FORMAT = Compact;

ROW_FORMAT(行格式)
在这里插入图片描述

  1. record_type :记录头信息的一项属性,表示记录的类型, 0 表示普通记录、 2 表示最小记录、 3 表示最大记录、 1目录项记录
  2. next_record :记录头信息的一项属性,表示下一条地址相对于本条记录的地址偏移量,我们用箭头来表明下一条记录是谁
  3. 各个列的值 :这里只记录在 index_demo 表中的三个列,分别是 c1 、 c2 和 c3
  4. 其他信息 :除了上述3种信息以外的所有信息,包括其他隐藏列的值以及记录的额外信息

把记录放到页中如下图所示

在这里插入图片描述

3.2 简单的索引设计方案

接下来以一个例子进行演示

CREATE TABLE index_demo(
 c1 INT,
 c2 INT,
 c3 CHAR(1),
 PRIMARY KEY(c1)
 ) ROW_FORMAT = Compact;
 
insert into index_demo values(1,4,'u'),(3,9,'d'),(5,3,'y')

这三条数据按照上图的存储结构存储如下图所示

在这里插入图片描述
接下来再次插入一条数据

insert into index_demo values(4,4,'a')

我们目前假设一页存储3条数据(但是实际上真正的存储远不止如此),页10已经存满数据了,所以此时不得不再次分配一页来存储 (4,4,‘a’) 这条数据
在这里插入图片描述
注意:因为新分配的数据页编号可能并不是连续的。他们只是通过维护着上一页和下一页的编号而建立了链表关系,另外,页10中最大值为5,页28中有一条数据为4,因为5>4,所以这并不符合下一页的数据主键最小值值必须大于上一页的主键最大值的要求,所以再插入(4,4,‘a’) 这条数据时需要伴随着一次记录移动,把主键值为5的记录移动到页28中,主键值为4的记录移动到页10中

在这里插入图片描述

页分裂:

这表明了在对页的记录进行增删改操作时,我们必须通过一些诸如记录移动的操作来始终保持上述状态一直成立
(下一页的数据主键最小值值必须大于上一页的主键最大值)这个过程我们成为页分裂,例如当前页存满了,再次分配一个新的页进行存储,这也称为页分裂。

由于数据页的编号可能是不连续的,所以在向index_demo表插入很多记录后,如下图所示

在这里插入图片描述
因为这些数据页在物理存储上不是连续的,所以如果想在这些页中根据主键值快速定位某条记录所在页数,我们需要给其做一个目录,每个页对应一个目录,每个目录包括下面两个部分:

  1. 页的用户记录中最小的主键值,我们使用key来进行表示
  2. 页号,我们使用page_no来进行表示

在这里插入图片描述
例如查找主键为20的记录,可以直接定位到在目录项3,页9上,因为(12<20<209),然后进入页9去寻找主键为20的记录。

上述就是数据页的简单目录(索引)

3.3 简单索引的迭代

在这里插入图片描述
上述情况中,如果页的数量很大,则对应的目录项也会很大,那么我们也可以将目录项的一条条记录构建为页,我们可以将目录项构建的页称为目录页,数据构成的页成为数据页,那么目录页和数据页如何区分呢?上述讲解到了行记录的概念,是通过record_type进行区分的。

record_type :记录头信息的一项属性,表示记录的类型, 0 表示普通记录、 2 表示最小记录、 3 表示最大记录、 1目录项记录

优化后如下图:

在这里插入图片描述

上图中,目录页和数据页的不同处:

  1. record_type
  2. 目录页中的一条条目录项只有主键值页页编号,而普通的数据页的数据不仅仅可以包含recort_type,字段等,还有InnoDB自己添加的隐藏列(注意:不同的存储引擎对应的索引的数据结构不同,这里概述的是InnoDB的索引的数据结构)
  3. 记录头信息里有min_rec_mask的属性,只存储于目录项中,在每一目录页中,主键值最小的目录项的min_rec_mask = 1,其余的为0。

相同点:

  1. 两者都是使用页的方式来存储,都会为主键生成Page Directory(页目录),从而在按照主键值进行查找时可以使用二分法进行查找,加快查询速度

现在以查找主键为 20 的记录为例,根据某个主键值去查找记录的步骤就可以大致拆分成下边两步:

  1. 先到存储 目录项记录 的页,也就是页30中通过 二分法 快速定位到对应目录项,因为 12 < 20 <
    209 ,所以定位到对应的记录所在的页就是页9。
  2. 再到存储用户记录的页9中根据 二分法 快速定位到主键值为 20 的用户记录。

随着目录页的数量级变大,如下图所示
在这里插入图片描述

现在因为存储目录项记录的页不止一个,所以如果我们想根据主键值查找一条用户记录大致需要3个步骤,以查找主键值为 20 的记录为例:

  1. 确定 目录项记录页
    我们现在的存储目录项记录的页有两个,即 页30 和 页32 ,又因为页30表示的目录项的主键值的范围是 [1, 320) ,页32表示的目录项的主键值不小于 320 ,所以主键值为 20 的记录对应的目录项记录在 页30 中。
  2. 通过目录项记录页 确定用户记录真实所在的页 。在一个存储 目录项记录 的页中通过主键值定位一条目录项记录的方式说过了。
  3. 在真实存储用户记录的页中定位到具体的记录

所以我们可以针对现有的目录页再给它创建一层目录页,如下图

在这里插入图片描述
如图,我们生成了一个存储更高级目录项的 页33 ,这个页中的两条记录分别代表页30和页32,如果用户记录的主键值在 [1, 320) 之间,则到页30中查找更详细的目录项记录,如果主键值 不小于320 的话,就到页32中查找更详细的目录项记录。

在这里插入图片描述
这个数据结构,它的名称是 B+树

一个B+树的节点其实可以分成好多层,规定最下边的那层,也就是存放我们用户记录的那层为第 0 层,之后依次往上加。之前我们做了一个非常极端的假设:存放用户记录的页 最多存放3条记录 ,存放目录项记录的页 最多存放4条记录 。其实真实环境中一个页存放的记录数量是非常大的,假设所有存放用户记录的叶子节点代表的数据页可以存放 100条用户记录 ,所有存放目录项记录的内节点代表的数据页可以存。

放 1000条目录项记录 ,那么:
如果B+树只有1层,也就是只有1个用于存放用户记录的节点,最多能存放 100 条记录。
如果B+树有2层,最多能存放 1000×100=10,0000 条记录。
如果B+树有3层,最多能存放 1000×1000×100=1,0000,0000 条记录。
如果B+树有4层,最多能存放 1000×1000×1000×100=1000,0000,0000 条记录。

你的表里能存放 100000000000 条记录吗?所以一般情况下,我们 用到的B+树都不会超过4层 ,那我们通过主键值去查找某条记录最多只需要做4个页面内的查找(查找3个目录项页和一个用户记录页),又因为在每个页面内有所谓的 Page Directory (页目录),所以在页面内也可以通过 二分法 实现快速定位记录。

一页16KB(叶子节点存放100条,则1条数据160B,若一条数据量大的情况,则只是叶子节点存放的数据个数小,目录页的存储数量还是不变)

3.4 常见索引概念

索引按照物理实现方式,索引可以分为 2 种:聚簇(聚集)和非聚簇(非聚集)索引。我们也把非聚集索引称为二级索引或者辅助索引。

3.4.1 聚簇索引

特点:

  1. 使用记录主键值的大小进行记录和页的排序,这包括三个方面的含义:页内 的记录是按照主键的大小顺序排成一个 单向链表 。各个存放 用户记录的页 也是根据页中用户记录的主键大小顺序排成一个 双向链表 。存放 目录项记录的页 分为不同的层次,在同一层次中的页也是根据页中目录项记录的主键大小顺序排成一个 双向链表 。
  2. B+树的 叶子节点 存储的是完整的用户记录。所谓完整的用户记录,就是指这个记录中存储了所有列的值(包括隐藏列)。

优点:

  1. 数据访问更快 ,因为聚簇索引将索引和数据保存在同一个B+树中,因此从聚簇索引中获取数据比非聚簇索引更快
  2. 聚簇索引对于主键的 排序查找 和 范围查找 速度非常快按照聚簇索引排列顺序,查询显示一定范围数据的时候,由于数据都是紧密相连,数据库不用从多个数据块中提取数据,所以 节省了大量的io操作 。

缺点:

  1. 插入速度严重依赖于插入顺序 ,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于InnoDB表,我们一般都会定义一个自增的ID列为主键
  2. 更新主键的代价很高 ,因为将会导致被更新的行移动。因此,对于InnoDB表,我们一般定义主键为不可更新
    二级索引访问需要两次索引查找 ,第一次找到主键值,第二次根据主键值找到行数据

限制:

  1. Mysql中只有InnoDB支持聚簇索引,MyISAM不支持聚簇索引
  2. 由于数据物理存储排序方式只能有一种,所以每个Mysql的表只能有一个聚簇索引,一般情况下是主键
  3. 如果没有定义主键,InnoDB会选择非空的唯一索引来代替,如果没有这样的索引,InnoDB会隐式的定义一个主键来作为聚簇索引
  4. 为了充分的利用聚簇索引的聚簇特性,所以InnoDB表的主键尽量选择有序的顺序id,不建议使用无序的id,

3.4.2 二级索引(辅助索引、非聚簇索引)回表

这是我们的表结构

CREATE TABLE `index_demo` (
  `c1` int(11) NOT NULL,
  `c2` int(11) DEFAULT NULL,
  `c3` char(1) DEFAULT NULL,
  PRIMARY KEY (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

如果我们想要使用别的字段进行查找该怎么办呢?例如c2,我们可以利用c2来建立索引,这就称为二级索引(辅助索引、非聚簇索引)
在这里插入图片描述
注意观察此时的叶子节点,存放的数据不是完整的数据,只存放了c2的值和主键的值(非聚簇索引的叶子节点是不存放隐藏列的)。(没有存放c3哦)

回表:我们根据这个以c2列大小排序的B+树只能确定我们要查找记录的主键值,所以如果我们想根据c2列的值查找到完整的用户记录的话,仍然需要到 聚簇索引 中再查一遍,这个过程称为 回表 。也就是根据c2列的值查询一条完整的用户记录需要使用到 2 棵B+树

因为c2不是主键,如果为c2创建索引,它是非聚簇索引,是不存放具体的每一行的数据的,所以根据c2构建的B+树寻找到对应的数据的主键,还得去聚簇索引构建的B+树去根据非聚簇索引构建的B+树得到的主键值去寻找对应的数据,相当于进行了两个B+树的遍历。

到这里也清楚了聚簇索引和非聚簇索引的区别以及优缺点。

在这里插入图片描述

3.4.3 联合索引

我们也可以同时以多个列的大小作为排序规则,也就是同时为多个列建立索引,比方说我们想让B+树按照 c2和c3列 的大小进行排序,这个包含两层含义:

  1. 先把各个记录和页按照c2列进行排序。
  2. 在记录的c2列相同的情况下,采用c3列进行排序

注意一点,以c2和c3列的大小为排序规则建立的B+树称为 联合索引 ,本质上也是一个二级索引。它的意思与分别为c2和c3列分别建立索引的表述是不同的,不同点如下:

  1. 建立 联合索引 只会建立如上图一样的1棵B+树。
  2. 为c2和c3列分别建立索引会分别以c2和c3列的大小为排序规则建立2棵B+树。

在这里插入图片描述

3.5 InnoDB的B+树索引的注意事项

3.5.1 根页面万年不动

在介绍B+树索引时,当时是先有数据页再有目录页的,但是实际上的不是这样,真实的情况如下

  1. 当为表创建一个B+树索引(聚簇索引不是人为创建的,默认就有)的时候,都会为这个索引创建一个根节点最开始没有数据时,根节点既没有用户记录,也没有目录项记录
  2. 随后向表中插入数据,先把用户记录存到根节点中
  3. 当根节点可用空间满的时候,会将根节点的数据复制到新开辟的一个页中,对新的页进行页分裂,此时根节点就不存储用户记录了,转而存储目录页,当根节点的容量不够存储目录项时,会复制目录项到新开辟的一个页,继续对该页进行页分裂,此时根节点存储 “更高级” 的目录项(这个更高级指的是在树结构的高度上的更高级)后续同理。

注意:一个B+树的索引的根节点自诞生起便不会移动,我们对表创建索引时,他的根节点的页号会被记录,后续InnoDB存储引擎需要用到索引时,直接取出根节点的页号进行查询即可。

3.5.2 内节点中目录项记录的唯一性

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.5.3 一个页面最少存储2条记录

4. 索引的优化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/356243.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于飞桨PaddleClas完成半导体晶圆图谱缺陷种类识别

wolfmax老狼&#xff0c;飞桨领航团无锡团团长&#xff0c;飞桨开发者技术专家&#xff08;PPDE&#xff09;&#xff0c;AICA六期学员&#xff0c;某半导体CIM软件集成商图像算法工程师&#xff0c;主要研究方向为图像检测、图像分割等算法。• 作者AI Studio主页https://aist…

Android开发:Activity启动模式

1.怎样设置Activity的启动模式 可以在清单文件中自己添加活动的启动模式, android : launchMode"standard", 不写的话系统默认就是标准模式. 2.启动模式 2.1.默认启动模式 标准启动模式就是栈, 打开一个活动就将活动压入栈中, 返回就将活动退出栈中. 不同的Activit…

老大react说:schedule,我们今年的小目标是一个亿

hello&#xff0c;这里是潇晨&#xff0c;今天来讲个故事 讲个故事&#xff1a; 从前&#xff0c;有家z公司&#xff0c;z公司的ceo叫react&#xff0c;它收下有个小弟或者叫小leader&#xff0c;schedule schedule每天负责消化老大react画的大饼&#xff0c;然后将拆解成一…

如何开始写Python爬虫?给入门Python小白一条清晰的学习路线

记录一下我自己从零开始写Python爬虫的心得吧&#xff01; 我刚开始对爬虫不是很了解&#xff0c;又没有任何的计算机、编程基础&#xff0c;确实有点懵逼。从哪里开始&#xff0c;哪些是最开始应该学的&#xff0c;哪些应该等到有一定基础之后再学&#xff0c;也没个清晰的概…

Java程序怎么运行?final、static用法小范围类型转大范围数据类型可以吗?

文章目录1.能将int强制转换为byte类型的变量吗&#xff1f;如果该值大于byte类型的范围&#xff0c;将会出现什么现象&#xff1f;2. Java程序是如何执行的&#xff1f;3.final 在 Java 中有什么作用&#xff1f;4.final有哪些用法?5.static都有哪些用法?1.能将int强制转换为…

Rust学习入门--【16】Rust 借用所有权 Borrowing / 引用

系列文章目录 Rust 语言是一种高效、可靠的通用高级语言&#xff0c;效率可以媲美 C / C 。本系列文件记录博主自学Rust的过程。欢迎大家一同学习。 Rust学习入门–【1】引言 Rust学习入门–【2】Rust 开发环境配置 Rust学习入门–【3】Cargo介绍 Rust学习入门–【4】Rust 输…

KubeSphere 社区双周报 | OpenFunction 集成 WasmEdge | 2023.02.03-02.16

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2023.02.03-2023.…

众德全自动批量剪辑工具,批量去重伪原创视频,全自动合成探店带货等视频

众德全自动批量剪辑工具已连续更新两年&#xff0c;服务了大大小小的自媒体公司工作室共200多个&#xff0c;成就了几百个草根创业者&#xff0c;实现月入10万&#xff0c;自从创办众德传媒之前&#xff0c;我一直坚信自媒体才是年轻草根创业者的出路&#xff0c;不需要技术门槛…

整合K8s+SpringCloudK8s+SpringBoot+gRpc

本文使用K8s当做服务注册与发现、配置管理&#xff0c;使用gRpc用做服务间的远程通讯一、先准备K8s我在本地有个K8s单机二、准备service-providerpom<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.…

2023年PMP考试教材有哪些?(含pmp资料)

PMP考试教材是《PMBOK指南》&#xff0c;但这次的考试因为大纲的更新&#xff0c;而需要另外的敏捷书籍来备考。且官方发了通知&#xff0c;3、5月还是第六版指南&#xff0c;8月及8月之后&#xff0c;使用第七版教材。 新版考纲将专注于以下三个新领域: 人 – 强调与有效领导项…

java设计模式——观察者模式

概述 定义:又被称为发布-订阅(Publish/Subscribe)模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时&#xff0c;会通知所有的观察者对象&#xff0c;使他们能够自动更新自己。 结构 在观察者模式…

数据分析:旅游景点销售门票和消费情况分析

数据分析&#xff1a;旅游景点销售门票和消费情况分析 文章目录数据分析&#xff1a;旅游景点销售门票和消费情况分析一、前言二、数据准备三、分析数据四、用户购买门票数量分析五、用户复购分析六、用户回购分析七、占比分析1.每个月分层用户占比情况。2.每月不同用户的占比3…

网络模型OSI

网络模型OSI定义模型分布数据封装、解封过程数据链路层1.LLC逻辑链路控制子层(Logic Link Control Sub Layer)2.MAC媒介访问控制子层(Medium Acess Control Sub Layer)CSMA/CARST-CST原理OSI定义 OSI&#xff1a;Open Systems Interconnection Reference Model&#xff0c;开放…

2023年前端开发的八大趋势,值得你关注下

随着新年的到来&#xff0c;许多人制定了提高自己和工作的决心。对于前端开发人员而言&#xff0c;跟上最新的潮流趋势是成功的关键。特别是在经济不好的情况下&#xff0c;很多科技专家在最近一个季度内被解雇&#xff0c;这更加强调了这一点。在2023年&#xff0c;有许多令人…

学习 Python 之 Pygame 开发坦克大战(五)

学习 Python 之 Pygame 开发坦克大战&#xff08;五&#xff09;坦克大战完善地图1. 创建砖墙2. 给砖墙增加子弹击中的碰撞效果3. 给砖墙坦克不能通过的碰撞效果坦克大战完善地图 我的素材放到了百度网盘里&#xff0c;里面还有原版坦克大战素材&#xff0c;我都放在一起来&am…

Blazor入门100天 : 身份验证和授权 (2) - 角色/组件/特性/过程逻辑

目录 建立默认带身份验证 Blazor 程序角色/组件/特性/过程逻辑DB 改 Sqlite将自定义字段添加到用户表脚手架拉取IDS文件,本地化资源freesql 生成实体类,freesql 管理ids数据表初始化 Roles,freesql 外键 > 导航属性完善 freesql 和 bb 特性 本节源码 https://github.com/…

Flink03: 集群安装部署

Flink支持多种安装部署方式 StandaloneON YARNMesos、Kubernetes、AWS… 这些安装方式我们主要讲一下standalone和on yarn。 如果是一个独立环境的话&#xff0c;可能会用到standalone集群模式。 在生产环境下一般还是用on yarn 这种模式比较多&#xff0c;因为这样可以综合利…

C++入门:引用

目录 一. 什么是引用 1.1 引用的概念 1.2 引用的定义 二. 引用的性质和用途 2.1 引用的三大主要性质 2.2 引用的主要应用 三. 引用的效率测试 3.1 传值调用和传引用调用的效率对比 3.2 值返回和引用返回的效率对比 四. 常引用 4.1 权限放大和权限缩小问题 4.2 跨…

【超好用】自定义的mybatis-plus代码生成器

BACKGROUND你是否也有这样的烦恼&#xff1a;每次写代码都需要创建很多包很多层很多类很多接口&#xff1f;耗时且费力姑且不谈&#xff0c;有时可能还大意了没有闪&#xff0c;搞出一堆bug这谁顶得住啊都3202年了&#xff0c;让程序自力更生吧&#xff01;&#xff01;教程 le…

原创|关于一次产品需求程序设计及优化的经历

文章目录一、流程梳理二、设计梳理三、技术方案3.1、下单接口扩展3.3.1、Request类新增deviceType3.3.2、申请单新增字段产品策略(productStrategy)3.3.3、下单产品策略的处理逻辑3.2、询价模块的设计3.2.1、Context设计3.2.2、ProductStrategy类设计3.2.2.1、AbstractProductS…