[MySQL#10] 索引底层(1) | Page | 页目录

 目录

1. 初识索引

2. 认识磁盘

3. MySQL与磁盘交互基本单位

4. 索引的理解

1. 重谈Page

2. 为什么IO交互要用Page

3. 有主键的表插入数据时的排序

4. 单个Page与多个Page

4.1 单个Page

4.2 多个Page

目录

单Page目录

多Page目录


在看本文之前,可以回顾一下这两篇前文~

[MySQL#5] 表约束(2) | 唯一键 | 外键 | 简易商店数据库

【Linux】(26) 详解磁盘与文件系统:从物理结构到inode机制

1. 初识索引

  • 索引是提高数据库性能的关键工具。
  • 它通过改变数据的组织方式,显著提升查询速度,而无需增加硬件资源或修改应用程序代码。
  • 然而,索引的使用也并非没有代价。
  • 查询速度的提升是以插入、更新和删除操作的性能下降为代价的,因为这些写操作会增加大量的I/O操作。
  • 因此,索引的价值在于提高海量数据的检索速度。

MySQL的CURD操作与内存

  • 所有MySQL的CURD操作都在内存中进行。MySQL在启动时会预先开辟一大块内存空间,用于缓存数据。
  • 这些数据会在适当的时候被刷新到磁盘中进行持久化
  • 因此,MySQL服务器本质上是在内存中运行的,所有的数据库操作都在内存中进行。索引同样也是在内存中的一种特定结构
  • 索引是提高效率的,一般我们知道提高算法效率的因素:1. 组织数据的方式 ,2. 算法本身
  • 索引是更改特定组织数据的方式,把以前数据的组织方式以新的数据结构组织起来。所以索引是内存中一种特定组织的一种数据结构,具体是什么结构后面再说,

索引的类型

常见的索引类型包括:

  • 主键索引(Primary Key)
  • 唯一索引(Unique)
  • 普通索引(Index)
  • 全文索引(Fulltext)——主要用于解决中文索引问题

创建海量数据表

为了演示索引的效果,我们先创建一个包含800万条记录的表,并观察没有索引时的查询性能。

-- 产生随机字符串
delimiter $$
create function rand_string(n INT)
returns varchar(255)
begindeclare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';declare return_str varchar(255) default '';declare i int default 0;while i < n doset 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)
begindeclare 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))
begindeclare i int default 0;set autocommit = 0;repeatset i = i + 1;insert into EMP (empno, ename, job, mgr, hiredate, sal, comm, deptno)values (start + i, rand_string(6), 'SALESMAN', 0001, curdate(), 2000, 400, rand_num());until i = max_numend repeat;commit;
end $$
delimiter ;-- 执行存储过程,添加8000000条记录
call insert_emp(100001, 8000000);

注意:创建表单前要注意,恢复默认结束符:使用 delimiter ; 将结束符恢复为默认的分号 ;

查询性能测试

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

select * from EMP where empno = 998877;

没有索引时,查询非常慢,耗时约4.29秒。在实际项目中,如果放在公网中,同时有1000人并发查询,可能会导致系统崩溃。

解决方法:创建索引

alter table EMP add index(empno);

创建索引后,查询速度大幅提升。


2. 认识磁盘

硬件理解

MySQL 给用户提供存储服务,而存储的都是数据,数据在磁盘这个外设当中。

磁盘是计算机中的一个机械设备,相比于计算机其他电子元件,磁盘效率是比较低的,在加上IO本身的特征,可以知道,如何提升效率,是 MySQL 的一个重要话题。

磁盘结构

  • 扇区:数据库文件保存在磁盘的扇区中。每个扇区通常是512字节。
  • 柱面(Cylinder):同半径的磁道构成一个柱面。
  • 磁头(Heads):每个盘面有一个磁头,磁头和盘面的对应关系是1对1的。
  • CHS(Cylinder, Head, Sector):通过磁头、柱面和扇区编号定位扇区。
  • LBA(Logical Block Addressing):系统使用线性地址,最终转换为CHS。

