详解MYSQL表空间

news2025/4/15 20:09:58

目录

表空间文件

表空间文件结构

行格式

Compact 行格式

变长字段列表

NULL值列表

记录头信息

列数据

溢出页

数据页


当我们使用MYSQL存储数据时,数据是如何被组织起来的?索引又是如何组织的?在本文我们将会解答这些问题。


表空间文件

当我们建立一个表后,会生成两个文件:.frm文件,.ibd文件(实际上每个存储引擎下生成的文件不同,本文主要以 InnoDB为例)。假设建立一个名为 test 表,会生成这两个文件: 

test.frm  
test.ibd

.frm 文件用于存储表结构定义的文件。包含了表的列定义、索引定义、约束条件以及其他与表结构相关的元数据信息。

.ibd 文件用于存储表的数据和索引,包含了表的实际数据行以及各种索引数据结构。也叫做表空间文件。

表空间文件负责了实际数据的存储


表空间文件结构

表空间文件的由以下部分组成:段(Segment),区(Extent),页(Page),行(Row)

段(Segment)

表空间文件由各种段组成,如数据段,索引段,回滚段等。

数据段:存储表的数据,负责实际数据的存储,存储的是B+树索引的叶子节点。

索引段:存储B+树的索引节点,也就是非叶子节点。

回滚段:回滚段实际就是 undolog的存储结构,主要用于存储 undo log 以及管理与事务回滚相关的信息。

区(Extent):

区的存在实际上是为了便于顺序IO,提高IO的效率。

在 InnoDB 中以 B+树 的形式组织数据,无论有没有建立索引,都会默认为主键(如何没有主键会有隐藏主键字段)建立B+索引。

在B+索引的每个节点都是一个页,一个页的大小为 16 KB,而B+树的每一层的节点都会链接为双向链表,使得每一层节点之间逻辑连续,但在物理上并不连续,这就会导致在搜索时会产生大量的随机IO(随机IO速度远小于顺序IO)。

为了解决这一点,MYSQL在有大量数据的表空间时分配空间时会以区为单位分配,一个区的小为 1 MB,远大于一个页的大小,这样同一层节点不仅在逻辑上连续也在物理上连续,搜索时也就不是随机IO而为顺序IO,提升了IO效率。

(Page):

InnoDB中一个页的大小为 16 KB,页是InnoDB管理资源的最小单位,读取和写入数据时都是页为单位,也就是说即使只读取/写入一行数据也会读取/写入一整个页,实际上这是一种提升IO效率的方式,感兴趣的读者可以搜索一下局部性原理与MYSQL的BufferPool。

页的类型有很多种:数据页,溢出页,undolog页等等,在下文我们会详细解释。

行(Row):

数据库的记录以行为单位存储,不同的格式有不同的存储结构,下面我们来详细了解行的格式。


行格式

在MYSQL中常见的行格式有很多种:

  • Compact 行格式
    • 这是 MySQL 5.0 版本开始引入的默认行格式。它采用了紧凑的存储方式,对于固定长度的字段,会按照定义的顺序依次存储,节省了存储空间。对于可变长度的字段,会在记录的开头使用额外的字节来记录每个可变长度字段的长度。
    • 适用于大多数常规的数据库表,尤其是包含较多固定长度字段的表,能够有效地利用存储空间,提高查询效率。
  • Redundant 行格式
    • 是 MySQL 5.0 之前的默认行格式。它的存储方式相对简单,每行记录都包含了一些固定的头部信息和字段数据。与 Compact 行格式相比,Redundant 行格式在存储可变长度字段时,使用了更多的空间来记录字段长度信息,因此会占用更多的存储空间。
    • 主要用于与旧版本的 MySQL 兼容。如果数据库中存在一些历史表,并且对兼容性有要求,可能会使用 Redundant 行格式。
  • Dynamic 行格式
    • 从 MySQL 5.7 版本开始引入。它与 Compact 行格式类似,但对于可变长度字段的存储方式有所不同。Dynamic 行格式会将长度超过一定阈值的可变长度字段存储在数据页的外部,只在数据页中保留一个指向外部存储位置的指针,这样可以减少数据页中碎片的产生,提高数据页的利用率。
    • 特别适用于包含大文本字段(如 VARCHAR、TEXT 等)或 BLOB 类型字段的表。当这些字段的值较大时,使用 Dynamic 行格式可以有效地避免数据页的分裂和碎片问题,提高数据库的性能。
  • Compressed 行格式
    • 同样是在 MySQL 5.7 版本引入。这种行格式在存储数据时会对数据进行压缩,以减少存储空间的占用。它使用了 zlib 压缩算法对数据页进行压缩,从而可以大大降低数据在磁盘上的存储量。
    • 适用于对存储空间要求较高的场景,如数据仓库或一些需要长期保存大量历史数据的数据库。通过压缩数据,可以减少磁盘 I/O 操作,提高查询性能,同时也降低了存储成本。

