【InnoDB数据存储结构】第3章节:区、段、碎片区和表空间

文章目录结构

在这里插入图片描述

区、段、碎片区和表空间

  • 什么是区?
  • 什么是段?
  • 什么是碎片区?
  • 什么是表空间?

在上文 InooDB 存储行格式一文中已经大致讲述过,再来回顾一下,直接上图:

名词解释如下:

  • 行(row): 数据库中的数据都是按行(row)存储的,行记录(统称)根据不同的 行格式 ,有不同的存储结构。
  • 页(page): 记录是按行存储的,但是数据库的读取并不是以 为单位,而是以 为单位, 每页的大小为 16KB ,否则读取一次只能读取一行数据(也就是一次 I/O 操作),只能处理一行数据,效率太低了。
  • 区(extent): B+Tree 的每一层的节点之间都是通过双向链表链接的,以页为单位,相邻的两个页之间位置并不是连续的,可能离的非常远,那么数据量大的情况在查询的时候就会产生大量的随机 I/O 操作(效率低)。为了解决这个问题,为某个索引分配空间的时候按照 为单位,一个区的大小为 1MB,对于 16KB大小的页来说,连续的 64 个页划分为一个 ,这样就使得相邻的页的物理位置也是相邻的,就可以使用 顺序 I/O 了。
  • 段(segment): 表空间由多个段组成,段是由多个区组成。段一般分为 数据段 索引段 回滚段 等。
    • 数据段:存放 B+Tree 叶子节点区的集合
    • 索引段:存放 B+Tree 非叶子节点区的集合
    • 回滚段:存放回滚数据区的集合(MVCC 就是利用了回滚段实现了多版本查询控制)
  • 表空间(Tablespace): 是一个逻辑容器,表空间存储的对象是段,在一个空间中可以由一个段或多个段,但是一个段只能属于一个表空间。

为什么要有区?

B+Tree的每一层的节点之间都是通过双向链表链接的,以 页为单位 为单位分配存储空间,相邻的两个页之间位置并不是连续的,可能离的非常远。

B+Tree 索引适用的场景提到在范围查询时,只需要定位到最左边和最右边的记录(顺序存储),然后沿着双向链表一直扫描就可以了,而如果链表中相邻的两个数据页的物理位置相差的很远,中间的查询产生的就是 随机 I/O

磁盘的速度和内存的速度差了好几个量级, 随机 I/O 是非常慢的

为了能够使用 顺序 I/O ,尽量让链表中相邻的页的物理位置也相邻,这就引入了 的概念,一个区在物理位置上就是连续的 64 个页 。因为 InooDB 的一个页默认大小为 16KB,所以一个区的大小为 1MB

在表中数据量非常大的时候,为某个索引分配空间的时候就不再按照页为单位分配了,而是按照 区为单位分配 ,甚至在表中的数据特别多的时候,可以一次性分配多个连续的区,虽然这样可能会造成 一点空间上的浪费(数据不足以填充满整个区) ,但是从性能上来讲,可以消除很多 随机 I/O ,所以从这点上来说功大于过,可以接受。

总结一句话,区的出现是为了尽量减少 随机 I/O

为什么要有段?

对于范围查询,其实是对 B+Tree 叶子节点中的记录进行顺序扫描,而如果不区分叶子节点和非叶子节点,统一把节点页面放在申请到的区中,进行范围扫描效率相比纯叶子节点就会低很多。所以 InnoDB 对 B+Tree 的 叶子节点 非叶子节点 进行了区别对待,分别放在 各自独立的区 中,从这点我们能知道创建一个索引会生成两个 ,一个是 叶子节点段 ,一个是 非叶子节点段

除了索引的叶子节点和非叶子节点组成的段之外,InnoDB 中还有为存储一些特殊的数据而定义的段,比如 回滚段

所以常见的段有 数据段 索引段 回滚段

  • 数据段:存放 B+Tree 叶子节点
  • 索引段:存放 B+Tree 非叶子节点
  • 回滚段:存放回滚数据

在 InnoDB 存储引擎中,对段的管理都是由存储引擎本身完成的,DBA 不能也没有必要对其进行控制,这从一定程度上简化了 DBA 对于段的管理。

