一条MYSQL记录是如何储存的

news2024/12/25 1:14:59

主要还是看MYSQL默认的储存引擎 InnoDB

每个创建一个数据库 就会在/var/lib/mysql/ 目录里面创建一个以 database 为名的目录

目录里面包含以下三个文件

  • db.opt,用来存储当前数据库的默认字符集和字符校验规则。(数据库的数据)
  • t_order.frm ,t_order 的表结构会保存在这个文件。在 MySQL 中建立一张表都会生成一个.frm 文件,该文件是用来保存每个表的元数据信息的,主要包含表结构定义。(表的数据)
  • t_order.ibd,t_order 的表数据会保存在这个文件。表数据既可以存在共享表空间文件(文件名:ibdata1)里,也可以存放在独占表空间文件(文件名:表名字.ibd)。这个行为是由参数 innodb_file_per_table 控制的,若设置了参数 innodb_file_per_table 为 1,则会将存储的数据、索引等信息单独存储在一个独占表空间,从 MySQL 5.6.6 版本开始,它的默认值就是 1 了,因此从这个版本之后, MySQL 中每一张表的数据都存放在一个独立的 .ibd 文件。(那就是行的数据喽)

所以我们针对于一行数据的话 就要分析这个.ibd(独立表空间文件) 

表空间结构:

行:

数据库表中的记录都是按行(row)进行存放的,每行记录根据不同的行格式,有不同的存储结构。

页:

记录是按照行来存储的,但是数据库的读取并不以「行」为单位,否则一次读取(也就是一次 I/O 操作)只能处理一行数据,效率会非常低。(储存单位为行,读取单位为页)

因此,InnoDB 的数据是按「页」为单位来读写的,也就是说,当需要读一条记录的时候,并不是将这个行记录从磁盘读出来,而是以页为单位,将其整体读入内存。

默认每个页的大小为 16KB,也就是最多能保证 16KB 的连续存储空间。

页是 InnoDB 存储引擎磁盘管理的最小单元,意味着数据库每次读写都是以 16KB 为单位的,一次最少从磁盘中读取 16K 的内容到内存中,一次最少把内存中的 16K 内容刷新到磁盘中。

页的类型有很多,常见的有数据页、undo 日志页、溢出页等等。数据表中的行记录是用「数据页」来管理的

区:

我们知道 InnoDB 存储引擎是用 B+ 树来组织数据的。

B+ 树中每一层都是通过双向链表连接起来的,如果是以页为单位来分配存储空间,那么链表中相邻的两个页之间的物理位置并不是连续的,可能离得非常远,那么磁盘查询时就会有大量的随机I/O,随机 I/O 是非常慢的。

解决这个问题也很简单,就是让链表中相邻的页的物理位置也相邻,这样就可以使用顺序 I/O 了,那么在范围查询(扫描叶子节点)的时候性能就会很高。

那具体怎么解决呢?

在表中数据量大的时候,为某个索引分配空间的时候就不再按照页为单位分配了,而是按照区(extent)为单位分配。每个区的大小为 1MB,对于 16KB 的页来说,连续的 64 个页会被划为一个区,这样就使得链表中相邻的页的物理位置也相邻,就能使用顺序 I/O 了

段(segment)

表空间是由各个段(segment)组成的,段是由多个区(extent)组成的。段一般分为数据段、索引段和回滚段等。

  • 索引段:存放 B + 树的非叶子节点的区的集合;
  • 数据段:存放 B + 树的叶子节点的区的集合;
  • 回滚段:存放的是回滚数据的区的集合,

 InnoDB 行格式

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

  • Redundant 是很古老的行格式了, MySQL 5.0 版本之前用的行格式,现在基本没人用了。
  • 由于 Redundant 不是一种紧凑的行格式,所以 MySQL 5.0 之后引入了 Compact 行记录存储方式,Compact 是一种紧凑的行格式,设计的初衷就是为了让一个数据页中可以存放更多的行记录,从 MySQL 5.1 版本之后,行格式默认设置成 Compact。
  • Dynamic 和 Compressed 两个都是紧凑的行格式,它们的行格式都和 Compact 差不多,因为都是基于 Compact 改进一点东西。从 MySQL5.7 版本之后,默认使用 Dynamic 行格式。

所以我们选择直接学习Compact

首先它长这个鬼样子

我们先来看

记录的额外信息

变长字段长度列表

就拿我们常用的VARCHAR来说吧 我们都知道VARCHAR就是变长字段,所以我们储存的时候就会把它的长度给存进去,这样我们读数据的时候才知道要读多少,TEXT、BLOB 等变长字段也是这么实现的。