Compact 行格式

本文我们主要了解Compact 行格式,Compact 行格式由四部分组成:变长字段列表,NULL值列表,记录头信息,行数据。

变长字段列表

在MYSQL中存在一些变长字段,比如VARCHAR,TEXT。这些字段的长度并不固定,但长度都记录在变长字段列表中。

变长字段的长度信息会按照列的逆序存储在变长字段列表中。每个变长字段的长度使用 1 个或 2 个字节来表示,具体取决于字段的最大长度和实际长度:

  • 字段最大长度小于等于 255 字节:使用 1 个字节来存储该字段的实际长度。
  • 字段最大长度大于 255 字节:当实际长度小于 128 字节时,使用 1 个字节存储;当实际长度大于等于 128 字节时,使用 2 个字节存储。

假设有下面一张表

CREATE TABLE test_table (
    col1 VARCHAR(10),
    col2 VARCHAR(200),
    col3 VARCHAR(300)
);

当插入一行数据 ('abc', 'defghijklmnopqrst', 'uvwxyz') 时,变长字段列表的存储情况如下:

  • col3 的实际长度为 6 字节,由于其最大长度 300 大于 255 且实际长度小于 128,所以用 1 个字节存储长度 6。
  • col2 的实际长度为 18 字节,同理用 1 个字节存储长度 18。
  • col1 的实际长度为 3 字节,其最大长度 10 小于等于 255,用 1 个字节存储长度 3。

实际存储情况如下:

这里需要注意如果变长字段为NULL值,变长字段列表不会记录该列的长度信息。

NULL值列表

NULL值列表用于标识行数据中的NULL值,为了节约空间列数据并不存储NULL值,而是由NULL值列表标识是否为NULL。NULL值列表以位为基本单位,一个位标记一个允许为NULL值的列。

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

NULL值列表的长度是按照字节进行存储,不足一个字节会补足到一个字节。比如,若表中只有 3 个可允许为NULL的列,NULL值列表仍然会占用 1 个字节(8 位),只是高 5 位没有实际意义。与变长列表相同,NULL值列表也是倒序存储。

假设有一个表 test_table 包含三列 col1、col2、col3,且都允许为 NULL:

CREATE TABLE test_table (
    col1 INT NULL,
    col2 VARCHAR(10) NULL,
    col3 DECIMAL(10, 2) NULL
);

当插入一行数据,其中 col1 和 col2 为 NULL,col3 有值时,NULL值列表如下:

实际上变长字段列表与NULL值列表并不是任何表中都存在,如果表中所有列都被定义为 NOT NULL,即不允许存储 NULL 值,那么就不会有 NULL 值列表。同理如果表中所有列都是固定长度的数据类型,那么就不需要变长字段列表。

记录头信息

记录头信息存储的是数据记录的一些元数据,主要有以下数据:

名称长度说明
预留位11目前未使用
预留位21目前未使用
delete_mask1删除标记位,值为 1 表示该记录已被删除,但还未被真正从磁盘中移除,属于逻辑删除。后续会通过页合并等操作进行物理删除。
min_rec_mask1仅在 B+ 树的非叶子节点中使用,用于标记该记录是否为最小的记录。
n_owned4表示该记录拥有的记录数。InnoDB 为了提高记录查找效率,会将页中的记录分组,每组记录有一个带头记录,n_owned 记录了带头记录所在组的记录数量。
heap_no13表示该记录在页中的堆(Heap)中的位置编号。在 InnoDB 中,新插入的记录会按照一定规则分配一个 heap_no 编号。
record_type3记录类型,常见取值及含义如下:
0:普通记录
1:B+ 树非叶子节点记录
2:Infimum 记录(页中最小的虚拟记录)
3:Supremum 记录(页中最大的虚拟记录)
next_record16指向下一条记录的相对偏移量。通过 next_record 可以将页中的记录串联成一个单向链表,方便进行记录的遍历和查找。

 这里重点介绍两个属性:delete_mask,next_record。

delete_mask 是一个一位的标志位,主要用于逻辑删除,当我们执行一个删除操作时,并不会在物理上将行记录删除,只会将记录的 delete_mask 标志位置为 1,表示该记录已被删除。在后续的查询操作中,数据库会忽略 delete_mask 为 1 的记录。

这些被逻辑删除的记录会在后续合适的时机被真正从磁盘中移除,比如在进行页合并、页分裂或者垃圾回收操作时。这样做的可以避免频繁的磁盘 I/O 操作,提高性能。

next_record 是一个长度为 16 位的属性,它存储的是指向下一条记录的相对偏移量。指向的是下一条记录的记录头信息和列数据之间的位置。通过 next_record,InnoDB 将页中的记录串联成一个单向链表,从而方便进行记录的遍历和查找。

列数据

列数据分两部分,一部分为隐藏字段,另一部分即表中各列具体存储的数据。

隐藏字段主要用于MVCC机制,不了解的同学可以参考这篇博客:MYSQL多版本并发控制(MVCC)_支持多版本并发控制-CSDN博客

至此行格式我们介绍完毕,下面我们来了解一下各种页。


溢出页

首先就是溢出页,溢出页是用于存储超过单个页面容量的数据的一种机制。MySQL 的数据存储是以页为单位的,默认页大小通常是 16KB。当某些数据类型(如 TEXT、BLOB 等)存储的内容过长,超过了一个页所能容纳的大小时,就会使用溢出页来存储额外的数据。

溢出页与普通的数据页结构类似,但在存储逻辑上有所不同。在campact格式中,当数据需要溢出时,会在原数据页中保留一部分数据,然后通过一个20位的指针指向溢出页。剩余的数据存放在溢出页中。

在其他行格式中处理方法类似,但只保留指针,不在原数据页中存放数据:

如果一个溢出页放不下,溢出页之间会通过双向链表进行链接,以便能够顺序地访问所有溢出的部分。这样,即使数据分布在多个溢出页上,也可以通过链表结构高效地遍历和读取。

数据页

数据页是 InnoDB 存储的基本单位,B+索引一个节点就是一个数据页。数据页的结构如下:

  • 文件头部(File Header):包含了一些关于页的通用信息,用于页的管理和识别。
  • 页头部(Page Header):记录了页的状态信息和一些与页内数据组织相关的信息。
  • 最大和最小记录(Infimum and Supremum Records):虚拟的记录,分别位于页的开头和结尾,用于界定页内记录的范围。
  • 用户记录(User Records):实际存储用户数据的部分,这些记录按照主键顺序排列。
  • 空闲空间(Free Space):页中尚未被使用的部分,用于存储新插入的记录。
  • 页目录(Page Directory):页目录是一个数组,用于快速定位页内的记录。
  • 文件尾部(File Trailer):用于校验页的完整性。

文件头部主要有以下字段:页校验和,页号,上一页号和下一页号,页类型。页校验和主要用于校验页的完整性。每个页都有一个唯一的编号,用于在表空间中定位该页。上一页号与下一页号实际上将数据页组成了一个链表结构,使数据逻辑上连续。

在数据页的用户记录中,行数据同样以链表方式按主键大小顺序连接起来。这一点我们在行格式已经结束了,那么如果要在页内检索数据怎么能快速定位呢?肯定有不少同学想到了二分搜索,但实际上行数据是以单链表的形式组织起来的,这时页目录就可以有效帮助进行检索。页目录的结构如下:

