前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >MySQL进阶突击系列(09)数据磁盘存储模型 | 一行数据怎么存?

MySQL进阶突击系列(09)数据磁盘存储模型 | 一行数据怎么存?

原创
作者头像
拉丁解牛说技术
发布2025-02-19 17:22:32
发布2025-02-19 17:22:32
1972
举报
文章被收录于专栏:MySQL突击进阶MySQL突击进阶

读书笔记:当一个人存在‘只有目标综合征’,容易陷入全能自恋状态。具体表现为,当设定一个一个目标后,会自动忽略时间、空间、过程维度信息,然后急切甚至要求立即实现目标。如果目标没有实现,将出现自我否定和破灭的想法。简单的把目标和‘我’混为一体,这是单一自恋维度的病态,会让人进入焦虑状态。打破单一自恋维度的方法,可以与周边建立连接,进入关系维度体验现实,让大自然、亲情友情、还有社会百态来增强现实体验,摆脱思维孤独。


一、前言背景

二、数据存储整体逻辑

2.1 存储地址和具体文件

2.2 表空间-段segement

2.3 表空间-区extent

2.4 表空间-页page

2.5 表空间-行数据row

三、如何设计存放一行数据

3.1 行格式类型

3.2 具体数据构成

3.3 变长字段、定长字段存储

3.4 空null存储

3.5 什么是行溢出现象?


一、前言背景

MySQL的一行数据在磁盘里是如何存放的?一行数据存放后,数据又是如何读取的?

这两个问题如果详细展开的话,各自至少需要一篇文章才能讲齐全。涉及底层的物理存储、模型设计逻辑,相对而言,会有一些难度以及枯燥乏味,导致去了解和探索的意愿相对也会少一些。保持尽可能详尽又易于理解,篇幅不能过长,避免大家阅读疲惫,今天我们仅探讨【一行数据内容在磁盘如何存放】问题。

二、数据存储整体逻辑

从逻辑上来讲,一个数据库里由多个表组成,每个表由多行数据组成。每个表的表空间由段segment、区extent、页page、行row组成。具体:

一个表空间由多个段segement组成。

一个段segement有多个数据区extent组成,256个数据区,是一组数据区。

一个数据区extent对应64个连续的数据页,也就是一个数据区是1Mb大小。

一个页page,是16kb大小,每个页里面存放具体行数据。

接下来,我们具体展开看看表空间的各个部分作用。

2.1 存储地址和具体文件

一个数据库以及相关表的数据在磁盘存放目录地址,可以通过命令查看:

show variables like '%datadir%';

默认数据文件地址目录是:/usr/local/var/mysql

每个数据库,都有单独的文件夹,存放对应表数据文件。

比如skd的数据库,里面有很多个表文件。

在InnoDB存储引擎里,一个表数据由.frm和.ibd后缀文件存储。其中xx.ibd文件存放的就是具体的数据内容,这个文件也被称为独占表空间。而frm文件存放的是表元数据信息和表结构信息。

2.2 表空间-段segement

段是表空间的逻辑概念,它类型有:数据段、索引段、回滚段。

每个表通常只有一个数据段,里面存储的是表实际数据,也就是B+树的叶子节点。

而索引段,就是我们存储引擎InnoDB索引系列说的,B+树的非叶子节点,存储表的索引信息。

而回滚段,就是对应undo log回滚日志,管理相关回滚日志的区数据。

2.3 表空间-区extent

表空间的一个区是由连续的64个页组成的空间,区的大小固定为1MB。区的提出和设计,主要是为了使逻辑上相邻的页节点在物理上也尽可能靠近,也就是为了让B+树的双向链表相邻的两个页尽可能挨着,从而减少磁盘随机IO的时间,尽可能实现顺序读写,提升IO效率。

如果没有区的概念,在B+树索引上,由于每一层都是双向链表,如果单纯以页为单位来分配存储空间,那么链表中相邻的两个页之间的物理位置并不是连续的,可能离得很远,在磁盘读取多个数据页时,可能就会出现大量随机IO,而随机IO速度是非常慢的。

所以,当表中数据量较大的时候,MySQL为索引分配空间就以区extent为单位分配,确保相关索引数据页地址是连续的,从而为后续数据读写实现顺序读写。

2.4 表空间-页page

数据页相关分享,在我们系列之前01-08说过很多次,MySQL数据库读取数据并不是以行为单位,而是按页为最小单位去读写的。每个页的默认大小是 16kb。

