当前位置: 首页 > news >正文

MySQL 索引:结构、对比与操作实践指南

MySQL系列


文章目录

  • MySQL系列
  • 前言
  • 案例
  • 一、认识MySQL与磁盘
    • 1.1 MySQL与存储
    • 1.2 MySQL 与磁盘交互基本单位
  • 二、 MySQL 数据交互核心:BufferPool 与 IO 优化机制
  • 三、索引的理解
    • 3.1 测试案例
    • 3.2 page
    • 3.3 页目录
    • 3.3 对比其他结构
  • 四、聚簇索引 VS 非聚簇索引
  • 五、索引操作
    • 5.1 创建主键索引
    • 5.2 唯一索引的创建
    • 5.3 普通索引的创建
    • 5.4 查询索引
    • 5.5 删除索引


前言

上一篇:MySQL 函数大赏:聚合、日期、字符串等函数剖析

在MySQL数据库中,索引是一种特殊的数据结构,它与表中数据关联,就像书籍的目录与正文的关系——目录通过章节标题和页码快速定位内容,而索引则通过存储数据的关键列值及其对应物理位置,帮助数据库快速定位目标数据。


本篇文章以主流的InnoDB引擎为例,展开介绍

案例

在MySQL中操作存储大量数据的表时,我们往往会面临处理数据慢,性能低下等问题,这时只需要建立索引就可以将这种问题优化。

在这里插入图片描述

在这里插入图片描述
从操作执行时间不难看出,建立索引给我们操作带来的巨大提升。

在学习索引是如何优化MySQL的性能之前,需要先知道MySQL为什么存在性能方面的问题。

一、认识MySQL与磁盘

1.1 MySQL与存储

MySQL 给用户提供数据存储服务的,早在之前我就介绍过,MySQL使用的库、表对数据存储,在Linux下都表现为特殊结构的文件,要想对数据进行持久化保存,这些文件最终都要存储在磁盘中,所有MySQL下数据存储在磁盘这个外设当中,相比于计算机其他电子元件,磁盘效率是比较低的,在加上IO本身的特征,可以知道,如何提交效率,是 MySQL 的一个重要话题。

Linux文件系统
这篇文章中详细介绍了系统对磁盘的访问,强烈建议看一下,这里就不介绍了

1.2 MySQL 与磁盘交互基本单位

MySQL 作为一款应用软件,可以想象成一种特殊的文件系统,它有着更多的IO需求,而IO操作会大大影响执行效率,所以,为了提高基本的IO效率, MySQL 进行IO的基本单位是 16KB

磁盘这个硬件设备的基本单位是 512 字节,操作系统在和磁盘交互时以4KB为单位,而 MySQL InnoDB引擎使用 16KB 和内存进行IO交互。所以可以理解为, MySQL 和磁盘进行数据交互的基本单位是 16KB 。这个基本数据单元,在 MySQL 这里叫做page(注意和系统的page区分)。

 SHOW GLOBAL STATUS LIKE 'innodb_page_size';

在这里插入图片描述
使用这个sql语句可以查找引擎页大小

二、 MySQL 数据交互核心:BufferPool 与 IO 优化机制

通过前文介绍可知,MySQL 中的数据文件在磁盘上以 page(16KB) 为单位存储。当执行 CURD(增删改查)操作时,MySQL 需先通过计算定位目标数据的位置——这一过程依赖 CPU 参与,而 CPU 只能直接操作内存数据。因此,数据必须先从磁盘临时加载到内存中,形成“磁盘一份、内存一份”的临时状态。待内存中的数据操作完成后,再通过特定策略将更新同步回磁盘,这一过程即涉及磁盘与内存的交互(IO),而 IO 的基本单位正是 page

为高效管理内存中的数据、减少频繁的磁盘 IO,MySQL 服务器在启动时会在内存中申请一块专用的大内存区域,称为 BufferPool(缓冲池)。它的核心作用是:

  • 缓存热点数据:将频繁访问的 page 临时存储在内存中,避免每次操作都直接读写磁盘(局部性原理:当你对某一块数据操作时,你的下一次操将有很大概率,会使用后面的数据)。
  • 优化 IO 效率:所有数据操作先在 BufferPool 中完成,操作完成后同步到磁盘,大幅减少磁盘 IO 次数(不可以操作一行,就获取一行)。

