【Hello mysql】 mysql的索引

2023-11-19

Mysql专栏:@Mysql
本篇博客简介:介绍mysql的索引

索引

索引是什么

在课件上对于索引的定义是这样子的

索引:提高数据库的性能,索引是物美价廉的东西了。不用加内存,不用改程序,不用调sql,只要执行正确的 create index ,查询速度就可能提高成百上千倍。但是天下没有免费的午餐,查询速度的提高是以插入、更新、删除的速度为代价的,这些写操作,增加了大量的IO。所以它的价值,在于提高一个海量数据的检索速度。

总结下上面这段话

索引是mysql的一个特性 使用它可以增加mysql的查找效率 但是可能会牺牲一部分IO效率

索引的分类

常见的索引分类如下

  • 主键索引(primary key)
  • 唯一索引(unique)
  • 普通索引(index)
  • 全文索引(fulltext)–解决中子文索引问题

关于这个分类 大家现在不理解也没关系 等看完整篇博客之后就能对于它们有一个清晰的认知

索引作用查看

我们这里创建一张海量数据的数据库表(八万条数据) 演示在有和没有索引的情况下 效率的差别

我们可以使用下面的sql来创建一个海量数据表 (直接复制粘贴到mysql终端即可)

drop database if exists `index_demon`;
create database if not exists `index_demon` default character set utf8;
use `index_demon`;

-- 构建一个8000000条记录的数据
-- 构建的海量表数据需要有差异性,所以使用存储过程来创建

-- 产生随机字符串
delimiter $$
create function rand_string(n INT)
returns varchar(255)
begin
declare chars_str varchar(100) default
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
declare return_str varchar(255) default '';
declare i int default 0;
while i < n do
set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1));
set i = i + 1;
end while;
return return_str;
end $$
delimiter ;

-- 产生随机数字
delimiter $$
create function rand_num( )
returns int(5)
begin
declare i int default 0;
set i = floor(10+rand()*500);
return i;
end $$
delimiter ;

-- 创建存储过程,向雇员表添加海量数据
delimiter $$
create procedure insert_emp(in start int(10),in max_num int(10))
begin
declare i int default 0;
set autocommit = 0;
repeat
set i = i + 1;
insert into EMP values ((start+i)
,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num());
until i = max_num
end repeat;
commit;
end $$
delimiter ;

-- 雇员表
CREATE TABLE `EMP` (
  `empno` int(6) unsigned zerofill NOT NULL COMMENT '雇员编号',
  `ename` varchar(10) DEFAULT NULL COMMENT '雇员姓名',
  `job` varchar(9) DEFAULT NULL COMMENT '雇员职位',
  `mgr` int(4) unsigned zerofill DEFAULT NULL COMMENT '雇员领导编号',
  `hiredate` datetime DEFAULT NULL COMMENT '雇佣时间',
  `sal` decimal(7,2) DEFAULT NULL COMMENT '工资月薪',
  `comm` decimal(7,2) DEFAULT NULL COMMENT '奖金',
  `deptno` int(2) unsigned zerofill DEFAULT NULL COMMENT '部门编号'
);

-- 执行存储过程,添加8000000条记录
call insert_emp(100001, 8000000);

上述SQL中创建了一个名为index_demon的数据库 在该数据库中创建了一个名为EMP的员工表 并向表当中插入了八百万条记录

建立该数据库需要大概七分钟左右的时间 大家耐心等待

在这里插入图片描述
创建完毕之后我们即可使用index_demo数据库和里面的EMP表 下面开始我们的对比工作

查询员工编号为998877编号的员工

在这里插入图片描述

我们可以发现 花费了4.35秒才找到了该条记录

创建索引

在这里插入图片描述

对这八百万条记录创建索引我们用了20.33秒

重新查询该员工的数据

在这里插入图片描述

重新查询之后进步十分明显 直接从4.35秒降低到了0秒

这里就说明一个道理 索引对于海量数据查询效率的提升是十分显著的

磁盘

mysql的工作过程

宏观过程

在这里插入图片描述

由于冯诺伊曼体系我们可以知道 mysql不能对于磁盘中的数据库进行直接访问 它的访问一定是要经过操作系统的内核缓冲区的

