MySQL —— 索引

索引的概念

MySQL的索引是⼀种数据结构,它可以帮助数据库高效地查询、更新数据表中的数据。索引通过
⼀定的规则排列数据表中的记录,使得对表的查询可以通过对索引的搜索来加快速度。

MySQL索引类似于书籍的目录,通过指向数据行的位置,可以快速定位和访问表中的数据,比如
汉语字典的目录(索引)页,我们可以按笔画、偏旁部首、拼音等排序的⽬录(索引)快速查找到需
要的字。

探讨索引的数据结构

我们来一起探讨那种数据结构用来当作索引更适合数据库

首先是哈希表,对于哈希表来说,查找的时间复杂度为 O(1),十分优秀,但是却不适合数据库,因为哈希表不支持范围查找,而我们数据库是需要支持范围查找的,例如:我们有张成绩表,要查询成绩大于等于60以上的学生时,这时就要求我们数据库能支持**范围查找,**所以哈希表并不适合作索引


接下来登场的是二叉搜索树,查找的时间复杂度为O(logN),但是如果出现极端情况,二叉搜索树是可能会退化成一颗单分支的树,这时候时间复杂度就变成O(N),还是不理想,这时候大家可能会想到AVL树或者红黑树,AVL树就不说了,旋转次数太多了,在数据库面前,数据量十分庞大,如果旋转,呵呵…
我们来看一下红黑树,虽然保持相对平衡,但是在数据一多的情况下,我们无法保证树到底有多高

为什么要讨论树高呢?
因为数据库的数据是存储在磁盘上的,所以当你需要读取数据的时候,是需要进行磁盘的IO的,磁盘的IO速度是十分慢的,如果IO 次数一高,效率就会低下哎,所以我们应该减少磁盘IO次数,也就是减小树高,那么红黑树就不能满足了

磁盘IO 是制约数据库性能的主要因素


既然红黑树不行,那我们可以考虑B树,这时一颗多路平衡树,由于是多路,所以可以降低书高,但是我们的MySQL还是不满意,觉得效率还不是不够高,于是MySQL 就使用B树的变形也就是B+树,我们在前面就知道B+树有一些特点:真实的数据都是保存在叶子节点上的,非叶子结点只是起到一个导航的作用,并且叶子结点是使用双向链表进行连接的,所以在数据库进行范围查找的时候十分方便。

B树示例:
在这里插入图片描述

B+树示例:
在这里插入图片描述
B+树的时间复杂度是O(logN),并且可以有效的控制树高

B+树与B树的对比:
1.B+树的叶子结点之间有相互连接的引用,可以通过这个连接找到与其相邻的兄弟节点,mysql 在组织叶子结点时使用的时双向链表
2.非叶子结点的值包涵在叶子结点中,MySQL 非叶子结点只保存了对子结点的引用,没有保存真实的数据,所有真实的数据都是在叶子结点中
3.对于B+树而言,在相同书高的情况下,查找任意元素的时间复杂度都是一样的,性能均衡。

MySQL的页

.ibd 文件中最重要的结构体是 Page(页),页是内存与磁盘交互最小单元,默认大小是 16KB

在这里插入图片描述

每一个.ibd 文件由页组成
在这里插入图片描述

每次内存与磁盘的交互至少读取一页,所以在磁盘中每个页内部的地址都是连续的,之所以这么做,是因为在使用数据的过程中,根据局部性原理,将来要使用的数据大概率与当前访问的数据在空间上是临近的所以一次从磁盘中读取一页的数据放入内存中,当下次查询的数据还在这个页中的时候,就可以直接从内存中读取,从而减少磁盘IO,以此来提高性能。

局部性原理:
是指程序在执行时呈现出局部性规律,在一段时间内,整个程序的执行仅限于程序中的某⼀部
分。相应地,执行所访问的存储空间也局限于某个内存区域,局部性通常有两种形式:时间局部
性和空间局部性。
时间局部性(Temporal Locality):如果⼀个信息项正在被访问,那么在近期它很可能还会被再
次访问。
空间局部性(Spatial Locality):将来要用到的信息大概率与正在使用的信息在空间地址上是临
近的。

