MySQL的一行数据是如何存储的?

news2024/11/16 14:51:35

目录

1.COMPACT 行格式长什么样?

例子1:用户设置了主键值,列都是not null的。(默认字符集是utf8mb4,在这种情况下,char(N)类型就不是定长的了)

例子2:没有设置主键,也没有唯一索引,列允许有null值。

2.COMPACT 行格式详解

变长字段长度列表

NULL 值列表

记录头信息

记录的真实数据

3.InnoDb的默认行格式Dynamic 及 MySQL 是怎么处理行溢出?


大多数人使用mysql一般都是使用 InnoDB 引擎,这也是默认的表引擎。我们这里讨论的也是默认引擎InnoDB。

InnoDB引擎表的数据是存储在磁盘的。那可能会好奇表的每一行是如何存储在磁盘中的呢?行结构是如何的呢?

行格式(row_format),就是一条记录的存储结构。

InnoDB 提供了 4 种行格式,分别是 Redundant、Compact、Dynamic和 Compressed 行格式。

MySQL5.7 版本之后,默认使用 Dynamic 行格式。而Dynamic行格式与Compact是很相似的。就先从Compact讲起。

我们需要知道文件的存储位置和开启单个文件存储,这样方便我们查看行数据结构。

1.COMPACT 行格式长什么样?

注意:

  • 当表中没有变长字段时候,就没有变长字段长度列表;当表中的列都设置为not null,那null值列表也会没有。当表的列值中有null时候,蓝色框内就不会出现该列的值。
  • 若是没有主键或者唯一索引,  隐藏列中的第一个就是mysql生成的row_id。若是有的话,就是用户设置的主键值。
  • 变长字段长度列表和null值列表都是都是逆序存放

当中的一些详细信息后面会具体讲解的。

例子1:用户设置了主键值,列都是not null的。(默认字符集是utf8mb4,在这种情况下,char(N)类型就不是定长的了)

mysql> create table has_key(id bigint primary key,age int not null,name varchar(10) not null,idcard char(18) not null);       Query OK, 0 rows affected (0.05 sec)

mysql> insert into has_key values(1,22,'aaaaaa','123456'),(2,23,'bbbb','12345678');       
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from has_key;
+----+-----+--------+----------+
| id | age | name   | idcard   |
+----+-----+--------+----------+
|  1 |  22 | aaaaaa | 123456   |
|  2 |  23 | bbbb   | 12345678 |
+----+-----+--------+----------+
2 rows in set (0.00 sec)

找到文件存储位置/var/lib/mysql,之后找到对应的数据库,进入该目录,之后会看到一个文件has_key.ibd。

 之后使用命令hexdump -C has_key.ibd。

 分析:

  • 变长字段长度列表(绿框部分):

绿框中的是变长字段长度列表(逆序存放的)。name varchar(10)字段数据'aaaaaa'对应的长度是06(十进制是6);idcard字段数据'123456'字段长度是12(十进制是18),就对应char(18)中的18。char(N)类型的,只要数据字节大小不超过N的,都是开辟N字节的。

  • 记录头信息(红框部分):

这里字段都是not null,所以没有null值列表。接着红框的就是5字节大小的记录头信息,具体内容后面会讲解。

  • 隐藏列(黄线部分):

黄线的0x80 00 00 00 00 00 00 01是id主键值字段。id字段类型是bigint,8字节的。开头的 80 是因为,正数要以 1 开头,这是 mysql 规定的,0x80 的二进制就是 1000 0000。然后继续黄线的是6字节的TRX_ID,之后是7字节的ROLL_PTR。

  • 真实数据(蓝线部分):

蓝线的是表的列值。0x80 00 00 16就是类age的值22,0x61 61 61 61 61 61就是列name的值'aaaaaa',0x31 32 33 34 35 36就是列idcard的值'123456'。

例子2:没有设置主键,也没有唯一索引,列允许有null值。

mysql> create table no_key(id int, name varchar(10));
Query OK, 0 rows affected (0.05 sec)

