【MySQL】索引的原理及其使用

文章目录

  • 什么叫索引
  • 减少磁盘IO次数
  • 缓存池(Buffer Pool)
  • MySQL的页
    • 页内目录
    • 页目录
  • 正确理解索引结构
    • 为什么Innodb的索引是B+树结构
    • 各种存储引擎支持的索引
    • 聚簇索引和非聚簇索引
    • 索引类型
  • 关于索引的操作
    • 创建主键索引
    • 唯一索引的创建
    • 普通索引的创建
    • 查看索引
    • 删除索引

什么叫索引

MySQL中的索引是一种用于快速查找记录的数据结构。它类似于一个目录,可以加速数据检索的速度。在处理较大数据时一般都会建立先对应的索引来帮助查找。

对于以上索引的定义,我们要搞懂几个地方:

  1. 为什么建立索引能便于快速查找?
  2. 这种数据结构是什么?
  3. 索引的类型有哪些?分别有什么用?

本篇文章将围绕以上问题展开叙述。

减少磁盘IO次数

在前面的学习中我们知道,数据库其实就是一个目录文件,而数据库中的表其实就是目录下的一个个文件。而文件中的数据是被存放在磁盘中的,当我们要检索表中的数据时,首先得从磁盘中读取数据,又由于磁盘IO的大小单位是固定的(块,4KB),于是检索数据的效率跟磁盘IO的次数成反比。也就是说,磁盘IO次数越多,检索数据的时间消耗就越高

关于文件系统IO的原理在我之前的文章中有讲到,感兴趣的可以去看看。

缓存池(Buffer Pool)

为了减少跟磁盘IO的次数,MySQL使用了缓存池的概念,即开辟一块大小固定的内存空间,用于存储数据页和索引页,当查询数据时,优先访问缓存池中的数据,如果缓冲池有则直接从从该池中读取,如果没有再去内核级缓冲区和磁盘中去找。缓冲池其实就是一个软件与操作系统之间的一个用户级缓冲区。这种机制能够有效的减少磁盘IO次数。缓存池除了用作数据缓存,还有以下作用:

  • 延迟写:当数据页被修改时,修改首先写入到缓存池中,而不是立即写入磁盘,Innodb会定期将这些修改后的数据页写回磁盘。这样减少了在大量写操作的情况下磁盘IO的次数。
  • 预读机制:缓冲池会提前加载需要的数据页,进一步减少查询时的磁盘IO延迟。

值得注意的是,不同的存储引擎之间的内存管理机制可能会有不同。由于mysql默认的·存储引擎是innodb,所以本篇文章默认mysql的存储引擎是innodb。

上面说缓存池中存储的是数据页和索引页,那页的大小是多少呢?

MySQL的页

MySQL作为一个数据库服务软件,它有着较高的IO场景(大多时候我们都在查询数据),所以为了保证一定的IO效率,MySQL进行IO的基本单位是page(innodb存储引擎是16KB)。注意,这里的IO是指mysql这个软件与内核级缓冲区之间的IO,而磁盘IO是磁盘和内核级缓冲区之间的IO。不同存储引擎的默认页大小可能存在差异。

为什么将页设置为16KB能减少IO次数呢?

16kb也就是4个数据块的大小。根据局部性原理,当访问到某一个数据块时,其周围的数据块被访问的概率会上升,一次性多读几个连续的块,可以有效减少下次磁盘IO的可能性。

那为什么是16kb而不是更多呢?16KB的页大小是一个经过多年实践验证的设置,MySQL和InnoDB开发团队已经对其进行了大量优化,确保其在各种应用场景下都能提供良好的性能和稳定性。

下面来看一个page的内容:
在这里插入图片描述
不同的page在mysql中都是16kb,其内部有prev和next两个指针构成双向链表。

一个表文件可能需要诺干个page来存储数据,于是,对数据库中表的管理就变成了对page的管理了。值得注意的是,MySQL 会默认按照主键给我们的数据进行排序。当数据量比较大时,一定需要多个page来存储数据,多个page之间通过指针连接起来,形成一个双链表。当我们要查询某一条记录,没有索引的情况下只能线性查找,这样的效率无疑是非常低下的。

类似于内存管理中的页目录机制,mysql的page也可以通过建立表目录来提高查找page的效率。

页内目录