再细看磁盘中一个盘片

扇区

数据库文件,本质其实就是保存在磁盘的盘片当中。也就是上面的一个个小格中,就是我们经常所说的扇区。当然,数据库文件很大,也很多,一定需要占据多个扇区。

题外话:

  • 从上图可以看出来,在半径方向上,距离圆心越近,扇区越小,距离圆心越远,扇区越大
  • 那么,所有扇区都是默认512字节吗?目前是的,我们也这样认为。因为保证一个扇区多大,是由比特位密度决定的。
  • 不过最新的磁盘技术,已经慢慢的让扇区大小不同了,不过我们现在暂时不考虑。
  • 我们在使用Linux,所看到的大部分目录或者文件,其实就是保存在硬盘当中的。(当然,有一些内存文件系统,如: proc , sys 之类,我们不考虑)
  • 建立数据库其实就是在linux下建立一个目录,建立一张表其就是在linux建立一个文件。

所以,最基本的,找到一个文件的全部,本质,就是在磁盘找到所有保存文件的扇区。

而我们能够定位任何一个扇区,那么便能找到所有扇区,因为查找方式是一样的。

定位扇区

  • 柱面(磁道): 多盘磁盘,每盘都是双面,大小完全相等。那么同半径的磁道,整体上便构成了一个柱面
  • 每个盘面都有一个磁头,那么磁头和盘面的对应关系便是1对1的
  • 所以,我们只需要知道,磁头(Heads)、柱面(Cylinder)(等价于磁道)、扇区(Sector)对应的编号。即可在磁盘上定位所要访问的扇区。这种磁盘数据定位方式叫做 CHS 。
  • 不过实际系统软件使用的并不是 CHS (但是硬件是),而是 LBA ,一种线性地址,可以想象成虚拟地址与物理地址。系统将 LBA 地址最后会转化成为 CHS ,交给磁盘去进行数据读取。不过,我们现在不关心转化细节,知道这个东西,让我们逻辑自洽起来即可。

结论

我们现在已经能够在硬件层面定位,任何一个基本数据块了(扇区)。那么在系统软件上,就直接按照扇区进行IO交互吗?不是

  • 如果操作系统直接使用硬件提供的数据大小进行交互,那么系统的IO代码,就和硬件强相关,换言之,如果硬件发生变化,系统必须跟着变化
  • 从目前来看,单次IO 512字节,还是太小了。IO单位小,意味着读取同样的数据内容,需要进行多次磁盘访问,会带来效率的降低。
  • 之前学习文件系统,就是在磁盘的基本结构下建立的,文件系统读取基本单位,就不是扇区,而是数据块。

故,系统读取磁盘,是以块为单位的,基本单位是 4KB 。

磁盘访问方式

  • 随机访问(Random Access):扇区地址不连续,磁头需要移动。
  • 连续访问(Sequential Access):扇区地址连续,磁头移动较少,效率较高。

3. MySQL与磁盘交互基本单位

软件理解

MySQL是一个应用层软件,依赖操作系统进行I/O操作。操作系统和磁盘之间的I/O单位是4KB,而MySQL为了提高效率,以16KB为单位先和 OS 进行I/O操作。

Buffer Pool

MySQL在内存中申请了一个大内存空间(Buffer Pool),用于缓存数据。默认大小为128MB。Buffer Pool用于减少磁盘I/O次数,提高查询性能。

I/O流程

  1. MySQL向操作系统请求16KB数据。
  2. 操作系统从磁盘读取4个4KB的数据块,加载到文件缓存区。
  3. 数据从文件缓存区加载到MySQL的Buffer Pool。
  4. MySQL在Buffer Pool中对数据进行处理。
  5. 更新数据时,MySQL将数据写入Buffer Pool,然后通过操作系统刷新到磁盘。