CREATE TABLE user (`id` int(11) NOT NULL,
       `name` VARCHAR(20) DEFAULT NULL,
       `phone` VARCHAR(20) DEFAULT NULL,
       `age` int(11) DEFAULT NULL,
       PRIMARY KEY (`id`) USING BTREE
     ) ENGINE = InnoDB DEFAULT CHARACTER SET = ascii ROW_FORMAT = COMPACT;

我们创建了这样一张表 注意我们设定了储存引擎为InnoDB(其实这个不用设置 默认的) 字符集ascii(所以一个字符一字节)行格式COMPACT

INSERT INTO user VALUES (1,'name','phone',18);

插入这样一条数据

name和phone 都是VARCHAR类型 也就说 他们两个都是变长数据类型

name长度 四字节 十六进制 为 0x04(刚才说了 这个列表里面储存的是变长字段的长度 储存的就是这个0x04)

phone长度 五字节 十六进制 为0x05

这些变长字段的真实数据占用的字节数会按照列的顺序逆序存放

所以里面存放的是 05 04 而不是04 05

 不过你说为啥它好好待着不行非得反着放呢

逆序存放的原因 

主要是因为「记录头信息」中指向下一个记录的指针,指向的是下一条记录的「记录头信息」和「真实数据」之间的位置,这样的好处是向左读就是记录头信息,向右读就是真实数据,比较方便。

就是这样的

「变长字段长度列表」中的信息之所以要逆序存放,是因为这样可以使得位置靠前的记录的真实数据和数据对应的字段长度信息可以同时在一个 CPU Cache Line 中,这样就可以提高 CPU Cache 的命中率

同样的道理, NULL 值列表的信息也需要逆序存放。

CPU Cache就是 CPU高速缓存 它的内存很小 要保证他们被一个CPU Cache命中 就要减小他们物理内存上的距离 如果逆序放的话 我们用name举例 它的长度就会在变长字段信息列表里面更后面的地方 这样就离它的真实数据(列1值)距离近了

NULL值列表

表中的某些列可能会存储 NULL 值,如果把这些 NULL 值都放到记录的真实数据中会比较浪费空间,所以 Compact 行格式把这些值为 NULL 的列存储到 NULL值列表中。

如果存在允许 NULL 值的列,则每个列对应一个二进制位(bit),二进制位按照列的顺序逆序排列。

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

另外,NULL 值列表必须用整数个字节的位表示(1字节8位),如果使用的二进制位个数不足整数个字节,则在字节的高位补 0

我们还是用刚才那行数据举例:

因为这行数据全有值 所以都不是null 也就全是0 因为id列是NOT NULL的所以不会有它的对应NULL值列表

不过按照要求 必须用整数个字节的位表示 目前只有3位 我们还得补5位 形成8位

所以实际数据为

 

NULL 值列表也不是必须的。

当数据表的字段都定义成 NOT NULL 的时候,这时候表里的行格式就不会有 NULL 值列表了

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

注意啊 是至少1字节 不是最多1字节 那我定义九个列 都是NOT NULL 那就 9位 就超过一字节了 

记录头信息

这里面的数据太多了 例举几个重要的

  • delete_mask :标识此条数据是否被删除。从这里可以知道,我们执行 detele 删除记录的时候,并不会真正的删除记录,只是将这个记录的 delete_mask 标记为 1。
  • next_record:下一条记录的位置。从这里可以知道,记录与记录之间是通过链表组织的。在前面我也提到了,指向的是下一条记录的「记录头信息」和「真实数据」之间的位置,这样的好处是向左读就是记录头信息,向右读就是真实数据,比较方便。
  • record_type:表示当前记录的类型,0表示普通记录,1表示B+树非叶子节点记录,2表示最小记录,3表示最大记录

记录真实数据

记录真实数据部分除了我们定义的字段,还有三个隐藏字段,分别为:row_id、trx_id、roll_pointer,

  • row_id

如果我们建表的时候指定了主键或者唯一约束列,那么就没有 row_id 隐藏字段了。如果既没有指定主键,又没有唯一约束,那么 InnoDB 就会为记录添加 row_id 隐藏字段。row_id不是必需的,占用 6 个字节。

  • trx_id

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

  • roll_pointer

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

varchar(n) 中 n 最大取值为多少?

MySQL 规定除了 TEXT、BLOBs 这种大对象类型之外,其他所有的列(不包括隐藏列和记录头信息)占用的字节长度加起来不能超过 65535 个字节

也就是说,一行记录除了 TEXT、BLOBs 类型的列,限制最大为 65535 字节,

这个varchar(n) 里面的n参数 其实是字符 而非字节

比如 ascii 字符集, 1 个字符占用 1 字节,而UTF-8字符集 最多3个字节表示一个字符

单字段的情况