mysql> insert into  no_key values(1,'2233'),(2,'aaaaa');
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from no_key;
+------+-------+
| id   | name  |
+------+-------+
|    1 | 2233  |
|    2 | aaaaa |
+------+-------+
2 rows in set (0.00 sec)

 之后在命令行执行hexdump -C no_key.ibd。

 分析:

  • 变长字段长度列表(蓝框部分):

变长的列数据是'2233',即是4字节大小。

  • null值列表(白框部分):

因为列中值可以为null,所以有null值列表。这是用二进制位来表示的,一个字节有8位,可以表示8个列值是否是null。也是逆序的。该位是0表示该列的值不为NULL。

  • 记录头信息(红框部分):

红框的就是5字节大小的记录头信息。

  • 隐藏列(黄线部分):

该表没有设置主键,所以使用mysql自动生成的row_id,6字节大小,0x00 00 00 00 03 00。之后6字节的rtx_id和7字节的roll_ptr。

  • 真实数据(蓝线部分):

蓝线的是真实数据,0x80 00 00 01就是列id的值1,0x32 32 33 33 就是列name的值'2233'。

这里需要注意的是:不管是用户生成主键还是让mysql自动生成主键,其值的存储位置都是在row_id位置的。只是长度不同而已。用户生成的主键长度大小就由用户设置的类型来确定,mysql自动生成的就是6字节。

还有要是用户生成的主键,那在蓝色的不为null的列表值中就不会保存主键的值,即是蓝色框内是不会保存主键列的值。

用这两个例子来讲解,应该可以让大家对存储的解构有个比较清晰的认知了。接下来就来讲下那些具体的细节。

2.COMPACT 行格式详解

变长字段长度列表

mysql中有变长字段varchar(N),那保存的数据就是其字节大小和其数据。变长字段列表就是用来存储这些的。而不是变长字段的不用保存其字节大小的。

这些变长字段的真实数据占用的字节数会按照列的顺序逆序存放has_key.ibd文件对应的图中的12 06,其对应的就是06 12。06是列name的长度大小,12(十进制是18)是列idcard的长度大小。

为什么要逆序存放,小林coding文章里有写,但我看了好像还是不太明白,等后续弄明白后,会补上这部分。

每个数据库表的行格式不一定有 变长字段字节数列表 。要是都是定长类型的列,那就没有该列表

上面的例子,变长字段的字符串都是比较短的,长度大小可以使用1字节来表示。如果变长列的内容占用的字节数比较多(长度超过255),可能就需要用2个字节来表示。

用多少字节来表示真实数据占用的字节数,InnoDB 有它的一套规则。

我们先声明一些字母的意思:

  • 用W表示某个字符集中表示一个字符最多需要使用的字节数,
  • 用M表示对于变长类型 VARCHAR(M) 类型能存储最多 M 个字符,
  • 用L来表示它实际存储的字符串占用的字节数。

比如类型是varchar(10) charset=utf8mb4的字符串能保存的长度大小最大是10*4=40字节。

规则:

  • 当最大字节数(MxW)没有超过255,那么使用1个字节来表示真正字符串占用的字节数。
  • 当最大字节数(MxW)没有超过255,分两种情况:

                如果L <= 127 ,则用1个字节来表示真正字符串占用的字节数。

                如果L >127 ,则用2个字节来表示真正字符串占用的字节数。

InnoDB在读记录的变长字段长度列表时先查看表结构,如果某个变长字段允许存储的最大字节 数大于255时,该怎么区分它正在读的某个字节是一个单独的字段长度还是半个字段长度? 

mysql是把该字节的最左的一个二进制位(最高位)作为标志位:如果该字节的最高位为0,那 该字节就是一个单独的字段长度(使用一个字节表示不大于127的二进制的最高位都为0), 如果该字节的最高位位为1,那该字节就是半个字段长度。

127的二进制表示为0b01111 1111(0是最高位)。