因此,减少系统与磁盘的 IO 次数是提升 MySQL 效率的核心原则,而 BufferPool 正是实现这一目标的关键机制——它通过内存缓存降低了磁盘 IO 对性能的影响。

三、索引的理解

3.1 测试案例

建立测试表
create table if not exists user (
id int primary key, --一定要添加主键哦,只有这样才会默认生成主键索引
age int not null,
name varchar(16) not null
);
插入多条记录,注意此处数据的主键顺序
insert into user (id, age, name) values(3, 18, '杨过');insert into user (id, age, name) values(4, 16, '小龙女');insert into user (id, age, name) values(2, 26, '黄蓉');insert into user (id, age, name) values(5, 36, '郭靖');insert into user (id, age, name) values(1, 56, '欧阳锋');

在这里插入图片描述
可以发现MySQL会将插入的数据默认变为有序,那么这样做有什么好处呢?
排序插入是为了优化查询效率

具体形式后面介绍

3.2 page

我们目前可以简单理解一个独立表文件是由一个或者多个Page构成的,那么这个表该如何管理这些page呢?
在这里插入图片描述
不同的 Page ,在 MySQL 中,都是 16KB ,使用 prevnext 构成双向链表(像这种结构在学习Linux时,我们经常遇到),因为有主键的问题, MySQL 会默认按照主键给我们的数据进行排序,从上面的Page内数据记录可以看出,数据是有序且彼此关联的。

页内部存放数据的模块,实质上是一个链表的结构,链表的特点也就是增删快,查询修改慢,所以优化查询的效率是必须的,正是因为有序,在查找的时候,从头到后都是有效查找,没有任何一个查找是浪费的(这一点在后面感受)

通过页模式,MySQL 查询时会将一整页数据(16KB)加载到内存,以此减少硬盘 IO 次数、提升性能。

但页模式内部采用链表结构,本质上需通过逐条数据比较定位目标。若表数据量大,且目标数据位于最后一个 page 的最后一条,仍需遍历全表,导致查找速度过慢。
在这里插入图片描述

3.3 页目录

在课本中查找知识点时,我们会选择优先查看目录,找到具体的页,再从页中查找知识点,这样的查找效率要比从头开始找,高效的多。在这里每一个独立的page或每一行数据,都可以视为“页”,而我们要做的是,给这些“页”添加属于他们的目录。
page内部:
在这里插入图片描述
现在,要在一个Page内部,查找id=3记录,直接通过目录2[3],直接进行定位新的起始位置,提高了效率。现在我们可以再次正式回答上面的问题了,为何通过键值 MySQL 会自动排序?可以很方便引入目录,提高查找效率

图中是为了迎合上面的数据,在实际情况下目录间的区间是很大的,在进行目录查找时,一次查找可以pass掉很多数据。

多page情况:
单表数据不断被插入的情况下, MySQL 会在容量不足的时候,自动开辟新的Page来保存新的数据,然后通过指针的方式,将所有的Page组织起来。

上面的方法帮我们提高了表内部遍历数据的效率,但是仍需要将每个page都,加载值内存中,为了进一步减少IO操作,我们采用页目录的方式
在这里插入图片描述
依照这个思路,我们还可以对目录页再次添加目录管理,现在可以得出结论:

  • Page分为目录页和数据页。目录页只放各个下级Page的最小键值。
  • 查找的时候,自定向下找,只需要加载部分目录页到内存,即可完成算法的整个查找过程,大大减少了IO次数
    这个结构最终就是一颗B+树,整个过程中我们所要IO的次数,就是整个结构数的高度

目录页的本质也是页,普通页中存的数据是用户数据,而目录页中存的数据是普通页的地址。

3.3 对比其他结构