对于page内部来说,16kB的大小已经可以存很多记录了,要想在一个page中找到目标记录,我们就需要线性遍历整个page,这样的效率太低了,更何况还会有要找多个目标记录的情况。于是在page内部mysql也设置了一个目录,用于page内部查找记录。其大致结构如下:
在这里插入图片描述
将一个page中所有数据记录分成诺干个组,每个目录项的的内容都是一个指向对应组的指针。当我们要查询一个id为4的记录时(假设主键是id),直接扫描目录,对比目录指向记录的id,看是否在其范围内。这样我们就只需要遍历目录就好了。例如上图id=4,属于目录2【3】之后,目录3【5】之前。所以id为4的记录一定在目录2指向的组中。这样分组一定是需要顺序的,而这种顺序是靠主键来维持的。如果一个表中没有手动设置主键,那么innodb会选择一个非空唯一键来排序,如果非空唯一键也没有,那么innodb会设置一个隐藏的属性来排序,我们无需关心。

下面sql样例能证明mysql会根据主键自动排序:
在这里插入图片描述
这样一来,排序之后的记录建立的目录就能通过比较主键来确定要查找的数据在哪一个目录里。而且,有了顺序之后,范围查找的效率也会快很多!只需要查找主键最大值和最小值得记录,中间的全部记录就是在这个范围之内。

页目录

随着数据量的不断增大,一个表文件所需的page也会越来越多,虽然在page内部我们能较快的查找到目标但是找到目标page本身就是一个麻烦事。难不成遍历整个双链表么?这样一来不还是线性查找么?遍历意味着依旧需要进行大量的IO,将下一个page加载到内存,进行线性检测。
在这里插入图片描述
于是,为了能够快速定位到目标page,mysql给page也加上了目录。注意这里得目录和page内部目录的区别。用一些page来充当目录,其内部不存放记录,而是存放指向其它page的指针!该目录具有以下特征:

  • 使用一个目录项来指向某一页,而这个目录项存放的就是将要指向的页中存放的最小数据的键值
  • 和页内目录不同的地方在于,这种目录管理的级别是页,而页内目录管理的级别是行
  • 其中,每个目录项的构成是:键值+指针

如下图所示:
在这里插入图片描述
这样一来,要想找到目标page我们只需要遍历页目录就可以了。

为了更好的理解页目录,我们可以假设这样一个例子:

现在一张学生表里有10000条记录,其主键是id,这10000条记录的id值从1-10000。我们将这1000条记录用100个page去存放数据。每个page就有100条记录,且每条记录之间的id值都是从小到大排好序的。

  • 对于page内部来说,将这100条数据依次分为10组,每组的首条记录的id值与下一组的首条记录的id值相差10,比如1、11、21、31等。页内目录的目录项指向每组首条记录假如要找52通过比较,该条记录在第5目录项和第6目录项之间,也就是在[51,61)之间,就去第5目录项指向的组中去找。对于任意一条记录找到目录项的次数不超过10次,在组中找不超过10次,加起来不超过20次就能在一个page内部找到一条记录
  • 对于page来说,将100个page同样依次分组,每组的page的首条记录的id值依次为1、1001、2001等一共也有10组,从前往后每组10个page。我们用一个page作为页目录,其目录项的键值依次为1、1001、2001等。一共有10个目录项。假设我们要找5005条记录,我们先遍历页目录,通过比较得知该条记录位于第5目录项和第6目录项之间,也就是[5001,6001),于是我们从第5组page中找。这样就能找到目标page。对于任意一条记录,从页目录找到page的比较次数不超过20次,从page内部找到该条记录的次数也不超过20次,于是在10000条记录里面,找到任意一条记录只需要比较最多40次就能找到了

类似的,面对更大的数据量,我们可以往上再次建立二级页目录等。如下图:
在这里插入图片描述
这玩意不就是B+树吗?上面说的这个结构跟索引有什么关系呢?其实,建立索引就是建立这种便于查找的数据结构!为什么这种数据结构能提高查找效率呢?借助缓冲池,自定向下找,只需要加载部分目录页到内存,即可完成算法的整个查找过程,大大减少了IO次数

正确理解索引结构

上面说的B+树是Innodb存储引擎管理数据的索引结构,不同的存储引擎其索引结构可能会有差别。但是其目的只有一个,那就是加快查找速度,减少IO次数!