段其实不是表空间中某一个连续的物理区域,而是一个逻辑上的概念,由 若干个零散的页面 一些完整的区 组成。

  • 零散的页面: 不归属于任何区,直接归属于表空间。

为什么要有碎片区?

默认情况下,一个使用 InnnoDB 存储引擎的表只有一个聚簇索引,一个索引会生成 2 个段,非叶子节点段和叶子节点段

,而段是区为单位社情存储空间的,一个区(连续的 64 个页)默认占用 1M(64 * 16Kb = 1024Kb)存储空间,那也就是说一个段默认情况下就是为 2M, 设想以下几个问题:

  • 默认情况下一个只存了几条记录的小表是否也需要 2M 的存储空间?
  • 以后每添加一个索引都会申请 2M 的存储空间吗?

这对于存储记录比较少的表来说,严重浪费存储空间。

造成这个问题的原因在于我们目前讨论的 都是 完整的纯粹区 ,也就是说一个区被完整的分配给某一个段,或者说区中所有的页面都是为了存储同一个段的数据而存在的,即使段的数据填不满区中所有的页面,那余下的页面也不会用于存储其他段的数据,所以严重造成空间浪费。

为了解决这种浪费存储空间的情况,InnoDB 提出了一个 碎片区 的概念。

在一个碎片区中并不是所有的页面都是为了存储同一个段中的数据而存在的,而是碎片区中的页面可以分别用于不同的用途,比如有些页用于段 A,有些页用于段 B,有些页甚至不属于任何一个端,而是直接归属于表空间

有了碎片区的概念,此后为某个段分配存储空间的策略如下:

  • 在刚开始向表中插入数据时,段是从 某个碎片区 以单个页面为单位来分配存储空间的。
  • 当某个段已经占用了 32 个碎片区页面之后,就会申请以 完整的区 为单位来分配存储空间。

所以现在的段不能仅仅定义为某些区的集合,准确来说应该是 某些零散页面 以及 一些完整的区 的集合。

区的分类

区大体上可以分为 4 种类型:

  • 空闲的区(FREE): 现在还没有用到这个区的任何页面。
  • 有剩余空间的碎片区(FREE_FRAG): 表示碎片区中还有可用的页面。
  • 没有剩余空间碎片的区(FULL_FRAG): 表示碎片区中所有页面都被使用,没有空间页面。
  • 附属于某个段的区(FSEG): 每个索引都可以分为叶子节点段(数据段)和非叶子节点段(索引段)。

处于 FREE、FREE_FRAG、FULL_FRAG这三种状态的区都是独立的,而处于 FSEG状态的区是附属于某个段的。

表空间

表空间可以看做是 InnoDB 存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。

表空间是一个 逻辑容器 ,表空间存储的对象是段,在一个表空间中可以由一个或多个段,但是一个段只能属于一个表空间。

表空间数据库由一个或多个表空间组成,表空间从管理上可以划分为:

  • 系统表空间(System TableSpace)
  • 独立表空间(File-per-table TableSpace)
  • 撤销表空间(Undo TableSpace)
  • 临时表空间(Temporary TableSpace)

独立表空间

独立表空间,即每张表都有一个独立的表空间,也就是数据和索引信息都会保存在自己的独立表空间中。

独立表空间(单表)可以在不同的数据库之间进行 迁移

表空间可以回收,命令 DROP TABLE_NAME操作可自动回收表空间,其他情况,表空间不能自动回收。另外DELETE FROM TABLE_NAME操作是假删除数据,数据底层只是被标记为删除了,但实际上还占据存储空间,所以空间不会被回收。

如果对于统计分析或者是日志表,删除大量数据后可以通过命令 alter table table_name engine=innodb;回收不用的空间(重置 InnoDB 索引结构,会重新分配存储空间,空闲的空间会被回收)。对于使用独立表空间的表,不管怎么删除,表空的碎片都不会太影响性能,而且还有机会被处理。

独立表空间结构

独立表空间由段、区、页组成。

真实表空间对应的文件大小

  1. MySQL 5.7 中,我们每新建一张表,在数据库的目录中都会新增两个文件,.frm.ibd文件。

创建一个表 temp,看下库中对应表文件的大小:

create table temp(id int, name varchar(15));

分析一下.frm.ibd文件。

  • .frm存放表结构文件默认大小为 9Kb,还不导一个页面大小;
  • .ibd存放数据文件默认大小为 96Kb,等于96 / 16 = 6个页面大小

加一起不到 7 个页面大小,因为表空间初始化没有数据,所以占用空间很小。但是 .ibd文件是 自扩展 的,随着表中的数据增多,表空间对应的文件也会逐渐增大。

  1. MySQL 8.0 中,我们每新建一张表,在数据库的目录中都会新增一个文件,.ibd文件,剔除了.frm文件。

创建一个表 temp,看下库中对应表文件的大小:

create table temp(id int, name varchar(15));

分析一下:

  • .ibd存放数据文件默认大小为 112Kb,等于112 / 16 = 7个页面大小,比 MySQL 5.7 多了一个页面大小,是因为 MySQL 8.0 将表结构和数据都存储在了 .ibd文件中。

查看 InnoDB 表空间类型

是否使用独立表空间可以通过一个全局参数innodb_file_per_table配置:

  • 设置为 1 就代表使用独立表空间(后缀 table_name.idb中)
  • 设置为 0 代表使用系统表空间(存储在共享表空间文件中ibdata1中)。

从 MySQL 5.6.6 版本之后,innodb_file_per_table默认值就为 1 了,因此此后的版本,表数据默认都是存储在独占表中的。

show variables like 'innodb_file_per_table';

MySQL 5.7 和 MySQL 8.0 查看结果都是如下:

系统表空间

每当我们向表中插入一条记录的时候,MySQL 的校验过程如下:

  • 先要校验一下插入语句对应的表存不存在?
  • 插入的列和表中的列是否符合?

如果语法没有问题,还需要知道该表的聚簇索引和所有二级索引的根页面是在哪个表空间下的哪个索引段中,然后把记录插入到对应索引的 B+Tree 中。

所以 MySQL 除了保存我们插入的用户记录之外,还需要保存许多额外的信息,例如下面的这些数据:

● 某个表属于哪个表空间,表里边有多少列
● 表对应的每一个列的类型是什么
● 该表有多少索引,每个索引对应哪几个字段,该索引对应的根页面在哪个表空间的哪个页面
● 该表有哪些外键,外键对应哪个表的哪些列
● 某个表空间对应文件系统上文件路径是什么
● ………………

上述这些数据并不是我们使用 INSERT语句插入的用户记录数据,而是为了更好的管理我们这些用户数据不得已才引入的一些额外数据,我们称这些数据为 元数据

InnoDB 存储引擎特意定义了一些列的 内部系统表(internal system table)来记录这些元数据:

上述这些系统表也被称为 数据字典 ,它们都是以 B+Tree 的形式保存在系统表空间的某些页面中,其中

  • SYS_TABLES
  • SYS_COLUMNS
  • SYS_INDEXES
  • SYS_FIELDS

这四个表尤其重要,称之为 基本系统表(basic system tables) 我们先看看这4个表的结构。

SYS_TABLES 表结构

SYS_COLUMNS 表结构

SYS_INDEXES 表结构!

SYS_FIELDS 表结构

注意:用户是不能直接访问 lnnoDB 的这些内部系统表,除非你直接去解析系统表空间对应文件系统上的文件。不过考虑到查看这些表的内容可能有助于大家分析问题,所以在系统数据库 information_schema 中提供了一些以 innodb_sys开头的表。

MySQL 8.0 中是不存在以 innodb_sys开头的表。

MySQL 5.7 中操作如下:

mysql> use information_schema;
Database changed
mysql> show tables like 'innodb_sys%';
+--------------------------------------------+
| Tables_in_information_schema (innodb_sys%) |
+--------------------------------------------+
| INNODB_SYS_DATAFILES                       |
| INNODB_SYS_VIRTUAL                         |
| INNODB_SYS_INDEXES                         |
| INNODB_SYS_TABLES                          |
| INNODB_SYS_FIELDS                          |
| INNODB_SYS_TABLESPACES                     |
| INNODB_SYS_FOREIGN_COLS                    |
| INNODB_SYS_COLUMNS                         |
| INNODB_SYS_FOREIGN                         |
| INNODB_SYS_TABLESTATS                      |
+--------------------------------------------+
10 rows in set (0.01 sec)