其规则总结:如果该可变字段允许存储的最大字节数(M×W)超过255字节并且真实存储的字节数(L) 超过127字节,则使用2个字节,否则使用1个字节

NULL 值列表

null值列表也是逆序存放的,其是每个列对应一个二进制位(bit)。

  • 二进制位的值为1时,代表该列的值为NULL。
  • 二进制位的值为0时,代表该列的值不为NULL。

 null值列表是一定要够整数字节的,只有7个列的话,那也要是一个字节。要是9列的话,那就需要2个字节来保存,以此类推的。如果使用的二进制位个数不足整数个字节,则在字节的高位补 0

若是所有的列都不允许为null,那就没有null值列表。

所以在设计数据库表的时候,符合业务要求情况下,通常都是建议将字段设置为 NOT NULL,这样可以至少节省 1 字节的空间(NULL 值列表至少占用 1 字节空间)。

记录头信息

记录头信息用于描述该记录的,它是由固定的5个字节组成,即40个二进制位,不同的位代表不同的意思;

  • 预留位1、2: 暂未使用
  • delete_mask: 标记该记录是否被删除
  • min_rec_mask: B+树的每层非叶子节点中的最小记录都会添加该标记
  • n_owned: 当前记录拥有的记录数
  • heap_no: 当前记录在记录堆中的位置信息
  • record_type: 当前记录类型;0-普通记录,1-B+树非叶子节点记录(即所谓的目录项记录),2-最小记录,3-最大记录
  • next_record: 下一条记录的相对位置

目前就简单了解下这些位表示的含义即可。

记录的真实数据

记录的真实数据除了自定义的列的数据以外,MySQL还会为每条记录默认的添加一些列(也称为隐藏列),具体的列如下:

每一行数据都会存储该事务id和回滚指针。

事务id,表示这个数据是由哪个事务生成的。 trx_id是必需的,占用 6 个字节。 

roll_ptr这个记录上一个版本的指针。roll_pointer 是必需的,占用 7 个字节。

当用户未设置数据表的主键时,MySQL会选择第一个非NULL的Unique列作为主键。而没有这个列的话,MySQL就会向数据表添加DB_ROW_ID字段用来作为主键(6字节大小)。

而当用户设置了主键的,那DB_ROW_ID就会让用户设置的主键代替,长度大小是用户设置的类型的大小。

那用户设置主键后,隐藏列后面的非null值的列就没有了主键存储的位置,因为这种情况主键存储在隐藏列的DB_ROW_ID位置了。

需要强调的是:记录的数据内容不包括字段值为NULL的数据内容

3.InnoDb的默认行格式Dynamic 及 MySQL 是怎么处理行溢出?

对于Dynamic行格式而言,其和compact行格式比较相似。

不同的在于,对待处理行溢出的处理及策略。

MySQL 中磁盘和内存交互的基本单位是页,一个页的大小一般是 16KB,也就是 16384字节,而一个 varchar(n) 类型的列最多可以存储 65532字节,一些大对象如 TEXT可能存储更多的数据,这时一个页可能就存不了一条记录。

这个时候就会发生行溢出,多的数据就会存到另外的「溢出页」中

compact会在该记录的数据内容的相应字段处存储该字段值前768个字节的数据,之后把剩余数据存储到溢出页

Dynamic、Compressed行格式会把记录中数据量过大的字段值全部存储到溢出页中。

compact处理行溢出

当然这20个字节中还包括这些分散在其他页面中的数据的占用的字节数,因为可能不止溢出一页。

Dynamic处理行溢出

为什么溢出页地址是20字节大小,为什么compact会存储768个字节数据?这些都不清楚,应该是代码中是这样编写的,也没有文章写其原因。

而compressed相比较dynamic行格式来说,前者会使用压缩算法对所有页面(自然也包括溢出页)进行压缩以减少存储占用。

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

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

相关文章

【间说八股】面试官:我看你这里用到了模板模式?你能不能说一下什么是模板模式

