【MySQL索引与优化篇】InnoDB数据存储结构

文章目录

    • 1. 数据库的存储结构:页
      • 1.1 磁盘与内存交互基本单位:页
      • 1.2 页结构概述
      • 1.3 页的上层结构
    • 2. 页的内部结构
    • 3. InnoDB行格式(或记录格式)
      • 3.1 Compact行格式
      • 3.2 Dynamic和Compressed行格式
      • 3.3 Redundant行格式
    • 4. 区、段与碎片区
      • 4.1 为什么要有区?
      • 4.2 为什么要有段?
      • 4.3 为什么有碎片区
      • 4.4 区的分类
    • 5. 表空间
      • 5.1 独立表空间
      • 5.2 系统表空间

1. 数据库的存储结构:页

索引结构给我们提供了高效的索引方式,不过索引信息以及数据记录都是保存在文件上的,确切说是存储在页结构中。另一方面,索引是在存储引擎中实现的,MySQL服务器上的 存储引擎 负责对表中数据的读取和写入工作。不同存储引擎中 存放的格式 一般是不同的。

由于 InnoDB 是MySOL的 默认存储引擎,所以本章剖析InnoDB存储引擎的数据存储结构。

1.1 磁盘与内存交互基本单位:页

InnoDB 将数据划分为若干个页,InnoDB中页的大小默认为 16KB(可通过show variables命令看innodb_page_size参数)。 是磁盘与内存交互的 基本单位

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

在这里插入图片描述

1.2 页结构概述

页a、页b、页c…页n 这些页可以 不在物理结构上相连,只要通过 双向链表 相关联即可。每个数据页中的记录会按照主键值从小到大的顺序组成一个 单向链表,每个数据页都会为存储在它里边的记录生成一个 页目录,在通过主键查找某条记录的时候可以在页目录中 使用二分法 快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录。

1.3 页的上层结构

另外在数据库中,还存在着区 (Extent)、段 (Segment) 和表空间 (Tablespace) 的概念。行、页、区、段、表空间的关系如下图所示:

在这里插入图片描述

区(Extent)是比页大一级的存储结构,在InnoDB 存储引擎中,一个区会分配 64 个连续的页。因为InnoDB 中的页大小默认是 16KB,所以一个区的大小是 64*16KB= 1MB

段(Segment)由一个或多个区组成,在段中不要求区与区之间是相邻的段是数据库中的分配单位不同类型的数据库对象以不同的段形式存在。当我们创建数据表、索引的时候,就会相应创建对应的段,比如创建一张表时会创建一个表段,创建一个索引时会创建一个索引段

表空间(Tablespace)是一个逻辑容器,表空间存储的对象是段,在一个表空间中可以有一个或多个段,但是一个段只能属于一个表空间。数据库由一个或多个表空间组成,表空间从管理上可以划分为 系统表空间用户表空间撤销表空间临时表空间 等。

2. 页的内部结构

页如果按类型划分的话,常见的有 数据页(保存 B+ 节点)系统页Undo 页事务数据页 等。数据页是我们最常使用的页。

数据页的 16KB 大小的存储空间被划分为七个部分,分别是文件头 (File Header) 、页头(Page Header) 、最大最小记录 (Infimum+supremum) 、用户记录 (User Records)、空闲空间 (Free Space) 、页目录 (Page Directory) 和文件尾 (File Tailer) 。

在这里插入图片描述

这7个部分的作用说明分别如下:

在这里插入图片描述

B+ 树是如何进行记录检索的?

通过 B+ 树的索引查询行记录,首先是从 B+ 树的根开始,逐层检索,直到找到叶子节点,也就是找到对应的数据页为止,将数据页加载到内存中,页目录中的槽(sot)采用 二分查找 的方式先找到一个粗略的记录分组(一个分组有8条以下的记录,页目录结构查找图如下),然后再在分组中通过 链表遍历 的方式查找记录。

在这里插入图片描述

3. InnoDB行格式(或记录格式)