为什么Innodb的索引是B+树结构

  • 首先排除链表,线性遍历效率太低了
  • 排除二叉搜索树。二叉搜搜索树可能会退化成单支树,效率不稳定
  • AVL或者是红黑树?虽然是平衡的或者是接近平衡的,但是由于是二叉树,相较于B+树这种多叉树,二叉树的的高度更高,也就意味着向下查找的次数会较多,还是效率低于B+s树。
  • hash结构查找的效率是O(1)的,但是范围查找的效率较低。有些存储引擎的索引结构是采用hash,InnoDB和MyISAM都是采用B+。
  • B树?B树的节点既有数据又有page指针,而B+树只有叶子节点有指针,其他目录页,只有键值和Page指针。也就是说,B树的节点中除了存放数据还存放指针,而B+树只有叶子节点有数据,非叶子节点不存放数据。换句话来说就是B树的page由于存放了一部分数据,指向其它page的指针就不多了,也就导致B树的分支较少于B+树,从而导致整体树高于B+树。而树越高,查找的次数就会越多,OI次数也会越多(假设同一个表中数据)。除此之外,B+树的叶子节点是全部相连的,因此在范围查找的效率也会比B树高。因此B+树在数据库中应用比B树会更加出色
    • B树:
      在这里插入图片描述
    • B+树:
      在这里插入图片描述

各种存储引擎支持的索引

存储引擎主要索引结构
InnoDBB-Tree索引(聚簇索引和辅助索引)、全文索引、空间索引
MyISAMB-Tree索引
MyISAMB-Tree索引
Memory哈希索引、B-Tree索引
NDB分布式哈希索引、B-Tree索引

聚簇索引和非聚簇索引

聚簇索引和非聚簇索引是索引的两个类型。注意与索引结构的区别。
虽然InnoDB和MyISAM都是采用B+树做为索引结构,但是实现的细节会有所差异。InnoDB采用的是聚簇索引,即叶子节点存放的就是数据本身,也就是说整个B+树和数据是一个整体。而MyISAM采用的是非聚簇索引,即叶子节点存放的是指向数据的地址,也就是说整个B+树和数据是分开的。

下面给出非聚簇索引结构示意图:
在这里插入图片描述
其中一个表只能有一个聚簇索引,可以有多个非聚簇索引

如何证明innodb存储引擎是聚簇索引而Myisam是非聚簇索引呢?
来看下面的sql样例:
创建一个表,将其存储引擎设置为MyISAM,观察其文件结构
在这里插入图片描述
在这里插入图片描述
其中.MYD文件用来存储数据,.MYI文件存储表的索引结构,这证明了MyISAM存储引擎的索引类型是非聚簇的,即数据和索引结构是分开的。

再来看InnoDB
创建一个表,将其存储引擎设置为InnoDB,观察其文件结构
在这里插入图片描述
在这里插入图片描述
其中.ibd文件存储的是数据和索引,这证明了InnoDB的引类型是聚簇索引,即数据和索引结构放在一起。

索引类型

除了区分聚簇索引和非聚簇索引,更细地,索引类型还能区分为以下几种类型:

  • 主键索引
    • 特点:这是表中唯一标识每条记录的字段索引,不能包含空值。一张表只能有一个主键索引
    • 用途:确保每一行都有唯一标识,并且加速基于主键的查询。
  • 唯一键索引
    • 特点:保证列中的所有值唯一,可以包含空值。
    • 用途:防止列中出现重复值,加速基于该列的查询。
  • 普通索引(辅助索引)
    • 特点:允许出现重复值和空值
    • 用途:用于加速某些列的查询
  • 全文索引
    • 特点:用于列内全文搜索,支持自然语言查询。
    • 用途: 适用于大型文本字段(如VARCHAR和TEXT),加速关键词搜索。
  • 组合索引
    • 特点:在多个列上创建的索引
    • 用途:加速基于这些列的组合的查询。

值得注意的是,辅助索引虽然也是B+树结构,但是其叶子节点并没有数据,而是存储对应的key值(主键),拿到这个歌key之后再去主键索引中找到目标记录。这个过程叫做回表查询。也就是说,辅助索引找到的并不是一条记录,而是记录的主键值。要想找到完整的记录就得再去主键索引中去找。这种机制避免了空间的浪费,否则每建立一个索引都需要存储一份表中数据。