information_schema数据库中的这些以 INNODB_SYS 开头的表并不是真正的内部系统表(内部系统表就是我们上边以 SYS开头的那些表),而是在存储引警启动时读取以 SYS 开头 的系统表,然后填充到以 INNODB_SYS 开头 的表中。以 INNODB_SYS 开头 的表和以 SYS 开头 的表中的字段并不完全一样,但供我们参考已经足矣。


在这里插入图片描述

一起学编程,让生活更随和!

如果你觉得是个同道中人,欢迎关注博主gzh:【随和的皮蛋桑】。

专注于Java基础、进阶、面试以及计算机基础知识分享🐳。偶尔认知思考、日常水文🐌。

在这里插入图片描述


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

go构建项目与打包

环境搭建 使用的组件及版本 operator-sdk v1.22.0go 1.20.0 linux/amd64git 1.8.3.1k8s 1.18.5docker 20.10.5 前期配置 安装Git yum install git安装docker yum install docker-ce安装go 官网下载 tar -C /usr/local/ -xvf go1.20.linux-amd64.tar.gz 环境配置 // 将go配置…

【tensorflowflutter】自己写个机器学习模型用在项目上?

背景 拍摄APP项目上线有一阵了,每天的拍摄数据呈现波动上升状态、业务方需要对数据进行加工,如果能有对未来的数据量的预测就好了 。 目标 在端侧展示拍摄数据可视化趋势图等、并能推断数据(选择预测日期) 简单实现个demo gif背…

uniapp自定义顶部导航并解决打包成apk后getMenuButtonBoundingClientRect方法失效问题

需求:要在app上的顶部导航提示哪里添加一些东西进去,用uniapp自带的肯定不行啊,所以自定义了所有的页面的顶部导航,之后自定义后用手机调试发现 uni.getMenuButtonBoundingClientRect()这个方法的top获取不到....网上找了很多种方…

服务注册中心

服务注册中心 注册中心与CAP理论介绍 1.注册中心 服务注册中心是微服务架构中的一个关键组件,它的主要作用是管理服务实例的注册、维护和发现。 是一个中心化的组件来分散的微服务实例的位置和状态。 注册中心有三种角色构成: 服务提供者&#xff1a…

每周一算法:倍增法求最近公共祖先(LCA)

最近公共祖先 最近公共祖先简称 LCA(Lowest Common Ancestor)。两个节点的最近公共祖先,就是这两个点的公共祖先里面,离根最远的那个。 题目链接 祖孙询问 题目描述 给定一棵包含 n n n 个节点的有根无向树,节点…

知虾shopee数据:为卖家提供了丰富的数据分析工具

使用Shopee的卖家都知道,这个平台为卖家提供了丰富的数据分析工具,帮助他们更好地理解店铺运营状况和市场趋势。这些数据分析工具不仅能够提供数据总览,还包括买家分析、商品排名、分类排名、销售辅导、流量分析、销售结构、行销活动、聊天响…

SpringCloud Alibaba之Nacos配置中心配置详解

目录 Nacos配置中心数据模型Nacos配置文件加载Nacos配置 Nacos配置中心数据模型 Nacos 数据模型 Key 由三元组唯一确定,三元组分别是Namespace、Group、DataId,Namespace默认是公共命名空间(public),分组默认是 DEFAUL…

聚类分析 | Matlab实现基于RIME-DBSCAN的数据聚类可视化

聚类分析 | Matlab实现基于RIME-DBSCAN的数据聚类可视化 目录 聚类分析 | Matlab实现基于RIME-DBSCAN的数据聚类可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.聚类分析 | Matlab实现基于RIME-DBSCAN的数据聚类可视化(完整源码和数据) 2.多特征输入&…

HarmonyOS 应用开发学习笔记 状态管理概述

移动端开发,最重要的一点就是数据的处理,并且正确的显示渲染UI。 变量在页面和组件、组件和组件之间有时候并不能实时共享,而有时候,又不需要太多的作用域(节省资源),作用就需要根据不同场景&am…

MQ 发消息的4种姿势