每一个页中即使没有数据也会使用 16KB 的存储空间,同时与索引的B+树中的节点对应

我们可以查询MySQL中页的大小:

在这里插入图片描述
16384 / 1024 = 16 KB

数据页

数据库由很多种的页,索引页、数据页等等,这里我们讨论数据页:

页头页尾

数据页由页头和页尾:
在这里插入图片描述

这里我们关注的是页头里包含上一页页号和下一页页号,通过这两个属性可以把页与与之间链接起来,形成一个双向链表。


页主体

页主体部分是保存真实数据的主要区域,每当创建一个新页时,都会自动分配两个行,一个是页内最小行Infinum,另一个是页内最大行Supremun,这两行并不存储任何真实数据,而是最为数据行链表的头和尾【你可以理解为链表的两个空节点,一个是空的头节点,另一个是空的尾节点】

第一个数据行有一个记录下一行的地址偏移量的区域next_record将页内所有的数据行组成了一个单向链表
在这里插入图片描述

当向一个新页插入数据时,将Infimun 连接第一个数据行,最后一行真实数据连接Supremun,这样数据行就构成了一个单向链表,当更多的数据行插入之后,会按照主键从小到大的顺序进行连接:
在这里插入图片描述

页目录

看到这里,大家是不是又疑惑,单向链表的查找效率不是很慢吗?时间复杂度是O(N),当然开发数据库的人也想到了,为了提高查找效率,InnoDB采用二分查找来解决查询效率问题,于是他们开发了页目录:

在这里插入图片描述

具体的实现方式是,在每一个页中加入一个叫做页目录的结构,将页内包括最大行和最小行在内的数据行进行分组,这里单独约定最小行独自为一组,其他分组最多8条数据,同时将最大行放在最后一个分组中,按照主键从小到大的顺序记录在页目录中,页目录每一个位置称为槽,每一个槽对应一个分组,一旦分组中数据行超过8个的时候,就会分裂出一个新的分组

在后续查找某条数据行时,通过二分查找,就可以找到对应的槽,然后在槽对应的分组中进行最多8条数据的便即可获得目标数据

数据页头

数据页头记录了当前页保存数据相关的信息
在这里插入图片描述

B+树在数据库中的应用

非叶子节点保存索引数据,叶子节点保存真实数据

在这里插入图片描述

举个例子:查找 id 为7 的数据行,首先通过索引页1,发现 7 = 7 ,进入到索引页2,然后 7 < 9,进入到数据页4,最后找到id 为 7 的数据行,一共进行了三次磁盘IO。

以上的IO过程,加载索引页1–>加载索引页2–>加载数据页3

我们现在来感受B+树的强大:

假设一条数据大小为 1KB,忽略数据页中页头页尾等等非真实数据的内容,也就是说一个数据页可以保存16条数据。

索引页保存的是索引,这里我们以主键索引为例,索引页保存主键值和下一页的地址,主键用bigint 来保存,也就是8字节,地址为6字节,一共就是14字节,页的大小为16KB,那么一个索引页可以保存 16 * 1024 / 14 = 1170 条索引记录

如果B+树树高为三层的话,一条索引记录对应一个数据页,那么三层B+树就可以保存 1170 * 1170 * 16 = 21,902,400 条数据(第一层一个结点,有1170 条索引,那么第二层就有1170个结点,第二层每个结点都有1170条索引),也就是说如果是在两千多万条数据的表中,我们通过三次IO 就可以完成数据的检索了。

索引的分类

主键索引

当在表中定义了一个主键primary key 的时候,MySQL会将其作为索引,这个索引我们称之为主键索引。

我们推荐在每一张表都去创建一个主键,如果没有的话,可以添加一个自增列。

唯一索引

当在表中定义一个唯一键unique 时,MySQL会自动创建索引,这个索引被称之为唯一索引

普通索引