验证

  • 16*1024=16384
  • 磁盘这个硬件设备的基本单位是 512 字节,而 MySQL InnoDB引擎 使用 16KB 进行IO交互。
  • 即, MySQL 和磁盘进行数据交互的基本单位是 16KB 。
  • 这个 16KB 的基本数据单元,在 MySQL 这里叫做 page(注意和系统的page区分),它们是1:4的关系

共识要点

  1. MySQL以16KB page为单位进行I/O
  2. MySQL有Buffer Pool,数据先加载到Buffer Pool,再进行处理。
  3. 减少系统和磁盘I/O次数,一次I/O的数据量越大,效率越高。

以上就是我们学习索引的预备工作,下面正式开始学习索引~

4. 索引的理解

建立测试表

create table if not exists user (id int primary key,age int not null,name varchar(16) not null
) engine=InnoDB;

插入多条记录

insert into user (id, age, name) values (3, 25, 'Alice');
insert into user (id, age, name) values (1, 20, 'Bob');
insert into user (id, age, name) values (2, 22, 'Charlie');

查看插入结果

发现数据是有序的,这是因为MySQL会默认按照主键进行排序。

我们向一个具有主键的表中,乱序插入数据,发现数据会自动排序。谁做的?为什么这么做?

为了解决这个问题,我们需要重新探讨一下MySQL中的Page概念。

1. 重谈Page

数据读取流程

  • 磁盘上的文件数据:首先会被读到操作系统的文件缓存区中。
  • MySQL的Buffer PoolMySQL在启动时会为自己申请一个Buffer Pool,用于缓存数据。MySQL与操作系统之间进行I/O交互的基本单位是16KB,这是为了提高效率,减少I/O成本。

Page的概念

  • Page:MySQL的基本数据单位,大小为16KB。
  • Buffer Pool:可以加载多个Page,使用双向链表连接。

管理Page的方法

  • 先描述,再组织不要简单地将Page认为是一个内存块,Page内部也必须写入对应的管理信息。
  • 链表管理:所谓在MySQL中申请一个Page,实际上是new一个Page对象。然后将所有Page用“链表”的形式管理起来。我们在Buffer Pool内部对MySQL中的Page进行了建模。

2. 为什么IO交互要用Page

Page的重要性

  • 问题: MySQL和磁盘进行I/O交互时,采用Page方案的原因是为了提高效率。如果每次只加载需要的数据,例如查找id=2的记录,第一次加载id=1,第二次加载id=2,一次一条记录,那么就需要2次I/O。如果要找id=5,那么就需要5次I/O。
  • 批量加载:但如果这5条记录(或更多)都被保存在一个Page中(16KB,能保存很多记录),那么第一次I/O查找id=2时,整个Page会被加载到MySQL的Buffer Pool中完成一次I/O。此后如果查找id=1,3,4,5等,完全不需要进行I/O,而是在内存中进行。因此,在单Page内大大减少了I/O的次数。

局部性原理

  • 不能严格保证:我们不能严格保证用户下次查找的数据一定在同一个Page内,但有很大概率,因为有局部性原理。
  • 主要矛盾:IO效率低下的主要矛盾不是单次I/O数据量的大小,而是I/O的次数
3. 有主键的表插入数据时的排序

现象

  • 乱序插入,有序查询:我们向一个具有主键的表中,乱序插入数据,发现数据会自动排序。
  • 结论:数据最终以Page为单位进行管理,而Page是先描述后组织的

4. 单个Page与多个Page

4.1 单个Page

Page的定义

  • Page:一个Page可以看作是一个大的结构体,里面可以放很多数据,也有它自己的属性。
  • 数据承载:一个Page可以承载一部分数据,而一个文件可能很小也可能很大,因此MySQL建立的表可能会是一个或多个Page构成。

Page的特性

  • 大小:在MySQL中,每个Page的大小都是16KB。
  • 双向链表:Page使用prevnext指针构成双向链表,方便管理和遍历。

主键与排序

  • 主键:MySQL会默认按照主键对数据进行排序。
  • 无主键:如果没有主键,默认插入的顺序就是查询时的顺序。