我们平时的数据以行为单位来向表中插入数据,这些记录在磁盘上的存放方式也被称为行格式或者记录格式。InnoDB存储引擎设计了4种不同类型的行格式,分别是CompactRedundantDynamicCompressed 行格式。

查看MySQL8.0默认的行格式或具体表使用的行格式:

SELECT @@innodb_default_row_format; -- 默认dynamicSHOW TABLE STATUS LIKE '表名'; -- 查看具体表使用的行格式Row_format列

3.1 Compact行格式

在这里插入图片描述

  1. 变长字段长度列表:存储所有变长字段占用字节数,存储顺序是反的

  2. NULL值列表:1代表NULL,0代表非NULL,如果表中没有允许存储NULL的值,则NULL值列表也不存在了,故可考虑尽可能设置表字段为非NULL,注意:存储的顺序也是反的,下面有示意图

  3. 记录头信息(5字节)

    • 预留位1和2:各占1bit,没有使用

    • delete_mask:占1bit,删除标识

    • min_rec_mask:占1bit,B+树的每层非叶子节点中的最小记录都会添加该标记

    • n_owned:占4bit,表示当前记录槽拥有的记录数,只有组中最大的会记录

    • heap_no:占13bit,表示当前记录在记录堆的位置信息

    • record_type:占3bit,表示当前记录的类型,0表示普通记录,1表示B+树非叶节点记录,2表示最小记录,3表示最大记录

    • next_record:占16bit,表示下一条记录的相对位置

  4. 记录的真实数据:除了自己定义的列的数据外,还会有三个隐藏列

    • DB_ROW_ID:6字节,行ID,唯一标识一条记录,一个表即没有主键又没有唯一索引则会默认添加该项隐藏列作为主键
    • DB_TRX_ID:事务ID
    • DB_ROLL_PTR:回滚指针

NULL值列表示意图:

在这里插入图片描述

记录头信息示意图:

在这里插入图片描述

3.2 Dynamic和Compressed行格式

Dynamic和Compressed相比Compact行格式只是对于行溢出的处理方式不同。

行溢出:一个页的大小一般是16KB,也就是16384字节,而一个VARCHAR(M)类型的列就最多可以存储65533个字节(2个字节存储变长的长度,1个存储NULL标识),这样就可能出现一个页存放不了条记录,这种现象称为行溢出

  • Compressed和Dynamic两种记录格式对于存放在BLOB中的数据采用了完全的行溢出的方式。如图,在数据页中只存放20个字节的指针(溢出页的地址),实际的数据都存放在Off Page(溢出页) 中
    • Compressed行记录格式的另一个功能就是,存储在其中的行数据会以zlib的算法进行压缩,因此对于BLOB、TEXT、VARCHAR这类大长度类型的数据能够进行非常有效的存储
  • Compact和Redundant两种格式会在记录的真实数据处存储一部分数据(存放768个前缀字节),把剩余的数据分散存储在几个其他的页中进行分页存储然后记录的真实数据处用20个字节存储指向这些页的地址(当然这20个字节中还包括这些分散在其他页面中的数据的占用的字节数),从而可以找到剩余数据所在的页

Compact和Redundant页的扩展:

在这里插入图片描述

Compressed和Dynamic:

在这里插入图片描述

3.3 Redundant行格式

支持该格式是为了兼容MySQL 5.0之前版本的页格式,实际开发中不使用,故不作过多分析

4. 区、段与碎片区

4.1 为什么要有区?

B+树的每一层中的页都会形成一个双向链表,如果是以 页为单位 来分配存储空间的话,双向链表相邻的两个页之间的 物理位置可能离得非常远。我们介绍B+树索引的适用场景的时候特别提到范围查询只需要定位到最左边的记录和最右边的记录,然后沿着双向链表一直扫描就可以了,而如果链表中相邻的两个页物理位置离得非常远就是所谓的 随机I/O。再一次强调,磁盘的速度和内存的速度差了好几个数量级,随机I/O是非常慢 的,所以我们应该尽量让链表中相邻的页的物理位置也相邻,这样进行范围查询的时候才可以使用所谓的 顺序I/O