最基本的索引类型,没有唯一性的限制,也就是相比于唯一索引来说,可以存在重复的列

普通索引可以由多列组合组成,这时候可以称为复合索引。

普通索引是由用户自己手动创建的,如果用户觉得某个列查询比较频繁,可以考虑为这一列创建索引。

全文索引

基于文本列(char、varchar)上创建的索引,叫做全文索引。

全文索引可以加快对这列文本列包含的数据查询和DML操作,用于全文搜索,仅有MyISAM 和 InnoDB 引擎支持该索引。

聚集索引

与主键索引是同义词,也就是说主键索引就是聚集索引,也可以称为聚簇索引。

如果表中没有定义主键primary key ,InnoDB 会使用第一个 uniquenot null 的列作为聚集索引。

如果表中实在没有primary key 或者合适的 unique 索引,InnoDB 会为新生成的行生成一个行号为 6 字节的 ROW_ID 字段记录,ROW_ID 是单调递增的,并且使用 ROW_ID 作为索引。

注意ROW_ID 是MySQL内部的设置,用户是无法获取的,MySQL使用这个索引是为了更好地创建索引树来管理数据。

非聚集索引

非聚集索引(也可以称为二级索引),就是除了聚集索引以外的索引就是非聚集索引。

二级索引中的每条记录都包含改行的主键列,以及二级索引指定的列。

InnoDB 会使用这个主键值来搜索聚集索引中的行,这个过程称为回表查询

索引覆盖

当一个 select 语句使用了普通索引且查询列表中的列刚好是创建普通索引时的所有或部分列,这时候就可以直接返回数据,就不用进行回表查询,这样的现象被称为索引覆盖


简单解释一下:聚集索引创建的索引树是包含完整的真实数据的,非聚集索引创建的索引树是没有包含完整的真实数据但是包含对应的主键值,如果通过非聚集索引查询到的数据不满足用户要求会通过这个主键值来到主键索引树获取完整的数据,这个现象就是回表查询,也就是查了两个索引树

如果通过非聚集索引的索引树能获取到目标数据,就不需要进行回表查询,直接返回数据即可,这就是索引覆盖

举个例子:我们有一张学生表,包含学生的 id ,姓名,班级,其中定义学生的 id 为主键值,姓名字段定义为普通索引,现在我要查询张三的所有信息:

由于姓名就是索引,通过张三到姓名的索引树中查找,随后只能获得主键值id + 姓名为张三的数据,但是我们要的是张三所有的数据,所以数据库就会通过主键值到主键索引树去查找张三的完整的数据,这就是回表查询

如果我们至少想查张三的姓名:select name from student where name = '张三'; 这时废话sql ,至少作为演示,数据库通过姓名这个索引树就可以得到张三这个名字,直接返回数据,不需要进行回表查询,这就是索引覆盖

索引的使用

自动创建

当表中定义了主键、外键、唯一键,MySQL都会为其创建对应的索引以及索引树。

如果表中没有任何约束,MySQL回自动为每一列生成一个索引并且使用ROW_ID 进行标识

手动创建

主键索引

  1. 在创建表时,直接在字段后面定义主键:
create table test0 (
	id bigint primary key auto_increment,
	name varchar(50)
);
  1. 在创建表时,先定义好字段,最后定义主键
create table test0 (
	id bigint auto_increment,
	name varchar(50),
	primary key(id)
);
  1. 创建表之后,我们可以通过修改表来定义主键
create table test0 (
	id bigint,
	name varchar(50)
);

alter table test0 add primary key(id);

如果你还想让主键自增,还可以在写下面的语句:

alter table test0 modify id bigint auto_increment;

modify 后面要跟完整的字段定义语句。

唯一索引

  1. 在创建表时在定义字段的同时,直接定义唯一键unique
create table test1 (
	id bigint,
	name varchar(50) unique
);
  1. 创建表时,在定义完字段后,单独定义唯一键unique
create table test1 (
	id bigint,
	name varchar(50),
	unique(name)
);
  1. 在创建表后可以通过修改表的字段来定义唯一键