大概的运行过程如上图

  1. mysql在内存中申请一大块内存空间 我们一般叫做buff pool
  2. mysql对于数据的curd都是在内存中进行操作的
  3. mysql调用系统函数write之后这些数据会写入操作系统的内核缓冲区当中
  4. 操作系统的内核缓冲区会定期刷新数据到磁盘中的数据库

认识磁盘

磁盘的整体结果如下

在这里插入图片描述

我们再来看看磁盘中的一个盘片

在这里插入图片描述

盘片中又被分为磁道和扇区 如上图所示 我们这里对于磁盘的整体结构和扇区概念有一个认知即可

其中扇区的大小大部分都是512字节

而我们前面说过 数据库的文件都保存在磁盘当中 所以说我们查找数据的本质就是在定位扇区

定位扇区

在这里插入图片描述

我们在硬件层面定位扇区的方式叫做CHS

  1. 首先找到磁头来确定文件在哪一个盘面
  2. 之后在该盘面中找到数据所在的磁道
  3. 最后在磁道中找到对于的扇区

但是在软件层面上我们使用的定位方式叫做LBA(一种线性地址) 我们可以把它们之间的关系想象成虚拟地址和物理地址之前的关系来方便理解

在软件层面上通过LBA定位到以后最终还是要使用CHS来定位具体的物理空间

操作系统和磁盘交互的基本单位

我们现在已经能够在硬件层面定位扇区了 那么在系统软件上我们就直接按照扇区的基本单位(大部分512字节)进行交互嘛?

答案是否定的 原因有以下几点

  • 如果操作系统使用硬件提供的基本单位进行交互 那么操作系统和硬件之间就会产生强相关的关系 一旦硬件发生大的改变 操作系统势必也要发生改变
  • 我们都知道 IO的效率是非常非常慢的 单次IO512字节还是太小了 而每次读取很小的数据势必会造成更多次的IO
  • 我们在基础IO时学习文件系统 文件系统就是在磁盘的基本结构下建立的 而它的基本单位不是扇区 而是数据块

综上所述 系统读取磁盘是以块为单位的 大小为4kb

磁盘随机访问(Random Access)与连续访问(Sequential Access)

  • 随机访问:本次IO所给出的扇区地址和上次IO给出扇区地址不连续,这样的话磁头在两次IO操作之间需要作比较大的移动动作才能重新开始读/写数据。
  • 连续访问:如果当次IO给出的扇区地址与上次IO结束的扇区地址是连续的,那磁头就能很快的开始这次IO操作,这样的多个IO操作称为连续访问。

因此尽管相邻的两次IO操作在同一时刻发出,但如果它们的请求的扇区地址相差很大的话也只能称为随机访问,而非连续访问。

磁盘是通过机械运动进行寻址的,随机访问不需要过多的定位,故效率比较高。

mysql和磁盘交互的基本单位

mysql和磁盘交互这句话其实并不准确 因为mysql没有资格直接和磁盘进行交互 它们之间一定是要经过操作系统的 但是我们学习这部分的时候为了方便理解可以暂时忽略操作系统

MySQL 作为一款应用软件,可以想象成一种特殊的文件系统。它有着更高的IO场景,所以为了提高基本的IO效率, MySQL 进行IO的基本单位是 16KB (后面统一使用 InnoDB 存储引擎讲解)

我们可以使用下面的sql语句来查看mysql交互的IO大小 我们可以发现是16384字节 实际也就是16kb

在这里插入图片描述

根据上面讲解的知识总结一些共识

  • MySQL 中的数据文件 是以page为单位保存在磁盘当中的
  • MySQL 的 CURD 操作 都需要通过计算 找到对应的插入位置 或者找到对应要修改或者查询的数据
  • 而只要涉及计算 就需要CPU参与 而为了便于CPU参与 一定要能够先将数据移动到内存当中
  • 所以在特定时间内 数据一定是磁盘中有 内存中也有 后续操作完内存数据之后 以特定的刷新策略刷新到磁盘 而这时就涉及到磁盘和内存的数据交互 也就是IO了 而此时IO的基本单位就Page
  • 为了更好的进行上面的操作 MySQL 服务器在内存中运行的时候 在服务器内部 就申请了被称为 Buffer Pool 的的大内存空间 来进行各种缓存 其实就是很大的内存空间来和磁盘数据进行IO交互
  • 为了更高的效率 要尽量减少系统和磁盘IO的次数