InnoDB 在建立索引结构来管理数据的时候,其他数据结构为何不行?

  • 链表?线性遍历
  • 二叉搜索树?退化问题,可能退化成为线性结构
  • AVL &&红黑树?虽然是平衡或者近似平衡,但是毕竟是二叉结构,相比较多阶B+树,意味着树整体过高,大家都是自顶向下找,层高越低,意味着系统与硬盘更少的IO Page交互。
  • Hash?官方的索引实现方式中, MySQL 是支持HASH的,不过 InnoDB 和 MyISAM 并不支持Hash跟进其算法特征,决定了虽然有时候也很快(O(1)),不过,在面对范围查找就明显不行。

B树?最值得比较的是 InnoDB 为何不用B树作为底层索引?
B树节点,既有数据,又有Page指针,而B+,只有叶子节点有数据,其他目录页,只有键值和Page指针B+,叶子节点,全部相连,而B没有,为何选择B+
节点不存储data,这样一个节点就可以存储更多的key。可以使得树更矮,所以IO操作次数更少。叶子节点相连,更便于进行范围查找

具体结构特征,你可以搜点图片理解

四、聚簇索引 VS 非聚簇索引

MyISAM 引擎同样使用B+树作为索引结果,叶节点的data域存放的是数据记录的地址。下图为MyISAM表的主索引,Col1 为主键
在这里插入图片描述
其中,MyISAM最大的特点是,将索引Page和数据Page分离,也就是叶子节点没有数据,只有对应数据的地址相较于InnoDB索引,InnoDB是将索引和数据放在一起的。
现在我们就可以回答,第一篇文章遗留的问题了

innodb引擎
create table itest(
id int primary key,
name varchar(11) not null
)engine=InnoDB;
MyISAM引擎
create table mtest(
id int primary key,
name varchar(11) not null
)engine=MyISAM;

在这里插入图片描述

MyISAM 这种用户数据与索引数据分离的索引方案,叫做非聚簇索引,InnoDB 这种用户数据与索引数据在一起索引方案,叫做聚簇索引。

MySQL 除了默认会建立主键索引外,我们用户也有可能建立按照其他列信息建立的索引,一般这种索引可以叫做辅助(普通)索引。对于MyISAM ,建立辅助(普通)索引和主键索引没有差别,无非就是主键不能重复,而非主键可重复。
下图就是基于 MyISAM 的 Col2 建立的索引,和主键索引没有差别
在这里插入图片描述
同时我们以上表中的 Col3 建立对应的辅助索引如下图:
在这里插入图片描述
MyISAM 的非主键索引中叶子节点并没有数据,而只有对应记录的key值。
所以通过辅助(普通)索引,找到目标记录,需要两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。这种过程,就叫做回表查询为何MyISAM 针对这种辅助(普通)索引的场景,不给叶子节点也附上数据呢?原因就是太浪费空间了。
那么普通索引为什么要存在呢?
当我们以符合间进行索引时如:(姓名,qq),只知道第一个键值,需要查找第二个键值,我们就可以直接查找:
在这里插入图片描述
对于复合索引,匹配原则是,从做到右的,也就是说,我们只需要知道姓名,就可以得到qq,这种普通索引方式,要比主键索引更快。

五、索引操作

5.1 创建主键索引

// 在创建表的时候,直接在字段名后指定 primary key
create table user1(id int primary key, name varchar(30));//在创建表的最后,指定某列或某几列为主键索引
create table user2(id int, name varchar(30), primary key(id));//创建表以后再添加主键
create table user3(id int, name varchar(30));
alter table user3 add primary key(id);

主键索引的特点:

  • 一个表中,最多有一个主键索引,当然可以使符合主键
  • 主键索引的效率高(主键不重复)
  • 创建主键索引的列,它的值不能为null,且不能重复
  • 主键索引的列基本上是int

这些特点,在介绍主键时都说过,只是当时没有提出索引的概念

5.2 唯一索引的创建