create table test1 (
	id bigint,
	name varchar(50)
);

alter table test1 add unqiue(name);

普通索引

  1. 创建表时,再创建完字段后,定义普通索引index
create table test2 (
	id bigint,
	name varchar(50),
	index(name)
);

通过查看表结构,我们得知普通索引的标记为MUL

在这里插入图片描述

  1. 创建完表后,可以通过修改表的方式添加普通索引index
create table test2 (
	id bigint,
	name varchar(50)
);

alter table test2 add index(name);
  1. 再创建表后,可以通过 create 语句手动创建普通索引

语法形式:create index index_name on table_name(字段名);

index_name 表示索引名,也就是说你可以自己命名索引。

推荐索引命名为 ind_表名_字段名,这样我们在查询索引的时候,可以得知这是在哪张表的普通索引,并且是哪个字段。

create table test2 (
	id bigint,
	name varchar(50)
);

create index ind_test2_name on test2(name);

复合索引

和创建普通主键一样,只是多加一些字段名。这里直接上sql 语句

create table test3(
	id bigint primary key auto_increment,
	sn bigint unique,
	name varchar(50) not null,
	class_name varchar(20) not null,
	index(sn,name)
);
create table test3(
	id bigint primary key auto_increment,
	sn bigint unique,
	name varchar(50) not null,
	class_name varchar(20) not null
);

alter table test3 add index (sn,name);
create table test3(
	id bigint primary key auto_increment,
	sn bigint unique,
	name varchar(50) not null,
	class_name varchar(20) not null
);

create index ind_test3_sn_name on test3(sn,name);

注意事项

索引的创建应该建立在高频的查询列上,并且数据量比较大,因为数据量小尽管这个是高频查询的列,但是全表扫描会比索引树要更快一些。

索引需要占用额外的空间,每创建一个索引都会创建对应的索引树。

对于表进行插入、删除、更新操作的时候,由于同时会修改索引树,可能会影响到性能。

创建过多或者不合理的索引会导致性能的下降,所以我们要谨慎创建索引。

查看索引

有三种方式查看索引:方式一:show keys from table_name;

在这里插入图片描述

我们来看一下上面的表格,Table 表示表名,
Non_unique 表示是否不唯一 :0 表示否(唯一)1表示是(不唯一)
Seq_in_index 表示在索引中的标号,从 1 开始排号,test3 有一个复合索引(sn, name),所以 name 标号为 2
Column_name 表示对应的字段名
Key_name 表示索引名,主键索引默认索引名为 PRIMARY
Index_type 表示索引的数据结构,这里显示BTREE,说明是B树,其实就是B+树,B+树是B树的变形。

方式二:show index from table_name;

获得的结果集和上面是一样的:
在这里插入图片描述

方式三:desc table_name; 这是简单查询索引:

在这里插入图片描述

删除索引

删除主键索引:alter table table_name drop primary key;
在这里插入图片描述
如果发生上面的报错信息,是因为这个主键值带有自增属性,我们要先删除其自增的属性,然后才能进行删除主键索引的操作:

alter table test3 modify id bigint;

在这里插入图片描述

删除了自增属性后,可以进行主键索引的删除:alter table test3 drop primary key;

在这里插入图片描述


删除其他索引:alter table table_name drop index 索引名;

在这里插入图片描述

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

小结

创建索引:主键索引(primary key)
唯一索引(unique)
普通索引(index)

创建索引可以在创建表中定义,也可以在表外创建

表外创建索引:alter table table_name add primary key / unique / index 字段名; 还有 普通索引的专属创建形式create index 索引名 on table_name(字段名,字段名...);

修改表的字段:alter table table_name modify 字段定义语句; 通过字段定义语句就可以修改对应的字段属性了。

查看索引的方法:show keys/index from table_name 或者简单查看索引desc table_name;
简单查看索引的结果集的Key: PRI 表示主键索引,UNI表示唯一索引,MUL 表示普通索引