为什么数据库在插入数据时要对其进行排序呢?我们按正常顺序插入数据不是也挺好的吗?

  • 目的:插入数据时排序的目的是优化查询的效率。Page内部的数据记录实质上是一个链表结构,链表的特点是增删快,查询修改慢。因此,有序的结构在查找时更加高效。
  • 优势:有序的数据在查找时从头到尾都是有效查找,没有任何一个查找是浪费的,而且如果运气好,可以提前结束查找过程。

4.2 多个Page

单Page的功能:在查询某条数据时,直接将一整页的数据加载到内存中,以减少硬盘IO次数,从而提高性能。

多Page的管理

  • 数据量大:如果有1千万条数据,需要多个Page来保存,多个Page彼此使用双链表链接起来,每个Page内部的数据也是基于链表的。
  • 线性查找:查找特定一条记录时,需要进行线性查找,效率较低,要遍历一万多条。

页目录的引入

  • 目录的作用:类似于书籍的目录,多花了空间但提高了效率。
  • 所以,目录,是一种“空间换时间的做法”

目录

单Page目录

牺牲page一部分保存数据的空间,把腾出来的空间用来保存目录,这所谓的目录里面只有两个字段

  • 第一个是它所指向起始位置的key值
  • 第二它有一个指针字段指向这条记录的起始位置

所以未来在查找key的时候,不需要在数据记录里面查找了,而是去目录中找

  • 先找到目录中对应key值的所处的起始位置
  • 然后在根据指针找到这条记录
  • 然后根据这条记录在向下遍历。
  • 虽然最后我们依旧需要遍历,但是是一个很小的子序列遍历了。效率也就大大提高了。

那么当前,在一个Page内部,我们引入了目录。比如,我们要查找id=4记录,之前必须线性遍历4次,才能拿到结果。现在直接通过目录2[3],直接进行定位新的起始位置,提高了效率。

现在我们可以再次正式回答上面的问题了,为何通过键值 MySQL 会自动排序?
可以很方便引入目录。

多Page目录

多Page的必要性

  • Page大小固定:MySQL中每一页的大小只有16KB,单个Page大小固定。
  • 数据量增长:随着数据量的不断增大,16KB不可能存下所有的数据,因此需要多个Page来存储数据。

Page的动态管理

  • 自动扩展:在单表数据不断被插入的情况下,MySQL会在容量不足时,自动开辟新的Page来保存新的数据。
  • 组织方式:通过指针的方式,将所有的Page组织起来,形成一个链表结构。

我们每次检索数据的时候,该从哪里开始呢?虽然顶层的目录页少了,但是还要遍历啊?

不用担心,可以再加目录页

多级目录:如果保存数据的Page变得非常多,上层的页目录也会变得非常多

  • 可以通过给上层页目录再加一层目录,形成多级目录结构

这就是传说中的B+树呀!没错,至此,我们已经给我们的表user构建完了主键索引。

随便找一个id=?

我们发现,现在查找的Page数一定减少了,也就意味着IO次数减少了,那么效率也就 提高了。


本篇文章小结:

  • Page:MySQL的基本数据单位,大小为16KB,用于提高I/O效率。
  • 主键与排序:MySQL会默认按照主键对数据进行排序,优化查询效率。
  • 多Page管理:通过页目录和B+树结构,提高多Page间的查找效率。

下篇文章继续讲解

  • B+树:节点不存储数据,叶子节点相连,适合范围查找。
  • 聚簇索引与非聚簇索引:InnoDB使用聚簇索引,MyISAM使用非聚簇索引,各有优劣。

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

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

相关文章

sklearn 实现随机森林分类器 - python 实现

python sklearn 实现随机森林分类器 from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import load_iris # 加载数据集 irisload_iris() x,yiris.data,iris.target print("x y shape:",x.shape,y.shape) # 创建并训练模型 model Random…

Altium Designer使用技巧(二)