页目录将页内的记录分成若干个组,每个组的最后一条记录的偏移量会被记录在页目录中。通过二分查找页目录,可以快速定位到目标记录所在的组,然后在该组内进行线性查找。

参考

从数据页的角度看 B+ 树 | 小林coding

MySQL 一行记录是怎么存储的? | 小林coding

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

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

相关文章

[Windows] 音速启动 1.0.0.0

[Windows] 音速启动 链接:https://pan.xunlei.com/s/VONiGZhtsxpPzze0lDIH-mR9A1?pwdxu7f# [Windows] 音速启动 1.0.0.0 音速启动是一款桌面管理软件,以仿真QQ界面的形式结合桌面工具的特点,应用于软件文件夹网址的快捷操作。

Hyper-V 虚拟机配置静态IP并且映射到局域网使用

环境 win11hyper-v麒麟v10 配置 编辑文件 vi /etc/sysconfig/network-scripts/ifcfg-eth0文件内容 GATEWAY 需要参考网络中配置的网关地址 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic DEFROUTEyes IPV4_FAILURE_FATALno IPV6INITyes IPV6_AUTOCONFyes …

操作系统基础:06 操作系统历史

我们前面已经讲过了操作系统的基本轮廓、启动过程以及系统调用等相关内容,就如同揭开了钢琴的盖子,对操作系统有了初步的表面认识。从现在起,我们要更深入地剖析操作系统,就像分解钢琴一样,探究其各个部分的构成、原理…

【大模型微调】如何解决llamaFactory微调效果与vllm部署效果不一致如何解决

以下个人没整理太全 一、生成式语言模型的对话模板介绍 使用Qwen/Qwen1.5-0.5B-Chat训练 对话模板不一样。回答的内容就会不一样。 我们可以看到例如qwen模型的tokenizer_config.json文件,就可以看到对话模板,一般同系列的模型,模板基本都…

【2025最新】windows本地部署LightRAG,完成neo4j知识图谱保存

之前在服务器部署neo4j失败,无奈只能在本地部署,导致后期所有使用的知识图谱数据都存在本地,这里为了节省时间,先在本地安装LigthRAG完成整个实验流程,后续在学习各种服务器部署和端口调用。从基础和简单的部分先做起来…

14、nRF52xx蓝牙学习(串口 UART 和 UARTE 外设应用)

一、UART 功能描述 串口 UART 也称为通用异步收发器。是各种处理器中常用了通信接口,在 nRF52 芯片中, UART 具有以下特点: ● 全双工操作 ● 自动流控 ● 奇偶校验产生第 9 位数据 串口 UART 的数据发送与接收流程 : ◆硬件配置…

DeepSeek轻松入门教程——从入门到精通

大家好,我是吾鳴。 今天吾鳴要给大家分享一份DeepSeek小白轻松入门指导手册——《DeepSeek 15天指导手册,从入门到精通》。指导手册分为基础入门对话篇、效率飞跃篇、场景实战篇、高手进化篇等,按照指导手册操作,DeepSeek从入门到…

Vue2 老项目升级 Vue3 深度解析教程

Vue2 老项目升级 Vue3 深度解析教程 摘要 Vue3 带来了诸多改进和新特性,如性能提升、组合式 API、更好的 TypeScript 支持等,将 Vue2 老项目升级到 Vue3 可以让项目获得这些优势。本文将深入解析升级过程,涵盖升级前的准备工作、具体升级步骤…

WXJ196微机小电流接地选线装置使用简单方便无需维护

WXJ196微机小电流接地选线装置,能在系统发生单相接地时,准确、迅速地选出接地线路母 线。使用简单方便,无需维护,可根据用户需要将相关信息通过通信接口传给上级监控系统, 适用于无人值守变电站。 2 功能及特点 全新的…

Java第四节:idea在debug模式夏改变变量的值

作者往期文章 Java第一节:debug如何调试程序(附带源代码)-CSDN博客 Java第二节:debug如何调试栈帧链(附带源代码)-CSDN博客 Java第三节:新手如何用idea创建java项目-CSDN博客 步骤一 在需要修改…

