MySQL Server 层和引擎层是如何交互的

Server 层、引擎层、BufferPool、磁盘间的关系


大体来说, MySQL可以分为Server层和存储引擎层两部分。

1)Server 层:Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

2)引擎层:在 Server 层下方的是各种存储引擎层,它们是 MySQL 的核心部分,负责数据的存储和检索。从 MySQL 5.5.5 版本开始 MySQL 将 InnoDB 设为默认存储引擎,它使用 BufferPool 作为缓存池来缓存磁盘中的数据,从而减少对磁盘的IO操作。

3)BufferPool:BufferPool 是 InnoDB 引擎中的一块儿缓存池,用于缓存磁盘中的数据。它位于引擎层和磁盘之间,起到了一个桥梁的作用。通过将数据缓存在内存中,可以减少对磁盘的访问次数,提高数据库的性能。

4)磁盘:磁盘是计算机的外部存储设备,用于存储大量的数据。在 MySQL 中,磁盘主要用于存储数据库的数据文件、日志文件等。

Server 层和引擎层是相对独立的两个模块,它们之间要配合完成工作,就会存在数据交互的过程,今天我们就以 Server 层从存储引擎层读取数据来讲讲这个起着关键作用的数据交互过程。

Server 层和引擎层交互原理


在源码中,数据库中的每个表都会对应 TABLE 类的一个实例,实例中有个 record 属性,record 属性是一个有着 2 个元素的数组,Server 层每次调用引擎层的方法读取数据时,都会用 table->record[0] 的形式把第 1 个元素的地址传给引擎层(这个地址实际上是指向 BufferPool 中存储的数据页的地址,即 Server 层通过使用 table->record[0] 间接访问 BufferPool)。引擎层从磁盘或者内存中读取数据之后,把引擎层的数据格式转换为 Server 层的数据格式,然后写入到这个地址对应的内存空间里,Server 层就可以拿这个数据来干各种事情了(比如:WHERE 条件筛选、分组、排序等)。

整个交互过程就是这么简单,这个交互过程之所以这么简单,是因为 Server 层前期做了足够的准备工作,才让这个过程看起来像百度的搜索框那么简单。

为了一探究竟,接下来就是我们往前追溯准备工作的时间了。

准备工作


在创建表时,会计算出来每个字段在记录(也就是我们常说的行)中的 Offset,以及一条记录的最大长度(包含存储变长字段的长度需要占用的字节数)。

当我们第一次查询某个表的时候,MySQL 会从 frm 文件中读取字段、索引等信息,以及刚刚提到的字段 Offset 、一条记录的最大长度。

接下来会根据记录的最大长度,为第 1 小节中提到的 TABLE 类实例的 record 属性申请内存,record 数组的两个元素 record[0]、record[1] 占用的字节数都等于记录的最大长度。

在源码中,每个字段都对应 Field 子类的一个实例,实例中有个 ptr 属性,指向每个字段在 record[0] 中对应的内存地址。对于变长字段,Field 子类实例中还会存储内容长度占用的字节数。

存储引擎从磁盘或者内存中读取一条记录的某个字段后,会判断字段的类型,如果是定长字段,把字段内容经过相应的格式转换后写入 ptr 指向的内存空间。如果是变长字段,先把内容长度写入 ptr 指向的内存空间,然后紧挨着把字段内容经过相应的格式转换后写入内容长度之后的内存空间。

说明:定长字段、变长字段是指数据类型的特性,具体可查看 MySQL 数据类型。

接下来会用一个实际的表为例子,并且通过一张图来展示 record[0] 的内存布局,以便有个直观的了解。

实例分析


示例表:

CREATE TABLE t(`id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `i1` int(10) unsigned DEFAULT '0',   `str1` varchar(32) DEFAULT '',   `str2` varchar(255) DEFAULT '',   `c1` char(11) DEFAULT '',   `e1` enum('北京','上海','广州','深圳') DEFAULT '北京',   `s1` set('吃','喝','玩','乐') DEFAULT '',   `bit1` bit(8) DEFAULT b'0',   `bit2` bit(17) DEFAULT b'0',   `blob1` blob,   `d1` decimal(10,2) DEFAULT NULL,   PRIMARY KEY (`id`)  ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

这是 record[0] 的内存布局:

示例表和内存布局图都有了,接下来我们对照着图来分析一下各个字段对应的内存空间的情况:

字段 NULL 值标记区域

这个区域是标记一条具体的记录中,定义表结构时没有指定 NOT NULL 的字段,实际的内容是不是 NULL,如果是 NULL,在这个区域中对应的位置会设置为 1,如果不是 NULL,则在这个区域中对应的位置会设置为 0,每个字段的 NULL 标记占用 1 bit。

这个字段在 record[0] 的开头处,所以它的 Offset = 0,由于示例表中,有 10 个字段都没有指定 NOT NULL,所以总共需要 10 bit 来存储 NULL 标记,共占用 2 字节

存储引擎读取每个字段时,如果该字段在字段 NULL 值标记区域有一席之地,就会把它对应的位置设置个值(0 或者 1)。

id 字段

id 字段的类型是 int,定长字段,占用 4 字节,Offset = 字段 NULL 值标记区域占用字节数 = 2,ptr 属性指向 Offset 2。

存储引擎读取到 id 字段内容,经过大小端存储模式转换之后(把引擎层的数据格式转换为 Server 层的数据格式),把内容写入到 ptr 属性指向的内存空间。

由于 InnoDB 中,内容是按大端模式存储的(内容高位在前,低位在后),而 server 层是按照小端模式读取的,所以在写入整数字段内容到 record[0] 之前会进行大小端存储模式的转换。

i1 字段

i1 字段的类型是 int,定长字段,占用 4 字节,Offset = id Offset(2) + id 长度(4) = 6,ptr 属性指向 Offset 6。

存储引擎读取到 i1 字段内容,经过大小端存储模式转换之后,把内容写入到 ptr 属性指向的内存空间。

str1 字段

str1 字段的类型是 varchar,变长字段,Offset = i1 Offset(6) + i1 长度(4) = 10,ptr 属性指向 Offset 10。

str1 字段定义时指定要存储 32 个字符,表的字符集是 utf8,每个字符最多会占用 3 字节(一个汉字字符占 3 字节),32 个字符最多会占用 96 字节,96 < 255,只需要 1 字节就够存储 str1 内容的长度了,所以 str1 len 区域占用 1 字节。(补充:VARCHAR(n) 存储可变长度的字符串(不会使用空格填充),n 表示字符的最大数量(即字符串的长度),取值范围从 0 到 65535 。VARCHAR 需要额外的 1 或者 2 字节存储字符串的长度,最大长度(此处最大长度是指 n 个字符最多占用字节数)小于等于 255 时额外需要1字节,否则需要2字节。

str1 字段内容紧挨着 str1 len 之后,由于 str1 len 占用 1 字节,所以 str1 内容的 Offset = 10 + 1 = 11。

存储引擎读取 str1 字段的内容时,也会读取到 str1 的内容长度,会先把内容长度写入 ptr 属性指向的内存空间,然后紧挨着写入 str1 的内容。

str2 字段

str2 字段的类型也是 varchar,变长字段,Offset = str1 Offset(10) + str1 内容长度占用字节数(1) + 内容最大占用字节数(96) = 107,ptr 属性指向 Offset 107。

str2 字段定义时指定要存储 255 个字符,最多会占用 255 * 3 = 765 字节,需要 2 字节才能存储 str2 的内容长度,所以 str2 len 区域占用 2 字节。

str2 字段内容紧挨着 str2 len 之后存储,由于 str2 len 占用 2 字节,所以 str2 内容的 Offset = 107 + 2 = 109。

存储引擎读取 str2 字段内容后,会先把内容长度写入 ptr 属性指向的内存空间,然后紧挨着写入 str2 的内容。

c1 字段

c1 字段的类型是 char,定长字段,Offset = str2 Offset(107) + str2 内容长度占用字节数(2) + 内容最大占用字节数(765) = 874,ptr 属性指向 Offset 874。

c1 字段定义时指定要存储 11 个字符,最多会占用 11 * 3 = 33 字节

存储引擎读取 c1 字段内容后,会把内容写入 ptr 属性指向的内存空间。如果 c1 字段的实际内容长度比字段内容最大字节数小,会挨着刚刚写入的内容,再写入一定数量的空格。

比如:实际内容长度为 11 字节,而字段内容最大字节数为 33,则会在实际内容之后再写入 22 个空格。 

e1 字段

e1 字段类型是 enum,定长字段,只有 4 个选项,占用 1 字节,Offset = c1 Offset(874) + 内容最大长度占用字节数(33) = 907。

enum 类型在存储引擎中是用整数存储的,存储引擎读取 e1 字段内容后,会对内容进行大小端转换,把转换后的内容写入 ptr 属性指向的内在空间。

s1 字段

s1 字段类型是 set,定长字段,只有 4 个选项,占用 1 字节,Offset = e1 Offset(907) + e1 长度(1) = 908。

set 类型在存储引擎中也是按照整数存储的,存储引擎读取 s1 字段内容后,也需要对内容进行大小端转换,把转换后的内容写入 ptr 属性指向的内存空间。

set 字段是用 enum 来实现的,最多占用 8 字节,共 64 bit,每个选项用 1 bit 表示,所以 1 个 set 字段总共可以有 64 个选项。enum、set 字段的需要长度说明一下,如果创建表时定义的选项数量不一样,字段的长度也可能会不一样(1 ~ 8 字节),但是字段长度在创建表时就已经是确定的了,所以它们也是定长字段。

bit1 字段

bit1 字段的类型是 bit,定长字段,创建表时定义的长度表示的是 bit,不是字节数,Offset = s1 Offset(908) + s1 长度(1) = 909。

bit1 字段定义时指定的是 bit(8),表示该字段长度为 8 bit,也就是 1 字节。

bit 类型的字段在存储引擎中是按 char 存储的,存储引擎读取 bit1 字段的内容后,把内容写入到 ptr 属性指向的内存空间。

这里的 char 是指的 C/C++ 里的 char,不是指的 MySQL 的 char 类型。

bit2 字段

bit2 字段的类型也是 bit,定长字段,创建表时定义的是 bit(17),占用 3 字节,Offset = bit1 Offset(909) + bit1 长度(1) = 910。

bit 类型的字段,如果创建表时指定的 bit 数不是 8 的整数倍,存储引擎在插入数据到磁盘或者内存时,就会在前面补充 0,比如 bit(17),占用 3 字节,内容为 00010000010010011 时,会在前面再补充 7 个 0 变成 000000000010000010010011,读出来的时候也还是这样的内容。

之所以定义 2 个 bit 字段,是为了测试 bit 类型的字段,定义的 bit 位数不是 8 的整数倍时,是不是会把多出来的那些 bit 存储到 字段值 NULL 标记区域中,后来发现,只有 MyISAM、NDB 存储引擎才会这样处理,InnoDB 中 bit 字段是按 char 存储的,bit 位数不是 8 的整数倍时,多出来的 bit 还需要占用 1 字节,比如:bit(17) 需要占用 3 字节。

blob1 字段

blob1 字段的类型是 blob,变长字段,Offset = bit2 Offset(910) + bit2 长度(3) = 913。

blob 类型的字段,最多可以存储 2 ^ 16 = 65536 字节 = 64K。

存储引擎读取 blob1 字段内容之后,会分配一块能够容纳 blob1 字段内容的内存空间,把读取出来的内容写入该内存空间中。然后把 blob1 字段的内容长度写入 ptr 属性指向的内存空间处,占用 2 字节,然后紧挨着写入刚刚分配的那块内存空间的首地址,占用 8 字节。

注意:只是把 blob1 字段的内容的首地址,而不是 blob1 字段的完整内容写入 record[0]。示例中只使用了 blob 类型的字段,实际 blob 类型分为 4 种:tinyblob、blob、mediumblob、longblob,这 4 种类型的内容长度分别占用 1 ~ 4 字节。另外,还需要说明的一点是:tinytext、text、mediumtext、longtext 也是用上面相应的 blob 类型实现的,json 类型是用 longblob 类型实现的。

d1 字段

d1 字段的类型是 decimial,定长字段,Offset = blob1 Offset(913) + blob1 长度占用字节数(2) + blob1 内容首地址占用字节数(8) = 923。

decimal 类型的字段,在存储引擎中是用二进制存储的,在创建表的时候,就计算出来了需要用几字节来存储。

存储引擎读取 d1 字段的内容之后,把内容写入 ptr 属性指向的内存空间。

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

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

相关文章

git 克隆无权限-重新输入账号密码

克隆项目代码时提示没有权限&#xff0c;有可能是没有登录账号&#xff0c;也可能是账号密码改了&#xff0c;运行下面指令&#xff0c;然后重新克隆项目&#xff0c;下载的时候会让你重新输入账号密码&#xff0c;则克隆成功 git config --global credential.helper cache 参考…

2023 金砖国家职业技能大赛网络安全省赛理论题样题(金砖国家未来技能挑战赛)

2023 金砖国家职业技能大赛网络安全省赛理论题样题&#xff08;金砖国家未来技能挑战赛&#xff09; 一、参加比赛的形式 团队参与&#xff0c;每队2名选手&#xff08;设队长1名&#xff09;。 二、项目项目阶段简介 项目由四个阶段组成&#xff0c;将按顺序完成。向参与者…

STM32——震动传感器点亮LED灯

震动传感器简单介绍 若产品不震动&#xff0c;模块上的 DO 口输出高电平&#xff1b; 若产品震动&#xff0c;模块上的 DO 口输出低电平&#xff0c;D0-LED绿色指示灯亮。 震动传感器与STM32的接线 编程实现 需求&#xff1a;当震动传感器接收到震动信号时&#xff0c;使用中断…

分布式数据库HBase

文章目录 前言 一、HBase概述 1.1.1 什么是HBase HBase是一个分布式的、面向列的开源数据库HBase是Google BigTable的开源实现HBase不同于一般的关系数据库, 适合非结构化数据存储HBase是一种分布式、可扩展、支持海量数据存储的 NoSQL数据库。HBase是依赖Hadoop的。为什么HBa…

Linux中的输入输出重定向

目录 1.输出重定向 > 2.追加重定向 >> 3.标准 正确/错误 输出重定向 4.输入重定向 < 5.标准输入 0 1.输出重定向 > 将命令执行之后的结果不打印出来&#xff0c;可以输入在另外一个文件当中。 如&#xff0c;我查看文件a.txt 的前3行&#xff0c;然后不显…

如何从eureka-server上进行服务发现,负载均衡远程调用服务

在spring cloud的maven的pom文件中添加eureka-client的依赖坐标 <!--eureka-client依赖--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependen…

gitLab 和Idea分支合并

以下二选1即可完成分支合并建议第一种简单有效 Idea合并方式 切换到被合并的分支&#xff0c;如我想把0701的内容合并到dev&#xff0c;切换到dev分支&#xff0c;然后再点击merge然后选择要合并的分支&#xff0c;即可,此时git上的代码没有更新只是把代码合到本地需要pull才…

盲盒小程序搭建:实现盲盒消费新体验

近几年来&#xff0c;潮玩市场中的盲盒逐渐席卷了年轻一代人的生活&#xff0c;吸引了不少消费者。盲盒的不确定性给消费者带来了惊喜和快乐&#xff0c;盲盒的商业价值也是逐渐增加&#xff0c;预计2024年盲盒市场规模将突破300亿元。 但在当下互联网快速发展的时代下&#x…

python/matlab图像去雾/去雨综述

图像去雾和去雨是计算机视觉领域的两个重要任务&#xff0c;旨在提高图像质量和可视化效果。本文将综述图像去雾和去雨的算法、理论以及相关项目代码示例。 一、图像去雾算法 基于暗通道先验的方法&#xff1a; 这是广泛应用于图像去雾的经典算法之一。该方法基于一个观察&…

Ubuntu22.04通过Maas和Juju部署openstack charm

目录 官方文档材料准备软件硬件 模板机和虚拟网络安装MAAS官方文档MAAS节点配置安装MAAS浏览器登录MAAS进行配置 激活DHCP 官方文档 https://docs.openstack.org/project-deploy-guide/charm-deployment-guide/2023.1/ 这是一个通过Maas面板即可部署openstack的方式&#xff0…

华为数通---使用基本ACL限制Telnet登录权限案例

组网需求 如下图所示&#xff0c;PC与设备之间路由可达&#xff0c;用户希望简单方便的配置和管理远程设备&#xff0c;可以在服务器端配置Telnet用户使用AAA验证登录&#xff0c;并配置安全策略&#xff0c;保证只有符合安全策略的用户才能登录设备。 配置通过Telnet登录设备…

学习极市开发平台

这是官网的链接&#xff1a;极市开发者平台-计算机视觉算法开发落地平台-极市科技 (cvmart.net) 第一次用这个平台有很多问题&#xff0c;首先在使用这个平台之前&#xff0c;我大部分时候使用的是百度的飞浆平台&#xff0c;也就是BML&#xff0c;去训练一些深度学习的模型。 …

防抖和节流

防抖&#xff08;Debouncing&#xff09;&#xff1a; 防抖是指在事件被触发后&#xff0c;等待一定的时间间隔&#xff0c;如果在这个时间间隔内再次触发该事件&#xff0c;则重新计时。只有当事件停止触发一段时间后&#xff0c;才会执行相应的操作。防抖常用于优化输入框的搜…

Elasticsearch,Kibana集成,x-pack鉴权配置

Elasticsearch,Kibana集成 Java8环境部署[CentOS7] cd /usr/local/src wget https://repo.huaweicloud.com/java/jdk/8u201-b09/jdk-8u201-linux-x64.tar.gztar -xzvf jdk-8u201-linux-x64.tar.gz -C /usr/local#配置环境变量 vim /etc/profile #文末添加 export JAVA_HOME/us…

强敌环伺:金融业信息安全威胁分析——钓鱼和恶意软件

门口的敌人&#xff1a;分析对金融服务的攻击 Akamai会定期针对不同行业发布互联网状态报告&#xff08;SOTI&#xff09;&#xff0c;介绍相关领域最新的安全趋势和见解。最新的第8卷第3期报告主要以金融服务业为主&#xff0c;分析了该行业所面临的威胁和Akamai的见解。我们发…

2023年11月Web3行业月度发展报告区块链篇 |陀螺研究院

11月&#xff0c;在宏观转好以及事件带动下&#xff0c;加密市场逐渐回暖。上月现货ETF带来的市场情绪持续增强&#xff0c;美方监管利好消息不断&#xff0c;零售投资者入场信号明显&#xff0c;持仓在10枚BTC以下的小规模投资者持仓持续上涨&#xff0c;推动BTC保持坚挺。利好…

sed 流式编辑器

使用方式&#xff1a; 1&#xff0c;前置指令 | sed 选项 定址符指令 2&#xff0c;sed 选项 定址符指令 被处理文档 选项&#xff1a; -n 屏蔽默认输出 -i写入文件 -r支持扩展正则 指令&#xff1a; p输出 d删除 s替换 sed -n 1p user //输出第1行 sed -n…

sklearn随机森林 测试 路面点云分类

一、特征5个坐标 坐标-特征-类别 训练数据 二、模型训练 记录分享给有需要的人&#xff0c;代码质量勿喷 import numpy as np import pandas as pd import joblib#region 1 读取数据 dir D:\\py\\RandomForest\\ filename1 trainRS filename2 .csv path dirfilename1file…

flink使用事件时间时警惕kafka不同分区的事件时间倾斜问题

背景 flink和kafka的消息组合消费模式几乎是实时流处理的标配&#xff0c;然后当在flink中使用事件时间处理时&#xff0c;需要注意kafka不同分区元素之间时间相差太大的问题&#xff0c;这样有可能会导致严重的数据堆积问题 kafka不同分区元素事件时间差异较大导致的问题 总…

〖大前端 - 基础入门三大核心之JS篇㊼〗- BOM基础之window对象

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;不渴望力量的哈士奇(哈哥)&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xf…