删除索引:alter table table_name (primary key) / (index 索引名);

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PCIe进阶之TL:First/Last DW Byte Enables Rules Traffic Class Field

1 First/Last DW Byte Enables Rules & Attributes Field 1.1 First/Last DW Byte Enables Rules Byte Enable 包含在 Memory、I/O 和 Configuration Request 中。本文定义了相应的规则。Byte Enable 位于 header 的 byte 7 。对于 TH 字段值为 1 的 Memory Read Request…

Requests-HTML模块怎样安装和使用?

要安装和使用Requests-HTML模块&#xff0c;您可以按照以下步骤进行操作&#xff1a; 打开命令行界面&#xff08;如Windows的命令提示符或Mac的终端&#xff09;。 使用pip命令安装Requests-HTML模块。在命令行中输入以下命令并按回车键执行&#xff1a; pip install request…

前端网页代码编辑器 Monaco Editor

前端网页代码编辑器 Monaco Editor Monaco Editor Monaco Editor 是由 Microsoft 开发的一款基于 Web 技术的开源代码编辑器&#xff0c;它是 Visual Studio Code 编辑器的核心。Monaco Editor 可以嵌入到网页中&#xff0c;提供类似于 Visual Studio Code 的编辑体验。 官方…

数据结构 Java DS——分享部分链表题目 (2)

目录 前言 题目一 —— 链表的回文结构 题目二 —— 二进制链表转整数 题目三 —— 设计链表 结尾 前言 关于JAVA的链表,笔者已经写了两篇博客来介绍了,今天给笔者们带来第三篇,也是分享了一些笔者写过的,觉得挺好的题目,链接也已经挂上了,笔者们可以去看看…

redis 基本数据类型—string类型

一、介绍 Redis 中的字符串&#xff0c;直接就是按照二进制数据的方式存储的&#xff0c;不会做任何的编码转换。 Redis对于 string 类型&#xff0c;限制了大小最大是512M 二、命令 SET 将 string 类型的 value 设置到 key 中。如果 key 之前存在&#xff0c;则覆盖&#…

亚马逊、沃尔玛、敦煌网、Target塔吉特、Temu环境搭建测评技术!

海外跨境电商各大主要平台正不断力推半托管模式&#xff0c;不断对商家开出众多吸引和扶持政策。全托管是指电商平台全面负责店铺的运营&#xff0c;包括仓储、配送、售后等&#xff0c;而商家主要负责提供货品。半托管模式则基本由商家自主经营&#xff0c;平台只负责仓配物流…

java中Class文件的文件格式

无关性的基石 计算机底层只能识别二进制&#xff0c;由CPU直接处理二进制&#xff0c;在底层上面是操作系统&#xff0c;在操作系统上面就是虚拟机&#xff0c;java有一个口号&#xff0c;“一次编写&#xff0c;到处运行”这个不太可能在操作系统层面上实现&#xff0c;不同的…

俄罗斯方块——C语言实践(Dev-Cpp)

目录 1、创建项目(尽量不使用中文路径) 2、项目复制 3、项目配置 ​1、调整编译器 2、在配置窗口选择参数标签 3、添加头文件路径和库文件路径 4、代码实现 4.1、main.c 4.2、draw.h 4.3、draw.c 4.4、shape.h 4.5、shape.c 4.6、board.h 4.7、board.c 4.8、cont…

Vue.js入门系列(二十九):深入理解编程式路由导航、路由组件缓存与路由守卫

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

解锁编程潜力,从掌握GitHub开始

目录&#xff1a; 一、搜索开源项目 1、什么是Git 2、Github常用词含义 3、一个完整的项目界面 4、使用Github搜索项目 1&#xff09;in关键词 2&#xff09;star或fork数量去查找 3&#xff09;awesome加强搜索 二、访问速度慢的解决 1、使用网易UU加速器 2、使用…

Visual Studio(vs)下载安装C/C++运行环境配置和基本使用注意事项