//在表定义时,在某列后直接指定unique唯一属性。
create table user4(id int primary key, name varchar(30) unique);//创建表时,在表的后面指定某列或某几列为unique
create table user5(id int primary key,name varchar(30), unique(name));//创建表以后再添加
create table user6(id int primary key, name varchar(30);
alter table user6 add unique(name);

唯一索引的特点

  • 一个表中,可以有多个唯一索引
  • 查询效率高
  • 如果在某一列建立唯一索引,必须保证这列不能有重复数据
  • 如果一个唯一索引上指定not null,等价于主键索引

5.3 普通索引的创建

 //在表的定义最后,指定某列为索引
create table user8(id int primary key,
name varchar(20),
email varchar(30),
index(name)
);//创建完表以后指定某列为普通索引
create table user9(id int primary key, name varchar(20),email varchar(30));
alter table user9 add index(name); //创建一个索引名为 idx_name 的索引
create table user10(id int primary key, name varchar(20),email varchar(30));create index idx_name on user10(name);

普通索引的特点:

  • 一个表中可以有多个普通索引,普通索引在实际开发中用的比较多
  • 如果某列需要创建索引,但是该列有重复的值,那么我们就应该使用普通索引

5.4 查询索引

show keys from 表名;show index from 表名;

在这里插入图片描述

5.5 删除索引

//删除主键索引 
alter table 表名 drop primary key;//其他索引的删除
alter table 表名 drop index 索引名; drop index 索引名 on 表名

在这里插入图片描述
余下指令你自己测试吧

http://www.dtcms.com/a/351790.html

相关文章:

  • 【合适新人】预测图片教程——如何随机抽取验证集图片进行可视化推理!(附完整代码)
  • DigitalOcean GPU 选型指南(三):中端AI GPU性价比之王 RTX 4000 Ada、A4000、A5000
  • 无人机航拍数据集|第33期 无人机树冠目标检测YOLO数据集5842张yolov11/yolov8/yolov5可训练
  • 【HZ-T536开发板免费体验】无需死记 Linux 命令!用 CangjieMagic 在 HZ-T536 开发板上搭建 MCP 服务器,自然语言轻松控板
  • Java大厂面试全真模拟:从Spring Boot到微服务架构实战
  • 文本转语音TTS工具合集(下)
  • 【强化学习】区分理解: 时序差分(TD)、蒙特卡洛(MC)、动态规划(DP)
  • 计算机底层硬件实现及运行原理通俗书籍推荐
  • 记一次MySQL数据库的操作练习
  • 把 AI 塞进「空调遥控器」——基于 MEMS 温湿阵列的 1 分钟极速房间热场扫描
  • 如何获取当前页面html元素的外层容器元素
  • vscode或者cursor配置使用Prettier - Code formatter来格式化微信小程序wxss/wxs/wxml文件
  • Vue Flow 设计大模型工作流 - 自定义大模型节点
  • 基于XiaothinkT6语言模型的文本相似度计算:轻量方案实现文本匹配与去重
  • 乳腺癌数据集支持向量机实践学习总结
  • 2025最新的软件测试热点面试题(答案+解析)
  • OnlyOffice 渲染时间获取指南
  • from中烟科技翼支付 面试题2
  • 项目集升级:顶部导览优化、字段自定义、路线图双模式、阶段图掌控、甘特图升级、工作量优化、仪表盘权限清晰
  • 用大语言模型提升语音翻译:一种全新的端到端方法
  • vue2+elementui 表格单元格增加背景色,根据每列数据的大小 颜色依次变浅显示2
  • 「大模型学习」(15)Prompt Tuning → P-Tuning v1 → P-Tuning v2
  • (论文速读)Prompt Depth Anything:让深度估计进入“提示时代“
  • 6.5 el-tree 组件
  • 用大语言模型实现语音到语音翻译的新方法:Scheduled Interleaved Speech-Text Training
  • Research相关的面试(个人)
  • 云服务器的作用
  • yggjs_rbutton React按钮组件v1.0.0 API 参考文档
  • linux、window java程序导出pdf\word、excel文字字体显示异常、字体样式不一样
  • 【lucene】spancontainingquery