引入区的概念,一个区就是在物理位置上连续的 64个页。因为 InnoDB 中的页大小默认是 16KB,所以一个区的大小是 64*16KB= 1MB 。在表中 数据量大 的时候,为某个索引分配空间的时候就不再按照页为单位分配了,而是按照 区为单位分配,甚至在表中的数据特别多的时候,可以一次性分配多个连续的区。虽然可能造成 一点点空间的浪费 (数据不足以填充满整个区),但是从性能角度看,可以消除很多的随机I/O,功大于过!

4.2 为什么要有段?

对于范围查询,其实是对B+树叶子节点中的记录进行顺序扫描,而如果不区分叶子节点和非叶子节点,统统把节点代表的页面放到申请到的区中的话,进行范围扫描的效果就大打折扣了。所以InnoDB 对B+树的 叶子节点非叶子节点 进行了区别对待,也就是说叶子节点有自己独有的区,非叶子节点也有自己独有的区。存放叶子节点的区的集合就算是一个 段( segment ),存放非叶子节点的区的集合也算是一个段。也就是说一个索引会生成2个段,一个 叶子节点段,一个 非叶子节点段

除了索引的叶子节点段和非叶子节点段之外,lnnoDB中还有为存储一些特殊的数据而定义的段,比如回滚段。所以,常见的段有 数据段索引段回滚段。数据段即为B+树的叶子节点,索引段即为B+树的非叶子节点。

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

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

4.3 为什么有碎片区

默认情况下,一个使用InnoDB存储引擎的表只有一个聚簇索引,一个索引会生成2个段,而段是以区为单位申请存储空间的,一个区默认占用1M (64*16Kb = 1024Kb)存储空间,所以默认情况下一个只存了几条记录的小表也需要2M的存储空间么?以后每次添加一个索引都要多申请2M的存储空间么? 这对于存储记录比较少的表简直是天大的浪费。这个问题的症结在于到现在为止我们介绍的区都是非常 纯粹 的,也就是一个区被整个分配给某一个段,或者说区中的所有页面都是为了存储同一个段的数据而存在的,即使段的数据填不满区中所有的页面,那余下的页面也不能挪作他用。

为了考虑以完整的区为单位分配给某个段对于 数据量较小 的表太浪费存储空间的这种情况,lnnoDB提出了一个 碎片(fragment)区 的概念。在一个碎片区中,并不是所有的页都是为了存储同一个段的数据而存在的,而是碎片区中的页可以用于不同的目的,比如有些页用于段A,有些页用于段B,有些页甚至哪个段都不属于。碎片区直属于表空间,并不属于任何一个段。

所以此后为某个段分配存储空间的策略是这样的:

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

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

4.4 区的分类

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

处于 FREEFREE_FRAG 以及 FULL_FRAG 这三种状态的区都是独立的,直属于表空间。而处于 FSEG 状态的区是附属于某个段的

如果把表空间比作是一个集团军,段就相当于师,区就相当于团。一般的团都是隶属于某个师的,就像是处于 FSEG 的区全都隶属于某个段,而处于 FREE、FREE_FRAG 以及 FULL_FRAG 这三种状态的区却直接隶属干表空间,就像独立团直接听命于军部一样。

5. 表空间

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

表空间是一个 逻辑容器,表空间存储的对象是段,在一个表空间中可以有一个或多个段,但是一个段只能属于一个表空间。表空间数据库由一个或多个表空间组成,表空间从管理上可以划分为 系统表空间 (Systemtablespace) 、独立表空间 (File-per-table tablespace) 、撤销表空间 (Undo Tablespace) 和 临时表空间(Temporary Tablespace) 等。

5.1 独立表空间

独立表空间,即每张表有一个独立的表空间,也就是数据和索引信息都会保存在自己的表空间中。独立的表空间(即:单表)可以在不同的数据库之间进行 迁移

空间可以回收(DROP TABLE 操作可自动回收表空间,其他情况,表空间不能自己回收)。如果对于统计分析或是日志表,删除大量数据后可以通过: alter table TableName engine=innodb; 回收不用的空间。对于使用独立表空间的表,不管怎么删除,表空间的碎片不会太严重的影响性能,而且还有机会处理。

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

