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

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
简介: 文中详细介绍了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

    每一行数据都有自己的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进阶调优系列(3)堆内存的对象什么时候被回收?

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

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

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

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

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
5月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
3月前
|
SQL 人工智能 关系型数据库
如何实现MySQL百万级数据的查询?
本文探讨了在MySQL中对百万级数据进行排序分页查询的优化策略。面对五百万条数据,传统的浅分页和深分页查询效率较低,尤其深分页因偏移量大导致性能显著下降。通过为排序字段添加索引、使用联合索引、手动回表等方法,有效提升了查询速度。最终建议根据业务需求选择合适方案:浅分页可加单列索引,深分页推荐联合索引或子查询优化,同时结合前端传递最后一条数据ID的方式实现高效翻页。
186 0
|
2月前
|
存储 关系型数据库 MySQL
在CentOS 8.x上安装Percona Xtrabackup工具备份MySQL数据步骤。
以上就是在CentOS8.x上通过Perconaxtabbackup工具对Mysql进行高效率、高可靠性、无锁定影响地实现在线快速全量及增加式数据库资料保存与恢复流程。通过以上流程可以有效地将Mysql相关资料按需求完成定期或不定期地保存与灾难恢复需求。
208 10
|
3月前
|
SQL 存储 缓存
MySQL 如何高效可靠处理持久化数据
本文详细解析了 MySQL 的 SQL 执行流程、crash-safe 机制及性能优化策略。内容涵盖连接器、分析器、优化器、执行器与存储引擎的工作原理,深入探讨 redolog 与 binlog 的两阶段提交机制,并分析日志策略、组提交、脏页刷盘等关键性能优化手段,帮助提升数据库稳定性与执行效率。
|
5月前
|
存储 关系型数据库 MySQL
【免费动手教程上线】阿里云RDS MySQL推出大容量高性能存储:高性能本地盘(最高16TB存储空间)、高性能云盘(最高64TB存储空间)
阿里云RDS MySQL提供高性能本地盘与高性能云盘等存储方案,满足用户大容量、低延迟需求。高性能本地盘单盘最大16TB,IO延时微秒级;高性能云盘兼容ESSD特性,支持IO性能突发、BPE及16K原子写等能力。此外,阿里云还提供免费动手体验教程,帮助用户直观感受云数据库 RDS 存储性能表现。
|
6月前
|
关系型数据库 MySQL Linux
在Linux环境下备份Docker中的MySQL数据并传输到其他服务器以实现数据级别的容灾
以上就是在Linux环境下备份Docker中的MySQL数据并传输到其他服务器以实现数据级别的容灾的步骤。这个过程就像是一场接力赛,数据从MySQL数据库中接力棒一样传递到备份文件,再从备份文件传递到其他服务器,最后再传递回MySQL数据库。这样,即使在灾难发生时,我们也可以快速恢复数据,保证业务的正常运行。
294 28
|
5月前
|
存储 SQL 缓存
mysql数据引擎有哪些
MySQL 提供了多种存储引擎,每种引擎都有其独特的特点和适用场景。以下是一些常见的 MySQL 存储引擎及其特点:
153 0
|
7月前
|
SQL 关系型数据库 MySQL
【YashanDB知识库】字符集latin1的MySQL中文数据如何迁移到YashanDB
本文探讨了在使用YMP 23.2.1.3迁移MySQL Server字符集为latin1的中文数据至YashanDB时出现乱码的问题。问题根源在于MySQL latin1字符集存放的是实际utf8编码的数据,而YMP尚未支持此类场景。文章提供了两种解决方法:一是通过DBeaver直接迁移表数据;二是将MySQL表数据转换为Insert语句后手动插入YashanDB。同时指出,这两种方法适合单张表迁移,多表迁移可能存在兼容性问题,建议对问题表单独处理。
【YashanDB知识库】字符集latin1的MySQL中文数据如何迁移到YashanDB
|
7月前
|
缓存 NoSQL 关系型数据库
Redis和Mysql如何保证数据⼀致?
1. 先更新Mysql,再更新Redis,如果更新Redis失败,可能仍然不⼀致 2. 先删除Redis缓存数据,再更新Mysql,再次查询的时候在将数据添加到缓存中 这种⽅案能解决1 ⽅案的问题,但是在⾼并发下性能较低,⽽且仍然会出现数据不⼀致的问题,⽐如线程1删除了 Redis缓存数据,正在更新Mysql,此时另外⼀个查询再查询,那么就会把Mysql中⽼数据⼜查到 Redis中 1. 使用MQ异步同步, 保证数据的最终一致性 我们项目中会根据业务情况 , 使用不同的方案来解决Redis和Mysql的一致性问题 : 1. 对于一些一致性要求不高的场景 , 不做处理例如 : 用户行为数据 ,

相关产品

  • 云数据库 RDS MySQL 版
  • 推荐镜像

    更多
    下一篇
    oss教程