一、创建类 1、按DC键&#xff0c;打开对象类。 2、右键添加一个类。命名为PWR。 3、将所有的电源类&#xff0c;全部添加到新创建的类中&#xff0c;从非成员类中点选到成员类中。 4、右下角点panes ,点PCB。 5、然后在左边单击PWR&#xff0c;点连接&#xff0c;可显示或…

<十六>Ceph mon 运维

Ceph 集群有故障了&#xff0c;你执行的第一个运维命令是什么&#xff1f; 我猜测是ceph -s 。无论执行的第一个命令是什么&#xff0c;都肯定是先检查Mon。 在开始之前我们有必要介绍下Paxos协议&#xff0c;毕竟Mon就是靠它来实现数据唯一性。 一&#xff1a; Paxos 协议 1…

Python Flask 数据库开发

Python Flask 数据库开发 引言环境配置创建 Flask 应用&#xff0c;连接数据库定义路由定义模型创建表创建 API 数据库直接操作启动 Flask 应用app.py 示例运行 Flask访问应用 展望 引言 在现代 web 开发中&#xff0c;Python 的 Flask 框架因其轻量和灵活性受到广泛欢迎。结合…

NPOI 操作详解(操作Excel)

目录 1. 安装 NPOI 2. 使用 NPOI 创建新 Excel 文件 3. 设置列宽和行高 1. 设置列宽 2. 设置行高 3. 同时设置列宽和行高 4. 设置统一的行高 5. 设置统一的列宽 6. 应用统一的行高和列宽 4. 合并单元格 5. 设置单元格样式&#xff08;字体、边框、背景色等&#xf…

TCP/IP网络编程:理解网络编程和套接字

TCP/IP网络编程&#xff1a;理解网络编程和套接字 网络编程又叫做套接字编程&#xff0c;是因为在网络编程中依赖使用套接字(socket),网络编程一般是C/S架构&#xff0c;即客户端/服务器模式&#xff0c;在服务器端依赖套接字绑定自身接口&#xff0c;并开启监听客户端连接&am…

Spring中lazy-init属性

Spring中lazy-init属性 1. 在 Spring 框架中的 lazy-init 属性 在 Spring 框架中&#xff0c;lazy-init 属性主要用于控制 Spring 容器中 Bean 的初始化时机。 含义&#xff1a; 当一个 Bean 被定义在 Spring 的配置文件&#xff08;可以是 XML 配置或者基于注解的配置等效场景…

多处理机调度(李昂学长视频总结)25新增考点

多处理机定义&#xff1a;多处理机指的是某个计算机系统中有多个cpu&#xff0c;在多处理机调度中&#xff0c;多处理机一般指的是共享存储器处理机&#xff0c;其两个或更多的cpu全部共享一个公用的RAM。 根据系统中的处理机相同与否&#xff0c;可将多处理机系统分为如下两类…

开源目标检测和语义分割都有哪些方法

在开源社区中&#xff0c;目标检测和语义分割都有许多广泛使用的方法和框架&#xff0c;以下是一些主流的方法&#xff1a; 1. 目标检测方法 目标检测的主要任务是识别图像中的物体并给出其位置&#xff08;通常为边界框&#xff09;&#xff0c;以下是常用的开源方法&#x…

少儿编程培训市场突破500亿元:教育新蓝海的崛起与未来展望

近年来&#xff0c;随着科技的迅速发展和家长对教育方式的重视&#xff0c;少儿编程市场成为一片新的蓝海。据最新市场调研报告显示&#xff0c;2024年中国少儿编程培训市场规模已突破500亿元&#xff0c;预计未来五年将持续增长。这一趋势反映了少儿编程教育的迅速崛起&#x…

【大数据学习 | kafka】producer的参数与结构

1. producer的结构 producer&#xff1a;生产者 它由三个部分组成 interceptor&#xff1a;拦截器&#xff0c;能拦截到数据&#xff0c;处理完毕以后发送给下游&#xff0c;它和过滤器不同并不是丢弃数据&#xff0c;而是将数据处理完毕再次发送出去&#xff0c;这个默认是不…