真实表空间对应的文件大小:到数据目录里看,会发现一个新建的表对应的 .ibd 文件只占用了 96K,才6个页面大小(MySQL5.7中),这些.ibd文件是 自扩展的,随着表中数据的增多,表空间对应的文件也逐渐增大

5.2 系统表空间

系统表空间的结构和独立表空间基本类似,只不过由于整个MySQL进程只有一个系统表空间,在系统表空间中会额外记录一些有关整个系统信息的页面,这部分是独立表空间中没有的。

InnoDB数据字典,SYS_TABLES等SYS开头的部分表,但用户并不能直接访问这些表,而是存储引擎在启动时会把SYS_等表的数据填充到information_schema数据库中以INNODB_SYS开头的表。

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

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

相关文章

Ortec974A EPICS IOC程序

1) 创建一个用户存放这个IOC程序结构的目录: rootorangepi4-lts:/usr/local/EPICS/program# mkdir ortec974A rootorangepi4-lts:/usr/local/EPICS/program# cd ortec974A/ rootorangepi4-lts:/usr/local/EPICS/program/ortec974A# ls2)使用…

mediapipe 训练自有图像数据分类

参考: https://developers.google.com/mediapipe/solutions/customization/image_classifier https://colab.research.google.com/github/googlesamples/mediapipe/blob/main/examples/customization/image_classifier.ipynb#scrollToplvO-YmcQn5g 安装&#xff1a…

CSS中的栅格布局

CSS中的栅格布局 在写前端项目的时候,我之前一直习惯使用flex布局,flex布局写起来比较随心,几乎可以实现任意形式的页面布局。不过自从B占看到某位大佬的grid布局后,发现布局居然还可以这么玩,正好自己在写一个vue3的…

【大数据Hive】hive 表数据优化使用详解

目录 一、前言 二、hive 常用数据存储格式 2.1 文件格式-TextFile 2.1.1 操作演示 2.2 文件格式 - SequenceFile 2.2.1 操作演示 2.3 文件格式 -Parquet 2.3.1 Parquet简介 2.3.2 操作演示 2.4 文件格式-ORC 2.4.1 ORC介绍 2.4.2 操作演示 三、hive 存储数据压缩优…

在线主动学习算法评估策略:prequential evaluation procedure