InnoDB 存储引擎磁盘管理的最小单元就是页page,数据库每次读写都是以 16KB 为单位,也就是每次从磁盘中最少会读取16kb数据到bufferpool中,或者从内存中把16kb数据刷到磁盘。

2.5 表空间-行数据row

每页数据,可能存放有多行数据。如果一行数据足够大,比如text类型,一个文本内容非常大,会大于16kb,就需要多个页去存放。

每行数据,除了要记录具体行数据,也需要记录列字段类型、类型长度、空字段值、还有在MVCC机制原理专题说过,每行数据都有一个row_id,还有更新该行数据的事务id,还有回滚指针地址roll_ptr_id等这些信息。

三、如何设计存放一行数据

3.1 行格式类型

MySQL 5.6 默认的row_format行格式是Compact,MySQL 5.7版本是dynamic。行格式可以通过创建表时候指定,比如:create table t1(columnxx ...)ROW_FORMAT=compact。

我们可以通过命令:SHOW TABLE STATUS LIKE '表名';

比如:SHOW TABLE STATUS LIKE 'sys_dept';查看表的具体格式:

这两种行格式的主要区别在于使用场景上。

Compact行格式,适用于读操作远多于写操作场景。

而Dynamic行格式,支持更多高级功能,如表压缩和长列数据的页外存储。适用于需要频繁插入、更新和删除数据的场景。

两个行格式很相近,这里我们对compact格式进行展开举例说明。

3.2 具体数据构成

上文说过,一行数据内容有:row_id、事务id、roll_ptr回滚地址、真实数据、以及变长字段真实值长度列表、null值字段列表、真实行数据值等其他信息。具体行数据结构如图:

0
0

每一行数据都有自己的row_id,如果表没有指定主键或者唯一索引,MySQL内部会默认新增一个row_id作为主键,对每一行数据进行唯一性标识。每行数据的写入或者更新都有对应事务id、以及对应回滚指针,指向之前旧数据地址。

整个表头信息,大概占用了40bit空间,里面第3bit,是delete_mask,标识该行数据是否被删除。所以MySQL每行数据的删除,不是立马从磁盘物理删除,而是先打标识。

此外表头信息里还有下一行数据指针地址、行数据类型等附加信息。

3.3 变长字段、定长字段存储

MySQL的varchar(n)类型字段,由于是变长字段,在解析真实行数据的时候,我们需要记录对应变长字段真实值的长度是多少。比如表有三个字段:name varchar(10),age char(2),sex char(2)。

某一行数据name列值实际长度可能是5,那通过变长字段实际长度值列表,可以得知后面该字段真实值长度,实现准确解析。

而定长字段,如果不是空值,在解析真实数据内容,就按指定长度截取解析即可。

3.4 空null存储

为了节省存储空间以及存储效率,MySQL对行数据字段存在null值的字段,同样参考变长字段长度列表,新增一个null值字段列表。如果该行数据,有N个字段是null值,这个列表就存放相关字段位置,就可以通过该列表表示相关列值为空。

null值列表是一个bit数组,长度就是允许为空列的个数。比如刚才的表三个字段:name varchar(10),age char(2),sex char(2),三个字段都允许为空。该行name列是空值,age不为空,sex为空。那null值列表bit数组值就是101。

这个设计,比真实存放null字符串能节省非常多的存储空间。

3.5 什么是行溢出现象?

行溢出现象就是:如果一行数据很大,就会出现一个数据页无法存储问题,比如存放text文本、blob类型数据容易出现这种情况。MySQL通过多个数据页来存储,加载到bufferpool也是用多个页去存储。

推荐阅读拉丁解牛相关专题系列(欢迎交流讨论公众号搜:拉丁解牛说技术)

1、JVM进阶调优系列(5)CMS回收器通俗演义一文讲透FullGC

2、JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?

3、JVM进阶调优系列(3)堆内存的对象什么时候被回收?

4、JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?

5、JVM进阶调优系列(1)类加载器原理一文讲透

6、JAVA并发编程系列(13)Future、FutureTask异步小王子

7、MySQL进阶突击系列(05)突击MVCC核心原理 | 左右护法ReadView视图和undoLog版本链强强联合

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前言背景
  • 二、数据存储整体逻辑
    • 2.1 存储地址和具体文件
    • 2.2 表空间-段segement
    • 2.3 表空间-区extent
    • 2.4 表空间-页page
    • 2.5 表空间-行数据row
  • 三、如何设计存放一行数据
    • 3.1 行格式类型
    • 3.2 具体数据构成
    • 3.3 变长字段、定长字段存储
    • 3.4 空null存储
    • 3.5 什么是行溢出现象?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档