基本安装 点击跳转到vs官网点击箭头所指的按钮进行下载双击运行刚才下载好的下载器点击继续勾选“使用C的桌面开发”和“Visual Studio扩展开发”点击“安装位置”&#xff0c;对vs的安装位置进行更改。你可以跟我一样只选择D盘或者其他你空闲的盘&#xff0c;然后将默认的路径…

响应式CSS 媒体查询——WEB开发系列39

CSS媒体查询&#xff08;Media Queries&#xff09;是响应式设计中的核心技术之一&#xff0c;帮助我们在不同设备上展示不同的样式。通过媒体查询&#xff0c;开发者可以检测用户设备的特性&#xff0c;如屏幕宽度、高度、分辨率、方向等&#xff0c;针对性地调整网页布局。 一…

架构师知识梳理(七):软件工程-工程管理与开发模型

软件工程概述 软件开发生命周期 软件定义时期&#xff1a;包括可行性研究和详细需求分析过程&#xff0c;任务是确定软件开发工程必须完成的总目标&#xff0c;具体可分成问题定义、可行性研究、需求分析等。软件开发时期&#xff1a;就是软件的设计与实现&#xff0c;可分成…

气压测试实验(用IIC)

I2C: 如果没有I2c这类总线&#xff0c;连接方法可能会如下图&#xff1a; 单片机所有的通讯协议&#xff0c;无非是建立在引脚&#xff08;高低电平的变换高低电平持续的时间&#xff09;这二者的组合上&#xff0c;i2c 多了一个clock线&#xff0c;负责为数据传输打节拍。 (i2…

如何删除git提交记录

今天在提交github时&#xff0c;不小心提交了敏感信息&#xff0c; 不要问我提交了啥&#xff0c;问就是不知道 查了下资料&#xff0c;终于找到简单粗暴的方式来删除提交记录。方法如下 git reset --soft HEAD~i i代表要恢复到多少次提交前的状态&#xff0c;如指定i 2&…

一文读懂:如何将广告融入大型语言模型(LLM)输出

本文是我翻译过来的&#xff0c;讨论了在线广告行业的现状以及如何将大型语言模型&#xff08;LLM&#xff09;应用于在线广告。 原文请参见”阅读原文“。 在2024年&#xff0c;预计全球媒体广告支出的69%将流向数字广告市场。这个数字预计到2029年将增长到79%。在Meta的2024…

微服务——网关路由(Spring Cloud Gateway)

网关路由 1.什么是网关 网关又称网间连接器、协议转换器&#xff0c;是在网络层以上实现网络互连的复杂设备&#xff0c;主要用于两个高层协议不同的网络之间的互连。网关就是网络的关口。数据在网络间传输&#xff0c;从一个网络传输到另一网络时就需要经过网关来做数据的路由…

Kafka 基础与架构理解

目录 前言 Kafka 基础概念 消息队列简介&#xff1a;Kafka 与传统消息队列&#xff08;如 RabbitMQ、ActiveMQ&#xff09;的对比 Kafka 的组件 Kafka 的工作原理&#xff1a;消息的生产、分发、消费流程 Kafka 系统架构 Kafka 的分布式架构设计 Leader-Follower 机制与…

安卓玩机工具-----无需root权限 卸载 禁用 删除当前机型app应用 ADB玩机工具

ADB玩机工具 ADB AppControl是很实用的安卓手机应用管理工具&#xff0c;无需root权限&#xff0c;通过usb连接电脑后&#xff0c;可以很方便的进行应用程序安装与卸载&#xff0c;还支持提取手机应用apk文件到电脑上&#xff0c;此外还有手机系统垃圾清理、上传文件等…

VMware Workstation Player虚拟机Ubuntu启用Windows共享目录

1、新建共享目录 2、安装并启用vmtools、fuse sudo apt update sudo apt install open-vm-tools open-vm-tools-desktop sudo apt install fuse sudo systemctl start open-vm-tools sudo systemctl enable open-vm-tools 3、命令挂载 sudo vmhgfs-fuse .host:/SharedFold…