假设数据库表只有一个 varchar(n) 类型的列且字符集是 ascii,在这种情况下, varchar(n) 中 n 最大取值是 65535 吗?

并不是哦 我们前面提到的行结构除了真实数据以外额外信息 也就是我们在储存的时候

  • 真实数据
  • 真实数据占用的字节数
  • NULL 标识,如果不允许为NULL,这部分不需要

假如说 我们允许为NULL 那么就需要用1字节的NULL列表

再有呢

每个变长字段的「变长字段长度」需要用多少字节表示?具体情况分为:

  • 条件一:如果变长字段允许存储的最大字节数小于等于 255 字节,就会用 1 字节表示「变长字段长度」;
  • 条件二:如果变长字段允许存储的最大字节数大于 255 字节,就会用 2 字节表示「变长字段长度」;

我们这里字段类型是 varchar(65535) ,字符集是 ascii,所以代表着变长字段允许存储的最大字节数是 65535,符合条件二,所以会用 2 字节来表示「变长字段长度」。

所以n值最大的是 65535-1-2 = 65532

当然,我上面这个例子是针对字符集为 ascii 情况,如果采用的是 UTF-8,varchar(n) 最多能存储的数据计算方式就不一样了:

  • 在 UTF-8 字符集下,一个字符最多需要三个字节,varchar(n) 的 n 最大取值就是 65532/3 = 21844。

上面所说的只是针对于一个字段的计算方式。

我的理解是 utf8一个字符最多需要三个字节 两万多个字符总不能都是3字节吧 少点就把3字节的额外数据空间给余出来了

行溢出后,MySQL 是怎么处理的?

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

如果一个数据页存不了一条记录,InnoDB 存储引擎会自动将溢出的数据存放到「溢出页」中。在一般情况下,InnoDB 的数据都是存放在 「数据页」中。但是当发生行溢出时,溢出的数据会存放到「溢出页」中。

当发生行溢出时,在记录的真实数据处只会保存该列的一部分数据,而把剩余的数据放在「溢出页」中,然后真实数据处用 20 字节存储指向溢出页的地址,从而可以找到剩余数据所在的页。大致如下图所示。

上面这个是 Compact 行格式在发生行溢出后的处理。

Compressed 和 Dynamic 这两个行格式和 Compact 非常类似,主要的区别在于处理行溢出数据时有些区别。

这两种格式采用完全的行溢出方式,记录的真实数据处不会存储该列的一部分数据,只存储 20 个字节的指针来指向溢出页。而实际的数据都存储在溢出页中,看起来就像下面这样:

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

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

相关文章

html---链接跳转案例

目录 一、要求:设置一个网页如下图所示,可实现首页、列表页、详情页、登录页链接 二、实现:实现代码及截图如下 三、寄语 一、要求:设置一个网页如下图所示,可实现首页、列表页、详情页、登录页链接 二、实现&…

飞书深诺、恒生面试(部分)(未完全解析)

飞书深诺 说一下你对SaaS项目的理解?数据隔离是怎么处理的?Answer: 我们采用的是SAAS服务多租户数据隔离架构中的1.3共享数据库,通过租户ID来隔离,成本最低,隔离级别最低。Q:有没有开发隔离的中间件&#x…

shopee,lazada卖家如何提高店铺权重,带来更多销量

1、优化标题关键词 标题关键词可以在很大程度上影响产品的显示,如果商店自然流量低,必须检查标题是否选择合适的关键词,如果关键词不合适需要优化并进行更换,可以选择一些准确的长尾关键词获得准确的流量,如果收集产品…

在Linux中查找用户帐户信息和登录详细信息的11种方法

在Linux系统中,用户帐户和登录详细信息对于系统管理和安全非常重要。本文将介绍 11 种在 Linux 系统查找用户相关信息的有用方法。这里,我们会讲解在系统中获取一个用户账户详细信息、展示登录详细信息以及用户行为数据的命令。 首先,我们会…

【ARM7.5作业】

作业1 作业2 代码实现: head.h #ifndef __UART4_H__ #define __UART4_H__#include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_uart.h"//初始化相关操作 void hal_uart4_init();//发送一个字符 void h…

Mockito的使用案例

流水线的单元测试 代码没有覆盖到,使用的是Mockito测试框架,原来是Mockito没有正确使用 package com.hmdp;import com.hmdp.controller.BlogController; import com.hmdp.entity.Blog; import com.hmdp.service.IBlogService; import com.hmdp.service.…

文心一言 VS 讯飞星火 VS chatgpt (55)-- 算法导论6.3 1题

文心一言 VS 讯飞星火 VS chatgpt (55)-- 算法导论6.3 1题 一、参照图6-3 的方法,说明 BUILD-MAX-HEAP在数组 A(5,3,17,10,84,19,6,22,9)上的操作…

