【Hello mysql】 mysql的索引

news2024/11/26 15:50:19

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

mysql索引

    • 索引
      • 索引是什么
      • 索引的分类
      • 索引作用查看
    • 磁盘
      • mysql的工作过程
      • 认识磁盘
      • 定位扇区
      • 磁盘随机访问(Random Access)与连续访问(Sequential Access)
      • mysql和磁盘交互的基本单位
    • 索引的理解
      • 建立测试表
      • 为何IO交互的单位要是page
      • 理解单个page
      • 理解多个page
        • 页目录
        • 单页目录
        • 多页目录
      • 为什么数据结构是B+树而不是其他数据结构
      • 聚簇索引和非聚簇索引
      • 主键和辅助索引
    • 索引操作
      • 创建主键索引
      • 创建唯一键索引
      • 创建普通索引
      • 全文索引
      • 索引查询
      • 删除索引
      • 什么字段可以创建索引

索引

索引是什么

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

索引:提高数据库的性能,索引是物美价廉的东西了。不用加内存,不用改程序,不用调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子句中的字段不该创建索引

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

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

相关文章

Python(十四)数据类型——浮点型

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

Redis(分布式缓存详解)

Redis 一、Redis简介1.1. 适用场景1.2. 常用数据类型1.3. 单点Redis缺陷 二、持久化机制&#xff08;解决数据丢失&#xff09;2.1. RDB2.1.1. RDB优缺点 2.2. AOF2.2.1. AOF配置2.2.2. AOF优缺点 三、Redis集群3.1. 主从&#xff08;解决并发读&#xff09;3.1.1. 主从数据同步…

HBase v2.2 高可用多节点搭建

最近刚刚完成了HBase相关的一个项目,作为项目的技术负责人,完成了大部分的项目部署,特性调研工作,以此系列文章作为上一阶段工作的总结. 前言 其实目前就大多数做应用的情况来讲,我们并不需要去自己搭建一套HBase的集群,现有的很多云厂商提供的服务已经极大的方便日常的应用使…

GEE:计算每个对象的面积、标准差、周长、宽度、高度

作者:CSDN @ _养乐多_ 本文记录了面对对对象分割,以及计算每个对象的面积、标准差、周长、宽度、高度的代码。 文章目录 一、代码一、代码 // 设置种子 var seeds = ee.Algorithms.Image.Segmentation.seedGrid(20)

node.js 第一天

目录 使用readFile()方法读取文件内容 判断文件是否读取成功 判断文件是否写入成功 案例 考试成绩整理 path path.join&#xff08;&#xff09; path.basename() 使用readFile()方法读取文件内容 // 1. 导入 fs 模块&#xff0c;来操作文件 const fs require(fs)// 2.…

Redis(二)网络协议和异步方式(乐观锁悲观锁)

Redis系列文章 Redis&#xff08;一&#xff09;原理及基本命令&#xff08;柔性数组&#xff09; Redis&#xff08;二&#xff09;网络协议和异步方式&#xff08;乐观锁&悲观锁&#xff09; Redis&#xff08;三&#xff09;存储原理与数据模型&#xff08;hash冲突、渐…

2023年NOC决赛-加码未来编程赛项决赛模拟题-Python模拟题--卷2

第一题: 题目:打印出所有的“水仙花数”,所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身。例如:153是一个“水仙花数”,因为153=1的三次方+5的三次方+3的三次方。 第二题: 题目:企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10…

mysql invalid conn排查

mysql invalid conn排查 问题背景 服务使用golang &#xff0c;客户端库是go-mysql-driver ,系统测试环境频繁但是不总是报出invalid conn 错误&#xff0c;但实际拿sql执行时却是正常执行。 排查思路 原因分析 客户端使用了无效连接 由于连接无效&#xff0c;首先考虑客…

AI绘画StableDiffusion实操教程:冰霜旗袍美女

飞书原文链接&#xff0c;获取更多资源&#xff1a;AI绘画StableDiffusion实操教程&#xff1a;冰霜旗袍美女 前几天分享了StableDiffusion的入门到精通教程&#xff1a;AI绘画&#xff1a;Stable Diffusion 终极炼丹宝典&#xff1a;从入门到精通 但是还有人就问&#xff1a…