如何理解IO请求

首先我们可以明确一点 系统中肯定会存在大量的IO请求 而操作系统作为软硬件资源的管理者肯定要对这些资源进行管理 那么问题又来了 应该如何管理呢? - - 先描述 再组织

我们都知道Linux操作系统是由C语言写的 而C语言中描述一个对象所使用的方式是结构体

所以说IO请求在Linux操作系统中的形式其实就是结构体 这些结构体再被通过双链表的形式组织起来 这就是操作系统对于IO请求的管理

索引的理解

建立测试表

我们建立一个测试表user 设置id为主键

create table user(
    -> id int primary key,
    -> age int not null,
    -> name varchar(16) not null
    -> );

之后我们往表中插入几组数据

在这里插入图片描述
在上图中可以发现我们是乱序插入的 可是当我们查询插入结果的时候却变成有序的了

在这里插入图片描述
那么看到这里我们就会有两个问题产生

  • 排序的工作是谁干的?
  • 为什么要排序?

在回答上面两个问题之间我们再理解下page

为何IO交互的单位要是page

为什么IO交互的单位要是page呢 为什么不可以我们需要哪个数据就将哪个数据拿到内存中来

我们在前面说过 实际上IO的过程是十分缓慢的 如果说我们要拿十个数据 按照上面的方法就要交互十次

但是如果按照单位page进行交互 根据计算机的局部性原理 我们很可能IO的次数要小于十次 需要注意的是 局部性原理并不能确保一定减少IO的次数 这只是一个大概率事件

理解单个page

mysql在启动时会向系统申请128mb的内存空间(这也就是为什么有时候mysql会启动失败 因为系统没有这么多空间了)

一个page的大小是16kb 也就是说如果这些内存空间全部用来储存page的话就大概会有8000多个page页 既然有这么多的page页mysql肯定就要对它们进行管理 而管理的方法无法就是 先描述 再组织

所以说单个的page其实就是一个结构体

大概的格式如下图
在这里插入图片描述

每个page中有一个前驱和后继指针指向前后的page以方便管理 此外数据记录也存放在page中

那么到现在为止 我们就能够回答上面提出的两个问题了

是谁对于这些数据进行排序

mysql

为什么要进行排序

为了提高搜索效率

理解多个page

通过上面的分析我们可以知道 mysql中存在大量的page 并且它们之间是用双链表的形式连接起来的 如下图

在这里插入图片描述

但是仔细观察下我们就可以发现下面两点

  • page页内的数据是通过链表连接起来的
  • page页之间是通过链表连接起来的

在之前数据结构–链表章节的学习中 我们知道链表只能够线性的查找数据 也就是说虽然我们将这些数据和页表排序了 但是事实上搜索的效率并不会快多少

这个时候我们就引入了一个叫做页目录的方式来提高效率

页目录

假如我们现在看《深入理解计算机系统》这本书的时候 我们想要找到优化程序性能这一章节的内容有两种选择

  1. 一页页的翻书 直到找到该内容为止
  2. 根据目录找到这部分的内容

显然方法二的查询比方法一要快速许多 那么在page内和page之间 我们当然也可以引入目录的概念

单页目录

在这里插入图片描述

我们要查找id=4记录,之前必须线性遍历4次,才能拿到结果。现在直接通过目录2[3],直接进行定位新的起始位置,提高了效率。现在我们可以再次正式回答上面的问题了,为何通过键值 MySQL 会自动排序

可以方便的建立目录

建立目录的行为本质是一种以空间换时间的做法 虽然目录占用了一定的空间 但是却可以大大提高我们的搜索效率

多页目录

前面我们说过 在mysql内部有着大量的page页由双链表连接 如下图
在这里插入图片描述

虽然说我们通过单页目录提高了页内的搜索效率 但是在实际搜索的过程中 我们还是会遇到下面的两个问题

  • 我们无法定位出需要的数据在哪个page内 所以必须要遍历page才能够找到
  • 由于不知道我们的数据在哪个page 所以说会和磁盘进行大量的IO来寻找