门极驱动器DRV8353M设计(二)

目录 13.3.4.4 MOSFET VDS 感测 (SPI Only) 13.3.5 Gate Driver保护回路 13.3.5.1 VM 电源和 VDRAIN 欠压锁定 (UVLO) 13.3.5.2 VCP 电荷泵和 VGLS 稳压器欠压锁定 (GDUV) 13.3.5.3 MOSFET VDS过流保护 (VDS_OCP) 13.3.5.3.1 VDS Latched Shutdown (OCP_MODE 00b) 13.…

学点概率论,打破认识误区

概率论是统计分析和机器学习的核心。掌握概率论对于理解和开发稳健的模型至关重要,因为数据科学家需要掌握概率论。本博客将带您了解概率论中的关键概念,从集合论的基础知识到高级贝叶斯推理,并提供详细的解释和实际示例。 目录 简介 基本集合…

NVIDIA AI Aerial

NVIDIA AI Aerial 适用于无线研发的 NVIDIA AI Aerial 基础模组Aerial CUDA 加速 RANAerial Omniverse 数字孪生Aerial AI 无线电框架 用例构建商业 5G 网络加速 5G生成式 AI 和 5G 数据中心 加速 6G 研究基于云的工具 优势100% 软件定义通过部署在数字孪生中进行测试6G 标准化…

OpenCV 关键点定位

一、Opencv关键点定位介绍 关键点定位在计算机视觉领域占据着核心地位,它能够精准识别图像里物体的关键特征点。OpenCV 作为功能强大的计算机视觉库,提供了多种实用的关键点定位方法。本文将详细阐述关键点定位的基本原理,深入探讨 OpenCV 中…

arm_math.h、arm_const_structs.h 和 arm_common_tables.h

在 ​​FOC(Field-Oriented Control,磁场定向控制)​​ 中,arm_math.h、arm_const_structs.h 和 arm_common_tables.h 是 CMSIS-DSP 库的核心组件,用于实现高效的数学运算、预定义结构和查表操作。以下是它们在 FOC 控…

buuctf sql注入类练习

BUU SQL COURSE 1 1 实例无法访问 / Instance cant be reached at that time | BUUCTF但是这个地方很迷惑就是这个 一个 # 我们不抓包就不知道这个是sql注入类的判断是 get 类型的sql注入直接使用sqlmap我们放入到1.txt中 目的是 优先检测 ?id1>python3 sqlmap.py -r 1.t…

具身导航中的视觉语言注意力蒸馏!Vi-LAD:实现动态环境中的社会意识机器人导航

作者:Mohamed Elnoor 1 ^{1} 1, Kasun Weerakoon 1 ^{1} 1, Gershom Seneviratne 1 ^{1} 1, Jing Liang 2 ^{2} 2, Vignesh Rajagopal 3 ^{3} 3, and Dinesh Manocha 1 , 2 ^{1,2} 1,2单位: 1 ^{1} 1马里兰大学帕克分校电气与计算机工程系, 2…

全局前置守卫与购物车页面鉴权

在很多应用里,并非所有页面都能随意访问。例如购物车页面,用户需先登录才能查看。这时可以利用全局前置守卫来实现这一鉴权功能。 全局前置守卫的书写位置在 router/index.js 文件中,在创建 router 对象之后,暴露 router 对象之前…

layui 弹窗-调整窗口的缩放拖拽几次就看不到标题、被遮挡了怎么解决

拖拽几次,调整窗口的缩放,就出现了弹出的页面,右上角叉号调不出来了,窗口关不掉 废话不多说直入主题: 在使用layer.alert layer.confirm layer.msg 等等弹窗时,发现看不到弹窗,然后通过控制台检查代码发现…

网络空间安全(57)K8s安全加固

一、升级K8s版本和组件 原因:K8s新版本通常会引入一系列安全功能,提供关键的安全补丁,能够补救已知的安全风险,减少攻击面。 操作:将K8s部署更新到最新稳定版本,并使用到达stable状态的API。 二、启用RBAC&…