在线主动学习算法评估策略:prequential evaluation procedure 在在线主动学习领域(Online Active Learning),对在线主动学习算法的评估策略有多种方法,而现如今常用的方法是prequential evaluation procedure(出自论文《High density-focuse…

c++二叉树遍历

参考文献 数据结构c语言版&#xff0c;严蔚敏_吴伟民著。 二叉树 中序遍历代码实现 #include<vector> #include<iostream> using namespace std;//Definition for a binary tree node. struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode() : v…

腾讯云轻量应用镜像、系统镜像、Docker基础镜像、自定义镜像和共享镜像介绍

腾讯云轻量应用服务器镜像类型分为应用镜像、系统镜像、Docker基础镜像、自定义镜像和共享镜像&#xff0c;腾讯云百科txybk.com来详细说下不同镜像类型说明和详细介绍&#xff1a; 轻量应用服务器镜像类型说明 腾讯云轻量应用服务器 应用镜像&#xff1a;独有的应用镜像除了包…

Mysql设置了更新时间自动更新,指定更新部分sql时不进行时间更新

现象&#xff1a; 因为字段设置了自动更新&#xff0c;所以sql语句一进行修改此字段就会自动更新时间&#xff0c;但是呢我们的有部分定时任务是半夜执行&#xff0c;并且不能让这个任务修改到数据的更新时间 解决&#xff1a; <update id"updateCreative">ALT…

GoLand GC(垃圾回收机制)简介及调优

GC(Garbage Collector)垃圾回收机制及调优 简单理解GC机制 其实gc机制特别容易理解&#xff0c;就是物理内存的自动清理工。我们可以把内存想象成一个房间&#xff0c;程序运行时会在这个房间里存放各种东西&#xff0c;但有时候我们会忘记把不再需要的东西拿出去&#xff0c…

HubSpot CRM是什么?如何添加、使用呢?

HubSpot CRM是一款强大的客户关系管理工具&#xff0c;它不仅简化了销售和市场营销过程&#xff0c;还提供了多种功能&#xff0c;有助于增强客户互动、提高销售效率和提供更多的洞察信息。 今天运营坛将带领大家深入了解HubSpot CRM&#xff0c;涵盖了它的定义、使用流程、添…

基于STM32闭环步进电机控制系统设计

**单片机设计介绍&#xff0c;1654基于STM32闭环步进电机控制系统设计&#xff08;仿真&#xff0c;程序&#xff0c;说明&#xff09; 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序文档 六、 文章目录 一 概要 基于STM32的闭环步进电机控制系统设计是…

Java版 招投标系统简介 招投标系统源码 java招投标系统 招投标系统功能设计

功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查看所…

安卓逆向之雷电模拟器中控

一, 雷电模拟器 安装使用 官方地址: https://www.ldmnq.com ,官方论坛 https://www.ldmnq.com/forum/ . 有一个多开管理器,还有就是设置手机的参数比较关键。 二,雷电模拟器开启面具,安装LSP。 设置root 权限。

【数据结构实战项目】C语言实现数据结构顺序表万字详解(附完整运行代码)

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 一.了解项目功能 在本次项目中我们的目标是实现一个顺序表: 该顺序表使用动态内存分配,可以用来存储任意数量的同类型数据. 顺序表需要包含三个要素:存储数据的数组arr,顺序表…

Python程序设计期末复习笔记

文章目录 一、数据存储1.1 倒计时1.2 os库1.3 字符串操作1.4 文件操作1.5 列表操作1.6 元组1.7 字典 二、文本处理及可视化2.1 jieba分词2.2 集合操作2.3 pdf文件读取2.4 参数传递2.5 变量作用域 三、数据处理分析3.1 Sumpy3.2 Matplotlib3.3 Numpy 四、Pandas4.1 索引操作4.2 …

技术视角下的跑腿小程序开发:关键挑战和解决方案

跑腿小程序作为连接服务提供者和用户的桥梁&#xff0c;面临着诸多技术挑战。本文将聚焦于技术层面的关键挑战&#xff0c;并提供解决方案&#xff0c;以帮助开发者应对技术上的复杂问题。 1. 实时性与性能挑战 挑战&#xff1a; 跑腿小程序需要实时地匹配订单、更新状态和提…

40基于MATLAB,使用模板匹配法实现车牌的识别。

基于MATLAB&#xff0c;使用模板匹配法实现车牌的识别。具体包括将原图灰度化&#xff0c;边缘检测&#xff0c;腐蚀操作&#xff0c;车牌区域定位&#xff0c;车牌区域矫正&#xff0c;二值化&#xff0c;均值滤波&#xff0c;切割&#xff0c;字符匹配&#xff0c;最终显示车…

小程序request请求封装

以上为本人的项目目录 1.首先在utils中创建request.js文件封装request请求&#xff0c;此封装带上了token&#xff0c;每次请求都会自带token&#xff0c;需要你从后端获取后利用wx.setStorageSync(token,返回的token),不使用的话就是空。 直接复制即可&#xff0c;需要改一下…

(三)库存超卖案例实战——使用redis分布式锁解决“超卖”问题

前言 在上一节内容中我们介绍了如何使用mysql数据库的传统锁&#xff08;行锁、乐观锁、悲观锁&#xff09;来解决并发访问导致的“超卖问题”。虽然mysql的传统锁能够很好的解决并发访问的问题&#xff0c;但是从性能上来讲&#xff0c;mysql的表现似乎并不那么优秀&#xff…

vue3后台管理系统之跨域代理

vite.config.js中 server: {port: 5002,host: true, //0.0.0.0open: false,strictPort: true,proxy: {// 请求前缀/api&#xff0c;只有加了/api前缀的请求才会走代理(前端自定义)/api: {target: http://127.0.0.1:8000,// 获取服务器地址的设置changeOrigin: true,// 路径重写…