上面的两个问题如果不解决 我们页内目录做出的效率提升就毫无意义

那么应该如何解决呢? 答案也是建目录

在这里插入图片描述

我们可以将单个的page页看作是一篇文章 它的代号就是page页中键值的最小值

之后我们再重新创建新的page页用来做目录 指向这些 “文章”

但是这样子的模型还是没有解决我们的问题

  • 一开始要到哪里去找到有我们想要page页的目录呢?

这个问题的解决方案还是加目录

我们可以在刚刚建立完毕的目录page上层再加上一个page页作为它们的目录指向这些page页

这样子当我们需要找一个page页的时候只需要从最上层的目录开始往下依次查找就可以了

如下图

在这里插入图片描述
我们再看上面的结构 其实就是一颗B+树

为什么数据结构是B+树而不是其他数据结构

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

也就是说我们选择B+数主要有两个方面的考虑

  1. 这是一颗矮胖形的树 决定了它和系统之间的IO次数会更少
  2. 它的范围查找很优秀

聚簇索引和非聚簇索引

这里直接给出解释

  • 聚簇索引就是数据和索引放在一起
  • 非聚簇索引就是数据和索引分开

其中innodb用的就是非聚簇索引 而myisam用的是聚簇索引

主键和辅助索引

mysql除了会建立默认的主键索引之外 用户也能按照其他列信息建立索引 而这些索引被叫做辅助索引

对于myisam来说辅助索引和默认的主键索引没有区别

而对于innodb来说 InnoDB的非主键索引中叶子节点并没有数据 而只有对应记录的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);

创建唯一键索引

创建唯一键索引的语法和创建主键索引相同 这里就不过多赘述

创建普通索引

方式一:在创建表的最后指定索引

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); --创建完表以后指定某列为普通索引

全文索引

创建全文索引

FULLTEXT (title,body)--创建全文索引

使用全文索引

WHERE MATCH (title,body) AGAINST ('database');

索引查询

show index from 表名;

删除索引

删除主键索引

--方式一:删除主键索引
alter table 表名 drop primary key;

删除其他索引

alter table 表名 drop index 索引名;

什么字段可以创建索引

  1. 比较频繁作为查询条件的字段应该创建索引
  2. 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件
  3. 更新非常频繁的字段不适合作创建索引
  4. 不会出现在where子句中的字段不该创建索引
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Hello mysql】 mysql的索引 的相关文章