数学专题训练1 概率dp

数学专题训练1 1. RollingDiceDivOne 有 n n n​ 个骰子&#xff0c;第 i i i​ 个骰子拥有 d i c e [ i ] dice[i] dice[i] 面&#xff0c;每面包含 1 , 2 , . . . , d i c e [ i ] 1,2,...,dice[i] 1,2,...,dice[i] 的点数。同时掷这 n n n 个骰子&#xff0c;问掷出来…

JavaWeb——类的加载过程

目录 一、类加载的过程 1、加载 2、连接 &#xff08;1&#xff09;、验证 &#xff08;2&#xff09;、准备 &#xff08;3&#xff09;、解析 3、初始化 二、类加载的触发 三、双亲委派模型 1、定义 2、过程 3、优点 &#xff08;1&#xff09;、避免重复加载类 …

Redis持久化AOF和RDB,学习总结篇!

大家好&#xff0c;我是三叔&#xff0c;很高兴这期又和大家见面了&#xff0c;一个奋斗在互联网的打工人。 前面笔者写了一篇关于Redis 数据结构和数据类型的博客总结&#xff0c;这篇博客总结一下关于 Redis 持久化。部分图片来自作者&#xff1a;小林哥&#xff0c;小林哥y…

JS debug跳过的几种方法

JS debug跳过的几种方法 第一种反调试解决方法: 禁用断点法 禁用所有断点&#xff0c;包括自己打的断点&#xff0c;无法调式。第二种反调试解决方法: 从来不执行法 在debug处 点击右键&#xff0c;选择 Nerver pause here&#xff0c;点击确定。第三种反调试解决方法: 条件…

常见的JS内置对象之数组

数组&#xff08;Array&#xff09; 1&#xff09;定义数组 直接push的话&#xff0c;值放在了创建的大小之后&#xff0c;用下标进行操作值在创建大小里 创建两个空对象 2&#xff09;数组的遍历&#xff1a; for in 循环 迭代数组&#xff1a;迭代出的是下标 拿到的不是值&am…

CnosDB x LangChain: 聊着天来查询时序数据库

本篇我们将主要介绍如何使用 LangChain 连接 CnosDB 数据库&#xff0c;实现使用自然语言和数据库的交流。 大模型等的相关话题已经霸榜半年有余&#xff0c;在讨论关注之余&#xff0c;CnosDB技术团队将大模型与人工智能相关技术与数据库开发与实践进行融合。继CnosDB全面整合…

实现最大子段和的计算,输入数据从文件读取。

1.设计思路   求解该题可以用分治法解题&#xff0c;总共有三种情况&#xff0c;最大子段在我们数组的左侧&#xff1b;最大子段在我们数组的右侧&#xff1b;最大子段跨过了左右两侧&#xff0c;在中间最大。第一种和第二种将两个子问题递归解出。分开的位置就是我们的中心位…

ios oc button 设置

Button调整内部的子控件的位置

图书借阅系统

系统描述 该图书借阅系统是一个用于图书馆或类似机构管理图书借阅流程的在线平台。系统主要包括管理员模块和读者模块&#xff0c;分别提供相应的功能和权限。 - 管理员模块&#xff1a; - 注册&#xff1a;允许管理员创建新的管理员账号。 - 登录&#xff1a;允许管理员使用账…

centos7 安装vmware17

vmware17 下载 官网 VMware Hong Kong - Delivering a Digital Foundation For Businesses | HK | HK 下载链接 Download VMware Workstation Pro 下载链接&#xff0c;直接点击下载即可 https://download3.vmware.com/software/WKST-1702-LX/VMware-Workstation-Full-17.0…

Unity自定义后处理——校色

Unity自定义校色后处理 大家好&#xff0c;我是阿赵。   之前介绍了一些后处理的做法&#xff0c;包括了PostProcessing和CommandBuffer。其实如果我们只是实现一些比较单一的全屏效果&#xff0c;也没必要搞那么复杂&#xff0c;直接在OnRenderImage里面写Graphics.Blit就可…