模板模式 行为模式&#xff1a;这类模式负责对象间的高效沟通和职责委派。 模板方法模式是一种行为设计模式&#xff0c; 它在超类中定义了一个算法的框架&#xff0c; 允许子类在不修改结构的情况下重写算法的特定步骤。 模板方法模式是一种行为设计模式&#xff0c;其核心思想…

【【C语言简单小题学习-1】】

实现九九乘法表 // 输出乘法口诀表 int main() {int i 0;int j 0;for (i 1; i < 9; i){for (j 1; j < i;j)printf("%d*%d%d ", i , j, i*j);printf("\n"); }return 0; }猜数字的游戏设计 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdi…

【STK】手把手教你利用STK进行仿真-STK软件基础02 STK系统的软件界面02 STK的主菜单

STK系统的菜单工具栏包含9个标准的下拉菜单&#xff0c;菜单项分别为“File”&#xff08;文件&#xff09;、“Edit”&#xff08;编辑&#xff09;、“Insert”&#xff08;插入&#xff09;、“View”&#xff08;查看&#xff09;、“Scenario”&#xff08;场景&#xff0…

文件操作和IO(2):Java中操作文件

目录 一、File的属性 二、File的构造方法 三、File的方法 四、代码示例 1、getName&#xff0c;getParent&#xff0c;getPath方法 2、getAbsolutePath&#xff0c;getCanonicalPath方法 3、exists&#xff0c;isDirectory&#xff0c;createNewFile方法 4、createNewF…

EtlCloud安装部署及简单应用

背景 最近碰到了一个数据同步的业务场景&#xff0c;客户要求生产环境的某些特定数据定时同步到指定的数据池中&#xff0c;并对数据池中的表名称有特殊要求&#xff0c;必须以t_xxxx_tablename的格式命名&#xff0c;其中xxxx为单位编号&#xff0c;tablename可以是应用中的表…

k8s资源管理之声明式管理方式

1 声明式管理方式 1.1 声明式管理方式支持的格式 JSON 格式&#xff1a;主要用于 api 接口之间消息的传递 YAML 格式&#xff1a;用于配置和管理&#xff0c;YAML 是一种简洁的非标记性语言&#xff0c;内容格式人性化&#xff0c;较易读 1.2 YAML 语法格式&#xff1a; ●…

Java网络通信UDP

目录 网络通信基础 UDP通信 服务器 1.想要使用UDP通信 要先打开DatagramSocket文件 端口号可以手动指定或系统随机分配 2.阻塞等待接收客户端数据&#xff1b;创建DatagramPacket接收客户端传来的数据 3.处理客户端传来的数据&#xff0c;并进行业务处理&#xff08;这里…

雷电将军部分技能AOE范围测试

简单说一下&#xff0c;以往的AOE范围数据大部分来自Dim提供的拆包文件或泄露的GM端控制台显示的距离数据&#xff0c;如《AOE范围学》中的数据&#xff0c;然而米哈游自1.6版本及以后未再公开泄露过GM端&#xff0c;因为一些原因Dim也没再更新拆包文件中角色技能参数相关的部分…

二路归并排序的算法设计和复杂度分析and周记

数据结构实验报告 实验目的: 通过本次实验&#xff0c;了解算法复杂度的分析方法&#xff0c;掌握递归算法时间复杂度的递推计算过程。 实验内容&#xff1a; 二路归并排序的算法设计和复杂度分析 实验过程&#xff1a; 1.算法设计 第一步&#xff0c;首先要将数组进行…

Vue3快速上手(十五)Vue3路由器使用和简单路由切换

一、路由的概念 1.1 路由及路由器 路由器&#xff1a;通常指的是我们家里上网用的路由器&#xff0c;通过网络接口&#xff0c;一根网线&#xff0c;链接至电脑&#xff0c;一般我们的电脑就可以上网了&#xff0c;多个网络接口&#xff0c;链接多个电脑&#xff0c;形成一组…

图神经网络实战——图论基础