随机推荐

  • python爬取(自动化)豆瓣电影影评,并存储。

    from selenium import webdriverfrom selenium webdriver import ActionChainsimport timedriver webdriver Chrome r C Program
  • [Qt]QObject::connect: Cannot queue arguments of type 'QString&'的解决方法

    参考 https blog csdn net tokary article details 5777141 遇到的问题是 在主线程和子线程中 通过信号与槽发送QString 遇到 QObject connect Cannot queue a
  • Ubuntu20.04中VSCode配置C++以及分文件编写配置

    网上搜索了很多文章 一直显示找不到自定义的头文件 今天总算捣鼓出来了 参考文章 https www cnblogs com icmzn p 16244665 html https blog csdn net qq 39048131 arti
  • python os.walk()方法使用详解

    os walk方法是python中帮助我们高效管理文件 目录的工具 在深度学习中数据整理应用的很频繁 如数据集的名称格式化 将数据集的按一定比例划分训练集train set 测试集test set 1 导入文件 使用os walk方法前需要
  • Unity使用Newtonsoft报错的解决方案

    文章目录 Unity 使用 Newtonsoft 报错的解决方案 问题描述 解决方法 方法一 使用 Unity 的 Package Manager 自动导入 方法二 访问 GitHub 下载 unitypackage 文件手动导入 Unit
  • Win7环境下robotframework环境搭建(含安装包下载地址)

    本文记录在Windows环境下使用robotframework搭建自动化测试环境 1 安装robotframework依赖的语言环境 Python2 7 Robot Framework是一款python编写的功能自动化测试框架 运行依赖于p
  • 解决Java JPA Es索引时间戳字符串格式比较问题

    说明 在Java开发中常见使用 yyyy MM dd HH mm ss 来格式化时间戳 例如 ApiModelProperty 订单开始日期 JsonFormat pattern yyyy MM dd HH mm ss JSONField
  • 华为云云耀云服务器L实例评测|在云耀云服务器L实例上部署battle-city坦克大战小游戏

    华为云云耀云服务器L实例评测 在云耀云服务器L实例上部署battle city坦克大战小游戏 一 前言 1 1 云耀云服务器L实例简介 1 2 battle city坦克大战小游戏简介 二 本次实践介绍 2 1 本次实践简介 2 2 本次环
  • 数据结构与算法书籍推荐

    学习数据结构与算法 还是很有必要看几本相关的书籍 但根据不同基础的人 合适看的书也不一样 因此 针对不同层次 不同语言的人 推荐几本市面上口碑不错的书 1 入门级 针对刚入门的同学 建议不要急着去看那些经典书 像 算法导论 算法 这些比较经
  • Python入门--变量

    变量是指在程序运行期间可以改变其值的数据类型 在Python中 变量可以指定任何数据类型 如字符串 整数 浮点数等 要创建一个变量 我们必须先指定变量的名称 然后将其赋值 在Python中 变量名可以是任何有意义的名称 但通常使用小写字母和
  • 【Leetcode】151. 翻转字符串里的单词

    题目描述 给你一个字符串 s 逐个翻转字符串中的所有 单词 单词 是由非空格字符组成的字符串 s 中使用至少一个空格将字符串中的 单词 分隔开 请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串 说明 输入字符串 s 可以在前面 后面
  • Android免打包多渠道统计如何实现,BAT面试文档

    我们程序员经常迷茫于有太多东西要学 有些找不到方向 不知所措 很多程序员都愿意说 我想变得更好 但是更好是什么却很模糊 同时我们又不知道该怎么样去做 我们的生命如此短暂 作为程序员的职业生涯可能会更短 所以我们更加需要充分利用工作 工作间隙
  • opencv resize()函数

    转自 https blog csdn net u012005313 article details 51943442 void resize InputArray src OutputArray dst Size dsize double
  • 在ping服务器时丢包该怎么办?

    一般跨境企业比如说跨境电商 游戏等等都会有海外各个节点服务器的需求 包括对海外服务器的需求 当使用服务器时 难免会出现一些问题 比如说丢包 那么 当服务器丢包的话 该如何处理呢 说到丢包 我们一般是在说PING服务器IP出现的数据包丢失的现
  • ASP.NET Core快速入门(第6章:ASP.NET Core MVC)--学习笔记

    课程链接 http video jessetalk cn course explore 良心课程 大家一起来学习哈 任务40 介绍 任务41 Individual authentication 模板 dotnet new mvc help
  • ChatGPT使用学习(三):ChatGPT桌面版使用

    文章目录 介绍 获取方式 界面展示 介绍 ChatGPT桌面版是指OpenAI推出的针对个人用户的ChatGPT应用程序 它是一个独立的桌面应用程序 可以在本地计算机上运行 而无需依赖互联网连接 这使得用户可以在没有网络连接的情况下使用Ch
  • 论文必备

    点击上方 小白学视觉 选择加 星标 或 置顶 重磅干货 第一时间送达 本文介绍了了12个将神经网络画地更好看的工具 1 draw convnet 一个用于画卷积神经网络的Python脚本 https github com gwding dr
  • 基于单片机的热敏电阻测温设计

    基于单片机的热敏电阻测温设计 1 基本功能 1 具有声光报警功能 2 使用液晶显示 3 温度上 下限报警值设定 温度上 下限报警 4 手动方式设定温度上下限 总体方案设计 温度控制系统主要由温度传感器 热敏电阻 A D转换器 单片机 STC
  • USB硬件设计注意事项

    USB物理层介绍及电路设计注意事项 博文原创 转载请注明出处 USB Universal Serial Bus USB 是英文Universal Serial Bus 通用串行总线的缩写 是一个外部总线标准 用于规范电脑与外部设备的连接和通
  • 【Hello mysql】 mysql的索引

    Mysql专栏 Mysql 本篇博客简介 介绍mysql的索引 mysql索引 索引 索引是什么 索引的分类 索引作用查看 磁盘 mysql的工作过程 认识磁盘 定位扇区 磁盘随机访问 Random Access 与连续访问 Sequent