微服务开发中经常会使用消息队列进行跨服务通信。在一个典型场景中,服务A执行一个业务逻辑,需要保存数据库,然后通知服务B执行相应的业务逻辑。在这种场景下,我们需要考虑如何发送消息。 1. 基础版 首先,我们可能会考虑将数据库操作和消息发送放在同一个事务中,以下是伪…

深度学习笔记

激活函数 激活函数可以让神经网引入非线性性质,去除线性相关特征映射,是网络捕获数据中的不同特征;relu max(0,x)输出为0到正无穷sigmod输出为0-1 形状为srelu 输出为-1-1 以0为中心,形状为s …

【STM32】STM32学习笔记-ADC模数转换器(21)

00. 目录 文章目录 00. 目录01. ADC简介02. ADC主要特征03. 逐次逼近型ADC04. ADC功能描述05. ADC基本结构06. 输入通道07. 转换模式08. 触发控制09. 数据对齐10. 转换时间11. 校准12. 硬件电路13. 附录 01. ADC简介 小容量产品是指闪存存储器容量在16K至32K字节之间的STM32F1…

再次拓宽信创生态版图,思迈特与统信软件完成产品兼容适配认证

近日,思迈特软件与统信软件科技有限公司(简称“统信软件”)完成产品兼容性适配互认证,加速国产信创生态化建设进程。 本次测试由商业智能与数据分析软件(简称:Smartbi Insight V11)产品与统信服…

AMEYA360分享:太阳诱电导电性高分子混合铝电解电容器

随着汽车电动化和电子控制化的进展,车载计算机和电气部件也在逐渐向大功率化的方向发展。而构成这些车载设备电源电路的电子元器件也必须随之进行技术革新。 太阳诱电集团携手全资子公司ELNA,开发并供应新型电容器“导电性高分子混合铝电解电容器”&…

农业银行RPA实践 3大典型案例分析

零接触开放金融服务在疫情之下被越来越多的银行和客户所认同,引起了更广泛的持续关注,各家银行纷纷开展产品服务创新,加速渠道迁移,同时通过远程办公、构建金融生态等方式积极推进零接触开放金融体系建设。 随着商业银行科技力量的…

JSP页面访问JDBC数据库的六个步骤

【例】创建exgample11_1.jsp页面&#xff0c;并在该页面中使用纯Java数据库驱动程序连接数据库test&#xff0c;并查询数据表goods中的数据。 <% page language"java" contentType"text/html;charsetUTF-8" pageEncoding"UTF-8"%> <% …

Flink窗口与WaterMark

本文目录 窗口的生命周期Window Assigners窗口函数&#xff08;Window Functions&#xff09;TriggersEvictorsAllowed Lateness 窗口 窗口&#xff08;Window&#xff09;是处理无界流的关键所在。窗口可以将数据流装入大小有限的“桶”中&#xff0c;再对每个“桶”加以处理。…

基于低代码的指尖遐想_3

低代码需要回答三个问题&#xff1a; 1、是否降低了门槛&#xff1f; 2、是否提高了效能&#xff1f; 3、使用者用户画像&#xff1f; 广义低代码&#xff0c;从项目全局维度&#xff08;项目启动、需求调研、方案设计、配置开发、系统验收、系统运营&#xff09;进行了解答。…

使用Termux+Hexo搭建个人博客结合内网穿透工具轻松实现公网访问内网博客

文章目录 前言 1.安装 Hexo2.安装cpolar3.远程访问4.固定公网地址 前言 Hexo 是一个用 Nodejs 编写的快速、简洁且高效的博客框架。Hexo 使用 Markdown 解析文章&#xff0c;在几秒内&#xff0c;即可利用靓丽的主题生成静态网页。 下面介绍在Termux中安装个人hexo博客并结合…

【算法每日一练]-动态规划 (保姆级教程 篇15)#动物 #赶deadline #page #构造字符串

目录 今日知识点&#xff1a; 01背包的路径输出 计算位和的数位dp 不用管字符串&#xff0c;只需要看好约束dp转移的变量 动物 赶deadline page 构造字符串 动物 有某类动物&#xff0c;可以在农场待n天&#xff0c;每天最多增加一只动物&#xff0c;第i天到来的动物每…