C#(五十二)之线程

线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。 C#线程操作,需要使用到Thread类。 使用命名空间 using Sy…

grafana+prometheus+pushgateway+flink可视化实时监控

文章目录 一、各部分介绍二、安装配置1、安装pushgateway2、安装Prometheus3、Grafana 安装 三、测试使用 一、各部分介绍 采集层 flink APP和linux system两部分,是我们要收集指标数据的组件传输层 Pushgateway:是一个推送收集和推送数据的组件 Node_ex…

【网络安全带你练爬虫-100练】第5练:爬虫的翻页操作+错误情况过滤

目录 一、翻页分析: 二、代码逻辑 1、入口程序修改 2、page参数传入 三、完整代码 1、运行结果 2、错误分析: 3、缺陷代码: 4、完善逻辑: 5、完善代码: (说在前面,任何逻辑都有很多方…

Python软件安装后,Scripts文件夹下为空解决办法

安装Python后,需要使用pip,发现Scripts下为空,无法install pip: 解决办法: cmd进入Windows命令提示符界面,进入Python的安装目录,并使用python -m ensurepip命令执行即可:

R语言——字符串处理

paste(abc, def, gh, sep ) #粘贴字符串 substr(abcdefg, 2, 3) # 取特定字符串 gsub(abc, , c(abc, abcc, abcbc)) # 将字符串中abc替换为空 strsplit(a;b;c, ;, fixed T) # 按照;切分字符串 strsplit(a222b2.2c, 2.2, fixed F) # 按照正则表达式分隔,这里的.是…

15.1 BP神经网络实现图像压缩——了解神经网络在图像处理方面的应用(matlab程序)

1.简述 BP神经网络现在来说是一种比较成熟的网络模型了,因为神经网络对于数字图像处理的先天优势,特别是在图像压缩方面更具有先天的优势,因此,我这一段时间在研究神经网络的时候同时研究了一下关于BP网络实现图像压缩的原理和过程,并且是在MATLAB上进行了仿真的实验,结果发现设…

Java:ThreadLocal解析

Java:ThreadLocal解析 前言一、 什么是ThreadLocal?二、ThreadLocal的内存泄漏问题1.什么是内存泄漏?2.为什么会出现内存泄漏问题?3.如何解决内存泄漏问题?(1)ThreadLocal会自动清除key为null的…

层级在BW4HANA中的处理

目录 1.从flatfile加载层级的处理 2. 从ERP数据源抽取区间层级到BW 2.1 在ERP中的层级数据源 2.1.1 PSA格式和IDoc格式的区别 2.1.2 怎么查看Interval的字段 2.1.3 如何在S4里查看层级数据源结构 2.1.4 关于时间相关层级date to和date from 1.从flatfile加载层级的处理 层…

【快速开始】一个简单的Flask-SocketIO应用,完成后端推送消息接收与关闭

效果图 先看运行效果图 OK,下面开始。 安装环境 本人使用环境及版本: Anaconda: 虚拟环境: Python版本:3.8.13 安装包及版本: Flask-SocketIO:5.3.4 eventlet:0.33.3 快速开…

BUU [ZJCTF 2019]NiZhuanSiWei

BUU [ZJCTF 2019]NiZhuanSiWei 先看题目,提示了useless.php。 猜到了flag在哪。 没啥可以学的,直接上exp。 ?textdata://text/plain,welcome to the zjctf &filephp://filter/readconvert.base64-encode/resourceuseless.php &passwordO:4:&q…

工作学习总结:安卓地图SDK测试--前置准备阶段8天

工作学习总结:安卓地图SDK测试--前置准备阶段8天 安卓地图SDK测试--前置准备阶段8天前置知识:1、安卓环境开发部署2、高德 SDK demo 如何进行调试(1)SHA1 密钥无法获取(2)下载了高德 3D 地图 demo&#xff…

Leetcode---352周赛

周赛题目 2760. 最长奇偶子数组 2761. 和等于目标值的质数对 2762. 不间断子数组 2763. 所有子数组中不平衡数字之和 一、最长奇偶子数组 这题的数据范围允许用暴力来做,只要我们分别枚举左端点left和右端点right,然后看区间[left,right]是否符合题目条…

stable diffusion 百宝书

文章目录 0. 环境搭建0.1 Windows0.1.1 git环境安装0.1.2 python 环境搭建0.1.2.1 配置pip国内镜像源 0.1.3 stable diffusion环境搭建0.1.3.1 远程访问Stable diffusion0.1.3.2 模型 Lora下载 1. 基础知识1.1 Stable Diffusion Webui及基础参数1.2 参数说明1.2.1 采样方法1.2.…