【论文速读】Optimization-based Prompt Injection Attack to LLM-as-a-Judge

基于优化的提示词注入攻击 摘要引言问题描述LLM-as-a-judge威胁模型攻击者知道什么 JUDGEDECEIVER 细节概述生成影子候选回复公式化为优化问题Target-aligned generation lossTarget-enhancement lossAdversarial perplexity loss优化问题 求解优化问题 摘要 LLM-as-a-Judge 利…

人工智能证书合集

本文将对目前市面上主流官方机构颁发的人工智能证书进行整理和介绍&#xff0c;由于整理的证书较多&#xff0c;本文共一万八千多字&#xff0c;请根据自己的考证需求阅读对应部分的内容&#xff0c;希望本文对人工智能行业的从业人员和计划从事人工智能相关岗位工作的人员有所…

Java入门8——二维数组

今天的内容算是数组的收尾~~ 从下次开始就要开始学习类和对象了&#xff0c;冲冲冲&#xff01; 首先二维数组&#xff0c;也很好理解&#xff0c;就是把几个一维数组拼在一起了&#xff0c;我们用代码来熟悉一下~ public class javaSchool {public static void main(String[…

自动售饮料机控制电路的设计

自动售饮料机控制电路的设计 1 设计目的 &#xff08;1&#xff09;熟悉数字电路的应用。 &#xff08;2&#xff09;掌握常常利用逻辑运算器及D触发器的逻辑功能及利用方式。 &#xff08;3&#xff09;熟悉电路仿真软件Multisim 利用。 &#xff08;4&#xff09;了解自动售饮…

高速高精运动控制解决方案亮相2024 NEPCON亚洲电子展!

■展会名称&#xff1a; NEPCON ASIA 2024 亚洲电子生产设备暨微电子工业展览会&#xff08;以下简称“亚洲电子展”&#xff09; ■展会日期 2024年11月6 -8日 ■展馆地点 中国深圳国际会展中心(宝安) ■展位号 11号馆-11A24 11月6日至8日&#xff0c;亚洲电子展将在中…

Flask轻松上手:从零开始搭建属于你的Web应用

目录 一、准备工作 二、安装Flask 三、创建你的第一个Flask应用 创建一个新的Python文件 编写Flask应用代码 运行Flask应用 四、创建一个简单的博客系统 定义路由和文章列表 创建模板文件 运行并测试博客系统 五、使用数据库存储用户信息 安装Flask-SQLAlchemy 修…

STM32开发 —— 新工程创建思路终于清晰了

目 录 工程创建三步法一、工程文件夹创建二、管理工程项三、配置工程参数 工程创建三步法 从ST官网下载好stm32标准库或HAL库&#xff0c;HAL库目录如下。 在Keil开发环境中创建STM32工程&#xff0c;分三大步即可完成工程的创建&#xff1a; 一步&#xff1a;在本地磁盘创建…

Java SpringBoot调用大模型AI构建AI应用

本文是一个用springboot 结合spring mvc 和spring ai alibaba 调用国产大模型通义千问的具体例子&#xff0c;按照这个做能够快速的搞定Java应用的调用。 然后就可以把这类应用泛化到所有的涉及到非结构化数据结构化的场景中。 Spring AI&#xff1a;简化Java中大模型调用的框…

【办公类-04-04】华为助手导出照片视频分类(根据图片、视频的文件名日期导入“年-月-日”文件夹中,并转移到“年-月”文件中整理、转移到“年”文件夹中整理)

背景需求 最近带班&#xff0c;没有时间整理照片&#xff0c;偶尔导一次&#xff0c;几个月的照片。发现用电脑版“华为手机助手“中的WLAN连接”与华为手机的“华为手机助手”连接&#xff0c;速度更快、更稳定&#xff0c;不会出现数据线连接时碰碰就断网的问题 1、先打开电…