关于索引的操作

创建主键索引

除了在创建表的时候指定主键,mysql自动建立主键索引,我们还能在创建表之后添加主键索引
如:

create table user3(id int, name varchar(30));
-- 创建表以后再添加主键
alter table user3 add primary key(id);

添加主键不只是给这一列添加了主键约束,同时也会建立一个索引结构。

唯一索引的创建

跟主键索引类似,除了在创建表的时候指定唯一键,mysql自动创建唯一键索引,我们还能在创建表之后添加唯一键索引,本质就是修改字段属性。
如:

create table user6(id int primary key, name varchar(30));
alter table user6 add unique(name);

注意,如果某列的属性是主键或者是唯一键,mysql就会自动生成相应的索引。

普通索引的创建

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

查看索引

  1. show keys from 表名
    在这里插入图片描述
  2. show index from 表名
    在这里插入图片描述
  3. desc 表名
    在这里插入图片描述
    mul表示可重复,即普通索引。

删除索引

  1. 主键索引删除可以使用:alter table 表名 drop primary key.本质就是删除一个列的主键属性。
  2. 其他索引的删除: alter table 表名 drop index 索引名;索引名就是show keys from 表名中的 Key_name 字段
  3. drop index 索引名 on 表名这是最常用的方法
    在这里插入图片描述

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

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

相关文章

在React中,如何利用React.memo函数对函数组件进行优化?

React.memo 是 React 的一个高阶组件,用于对函数组件进行性能优化。它通过记忆化(memoization)来避免不必要的重新渲染。当组件的 props 没有变化时,React.memo 可以防止组件重新渲染,从而提高应用的性能。 使用 Reac…

【Redis】分布式锁基本理论与简单实现

目录 分布式锁解释作用特性实现方式MySQL、Redis、Zookeeper三种方式对比 原理 reids分布式锁原理目的容错redis简单分布式锁实现锁接口实现类下单场景的实现容错场景1解决思路优化代码 容错场景2Lua脚本Redis利用Lua脚本解决多条命令原子性问题 释放锁的业务流程Lua脚本来表示…

开放式耳机怎么选?五款劲爆机型强势PK!2024推荐版!

身为健身达人,我对耳机的要求可不低。开放式耳机让我在健身时既能享受音乐,又能清晰听到教练的指导。它佩戴舒适,不易掉落,而且音质出色,让我沉浸于运动的节奏中。市面上开放式耳机种类繁多,我为大家挑选了…

SD-WAN为什么适合小企业

SD-WAN(软件定义广域网)是一种革新性的网络技术,通过软件智能管理,实现灵活和高效的网络连接。在数字化转型浪潮中,企业对网络稳定性和性能的要求不断提升,SD-WAN因此受到了广泛关注。对于资源有限的小型企…

qml/c++:基础界面的串口设置逻辑

文章目录 文章介绍效果图本机串口打开从虚拟端串口传数据到本机串口 代码添加serialporthandler类serialporthandler.hserialporthandler.cpp获取串口列表打开串口关闭串口清空按钮接收数据按钮逻辑:打开和关闭串口、弹出信息框、按钮文字改变 main.cpp 文章介绍 上…

怎么采集阿里巴巴1688的商品或商家数据?

怎么使用简数采集器批量采集阿里巴巴1688的商品或商家相关信息呢? 简数采集器暂时不支持采集阿里巴巴1688的相关数据,谢谢。 简数采集器采集网络网页数据非常简单高效:输入要采集的网址,简数智能算法会自动提取出网页上的关键信…

【自动驾驶】ROS小车系统

文章目录 小车组成轮式运动底盘的组成轮式运动底盘的分类轮式机器人的控制方式感知传感器ROS决策主控ROS介绍ROS的坐标系ROS的单位机器人电气连接变压模块运动底盘的电气连接ROS主控与传感器的电气连接ROS主控和STM32控制器两种控制器的功能运动底盘基本组成电池电机控制器与驱…

90V降5V1.5A恒压WT6039

90V降5V1.5A恒压WT6039 WT6039是一款专为宽电压输入范围设计的降压DC-DC转换器芯片,覆盖12V至90V电压。该芯片集成了包括使能控制开关、参考电源、误差放大器、过热保护、限流保护及短路保护等关键功能,确保在各种操作条件下的系统安全与稳定性。WT6039…