图神经网络实战——图论基础 0. 前言1. 图属性1.1 有向图和无向图1.2 加权图和非加权图1.3 连通图和非连通图1.4 其它图类型 2. 图概念2.1 基本对象2.2 图的度量指标2.2 邻接矩阵表示法 3. 图算法3.1 广度优先搜索3.2 深度优先搜索 小结系列链接 0. 前言 图论 (Graph theory) …

springboot-基础-eclipse集成mybatis+使用方法+排错

备份笔记。所有代码都是2019年测试通过的&#xff0c;如有问题请自行搜索解决&#xff01; 目录 集成mybatis安装mybatis的jar包安装插件&#xff1a;mybatis-generator安装方法生成方法报错&#xff1a;java.lang.RuntimeException: Exception getting JDBC Driver mybatis注解…

深入了解Kafka的文件存储原理

Kafka简介 Kafka最初由Linkedin公司开发的分布式、分区的、多副本的、多订阅者的消息系统。它提供了类似于JMS的特性&#xff0c;但是在设计实现上完全不同&#xff0c;此外它并不是JMS规范的实现。kafka对消息保存是根据Topic进行归类&#xff0c;发送消息者称为Producer&…

【鸿蒙开发】第十五章 ArkTS基础类库-并发

1 简述 并发是指在同一时间段内&#xff0c;能够处理多个任务的能力。为了提升应用的响应速度与帧率&#xff0c;以及防止耗时任务对主线程的干扰&#xff0c;OpenHarmony系统提供了异步并发和多线程并发两种处理策略&#xff0c;ArkTS支持异步并发和多线程并发。并发能力在多…

部署bpmn项目实现activiti流程图的在线绘制

本教程基于centos7.6环境中完成 github开源项目: https://github.com/Yiuman/bpmn-vue-activiti软件&#xff1a;git、docker 1. 下载源代码 git clone https://github.com/Yiuman/bpmn-vue-activiti.git2. 修改Dockerfile文件 声明基础镜像&#xff0c;将项目打包&#xff…

LeetCode---386周赛

题目列表 3046. 分割数组 3047. 求交集区域内的最大正方形面积 3048. 标记所有下标的最早秒数 I 3049. 标记所有下标的最早秒数 II 一、分割数组 这题简单的思维题&#xff0c;要想将数组分为两个数组&#xff0c;且分出的两个数组中数字不会重复&#xff0c;很显然一个数…

AI又进化了

B站&#xff1a;啥都会一点的研究生公众号&#xff1a;啥都会一点的研究生 一直想做但没做的板块&#xff0c;整理一段时间内AI领域的前沿动态&#xff08;符合大多粉丝研究领域/感兴趣方向&#xff09;&#xff0c;了解了解外面世界发展成啥样了&#xff0c;一起看看吧~ 谷歌…

网关kong记录接口处理请求和响应插件 tcp-log-with-body的安装

tcp-log-with-body 介绍 Kong的tcp-log-with-body插件是一个高效的工具&#xff0c;它能够转发Kong处理的请求和响应。这个插件非常适用于需要详细记录API请求和响应信息的情景&#xff0c;尤其是在调试和排查问题时。 软件环境说明 kong version 2.1.4 - 2.8.3 [可用亲测]C…

8、Redis-Jedis、Lettuce和一个Demo

目录 一、Jedis 二、Lettuce 三、一个Demo Java集成Redis主要有3个方案&#xff1a;Jedis、Lettuce和Redisson。 其中&#xff0c;Jedis、Lettuce侧重于单例Redis&#xff0c;而Redisson侧重于分布式服务。 项目资源在文末 一、Jedis 1、创建SpringBoot项目 2、引入依赖 …

【PDF技巧】网上下载的pdf文件怎么才能编辑

不知道大家有没有遇到过网上下载的PDF文件不能编辑的情况&#xff0c;今天我们来详细了解一下导致无法编辑的原因即解决方法有哪些。 第一种原因&#xff1a;PDF文件中的内容是否是图片&#xff0c;如果确认是图片文件&#xff0c;那么我们想要编辑&#xff0c;就可以先使用PD…