Kotlin 中的可见修饰符

Java 和 Kotlin 中的可见修饰符: Java:public、private、protected 和 default(什么都不写);Kotlin:public、private、protected 和 internal; 比较: 对于 public 修饰符:在 Java 和 Kotlin 中…

NSSCTF-Web题目13

目录 [SWPUCTF 2022 新生赛]js_sign 1、题目 2、知识点 3、思路 [MoeCTF 2021]Do you know HTTP 1、题目 2、知识点 3、思路 [SWPUCTF 2022 新生赛]js_sign 1、题目 2、知识点 base64编码、敲击码(tap code) 3、思路 页面没有什么,…

CPRI协议理解——控制字内容

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 CPRI协议理解——控制字内容 前言同步标识L1 Inband ProtocolZ130.0Z.194 C&M 通道慢速C&M 通道快速C&M 通道Vendor Specific DataControl AxC Data 后记 前言 …

IIS代理配置-反向代理

前后端分离项目,前端在开发中使用proxy代理解决跨域问题,打包之后无效。 未配置前无法访问 部署环境为windows IIS,要在iis设置反向代理 安装代理模块 需要在iis中实现代理,需要安装Application Request Routing Cache和URL重…

【论文精读】ViM: Out-Of-Distribution with Virtual-logit Matching 使用虚拟分对数匹配的分布外检测

文章目录 一、文章概览(一)问题来源(二)文章的主要工作(三)相关研究 二、动机:Logits 中缺失的信息(一)logits(三)基于零空间的 OOD 评分&#xf…

水光互补+短期调度!梯级水光互补系统最大化可消纳电量期望短期优化调度模型程序代码!

前言 构建含风电、光伏的多能互补系统是解决新能源并网灵活性的重要途径。国家发展和改革委员会、能源局《关于推进电力源网荷储一体化和多能互补发展的指导意见》(发改能源规〔2021〕280号)明确提出了多能互补的实施路径,要充分发挥流域梯级…

Python图像处理库之pyvips使用详解

概要 在图像处理领域,高效和快速的图像处理工具对于开发者来说至关重要。pyvips 是一个强大的 Python 库,基于 libvips 图像处理库,提供高效、快速且节省内存的图像处理能力。pyvips 支持多种图像格式,并且能够执行各种复杂的图像处理任务,如裁剪、缩放、旋转、滤波等。本…

哪里还能申请免费一年期SSL证书?

SSL证书是网络安全的基石之一,它确保了数据传输的安全性和网站身份的真实性。而申请免费一年期SSL证书,则为广大用户提供了一个经济高效的方式来提升网站的安全性。具体介绍如下: 基于不同服务平台的免费SSL证书申请 FreeSSL:此平…

SAFEnet加密机的加密算法和技术

SAFEnet加密机是一款功能强大、安全可靠的加密设备,它在网络安全领域发挥着不可替代的作用。下面将从特点、功能、应用及优势等方面对SAFEnet加密机进行详细介绍。 一、特点 先进的加密算法和技术:SAFEnet加密机采用了最先进的加密算法和技术&#xff0c…

Linux应用编程-动态加载动态库 dlopen dlsym dlclose

使用so动态库时,可以在编译时链接动态库,也可以在代码运行时动态加载so库。本文主要介绍如何动态加载so库。 常用的函数主要有dlopen,dlysm,dlclose,dlerror。 一、函数介绍 1、dlopen函数 void * dlopen( const cha…

不清楚数据治理路线图怎么制定?跟随这个思路即可

我们已迈入一个数据驱动的时代,企业的数据不仅数量庞大,而且种类繁多,它们来源于不同的业务流程、客户互动和运营系统。数据已成为企业决策的核心,是推动创新和竞争优势的关键资源。然而,随着数据量的爆炸性增长&#…

ubuntu多版本cuda如何指定cuda版本

本文作者: slience_me ubuntu多版本cuda如何指定cuda版本 文章目录 ubuntu多版本cuda如何指定cuda版本1. 关于cuda设置1.1 查看当前安装的 CUDA 版本1.2 下载并安装所需的 CUDA 版本1.3 设置环境变量1.4 验证切换1.5 安装对应的 NVIDIA 驱动程序 2. 设置环境变量2.1…