MySQL原理(一):逻辑存储结构

news2024/9/23 13:21:53

前言

从本文开始,我将分享一下近期学习 MySQL 的笔记,其中大部分来源于极客时间的《MySQL实战45讲》、小林coding、以及部分其他博客和书籍。

这次系列文章着重讲 MySQL 的原理部分,主要是用于面试,也就是我们常说的八股,至于基础的内容就不再介绍了。我将在后续文章中陆续介绍以下内容:存储结构、逻辑架构和执行流程、索引、事务、锁、日志、内存管理、磁盘空间管理、集群相关等。

另外,如果没有额外说明,介绍的主要内容都是关于默认的 InnoDB 引擎。

接下来开始正式讲解逻辑存储架构相关内容。我们都知道,MySQL 是将数据存储到硬盘、磁盘等外部存储器上的,逻辑存储架构,指的就是 MySQL 是如何组织、存储数据的。

逻辑存储结构

InnoDB 的所有数据都被逻辑地存放在表空间(tablespace)中,表空间又由段(segment),区/簇(extent),页(page)组成。

在这里插入图片描述

  • 表空间(tablespace):表空间可以看做是 InnoDB 存储引擎逻辑结构的最高层 ,所有的数据都是存放在表空间中。默认情况下有一个共享表空间 ibdata1 ,所有数据都放在这个表空间内 。
  • 段(segment):表空间由多个段组成,每一个索引在表空间中都有两个段,分别是叶子节点段(数据段)和非叶子节点段(索引段)。叶子节点保存在一个段内,可以更好的保持叶子节点的连续性,可以提升访问磁盘的效率。
  • 区(extend):多个页组成一个区,对于不大于16KB 的页来说,一个区是 1MB(64 个连续的 16KB 页或者 128 个 8KB 或者 256 个 4KB),对于大于 16K 的页,都是连续 64 个页组成一个区,比如 128KB 的页对应 2MB 的区,256KB 的页对应 4MB 的区。
  • 页(page):固定大小的一段连续空间,默认是 16KB,可以通过参数 nnodb_page_size 设置。是读写操作的最小单位
  • 行(row):指表中的一条记录,是存储的基本单位

表空间

系统表空间

System Tablespace,系统表空间可以对应文件系统上一个或多个实际的文件,默认情况下,InnoDB 会在数据目录下创建一个名为 ibdata1 的文件,大小为 12MB,这个文件就是对应的系统表空间在文件系统上的表示。这个文件是自扩展文件,当不够用的时候它会自己增加文件大小。在一个 MySQL 服务器中,系统表空间只有一份。

InnoDB 有四个最基本的系统表:

  • SYS_TABLES:记录了表的名称、每个表对应唯一的 ID、表拥有列的个数、表的类型(包括一些文件格式,行格式等)、MIX_ID、表所属表空间的 ID 等;
  • SYS_COLUMNS:记录了列所属表对应的 ID、列是表中的第几列、列名称、主数据类型、精确数据类型(比如是否允许 NULL 等)、列最多占用存储空间的字节数、列的精度【默认都是0】;
  • SYS_INDEXES:记录了索引所属表对应的 ID、索引的唯一 ID、索引名称、索引包含列的个数、索引类型、索引根页面所在表空间 ID、索引根页面所在的页面号等;
  • SYS_FIELDS:记录了索引列所属索引的 ID、索引列在某个索引中是第几列、索引列的名称等;

系统表空间与独立表空间的一个非常明显的不同之处就是在表空间开头有许多记录整个系统属性的页面:
在这里插入图片描述

独立表空间

File-per-table Tablespace,在 MySQL5.6.6 之后,InnoDB 并不会默认地把各个表的数据存储到系统表空间中,而是为每一个表建立一个独立表空间。创建一个表后,会在该表所属数据库对应的子目录下创建一个表示该独立表空间的文件,文件名为 表名.ibd

可以指定使用系统表空间还是独立表空间来存储数据,这个功能由启动参数 innodb_file_per_table 控制,在 my.cnf 配置文件中。

独立表空间整体结构如下:

在这里插入图片描述

其他表空间

  • 撤销表空间(Undo Tablespaces):专门用来存放 undolog 的日志文件。

  • 临时表空间(Temporary Tablespaces):存放用户创建的临时表和磁盘内部临时表。

  • 通用表空间(General Tablespaces):类似于系统表空间,通用表空间是共享表空间,可以存储多个表的数据。

段不对应表空间中某一个连续的物理区域,而是一个逻辑上的概念,由若干个零散的页面(碎片区)和一系列完整的区组成。

一个表空间里面会有很多个段组成。常见的段有数据段、索引段、回滚段等。

  • 数据段,leaf node segment:管理叶子节点的数据;
  • 索引段,non-leaf node segment:管理非叶子节点的数据;
  • 回滚段,rollback segment:存储 undo log,用于实现事务回滚;

每向表中插入一条记录,本质上就是向该表的聚簇索引以及所有二级索引代表的 B+ 树的节点中插入数据。

InnoDB 对 B+ 树的叶子节点和非叶子节点进行了区别对待,也就是说叶子节点有自己独有的区,非叶子节点也有自己独有的区。存放叶子节点的区的集合就算是一个段,存放非叶子节点的区的集合也算是一个段。也就是说一个索引会生成两个段,一个叶子节点段和一个非叶子节点段。

每个索引段的大小是固定的,通常为 16KB。如果索引键的大小超过了 16KB,则需要使用多个索引段来存储。

InnoDB 为每个段都定义了一个 INODE Entry 结构来记录段中的属性。

在这里插入图片描述

  • Segment ID:段的唯一 ID;
  • NOT_FULL_N_USED:指在 NOT_FULL 链表中已经使用了多少个页面;
  • 三个 List Base Node:分别为段的 FREE 链表、NOT_FULL 链表、FULL 链表定义了头节点;
  • Magic Number:用来标记这个 INODE Entry 是否已经被初始化了(初始化的意思就是把各个字段的值都填进去了)。如果这个数字是值的 97937874,表明该 INODE Entry 已经初始化,否则没有被初始化。
  • Fragment Array Entry:段是一些零散页面和一些完整的区的集合,每个 Fragment Array Entry 结构都对应着一个零散的页面,这个结构占 4 个字节,表示一个零散页面的页号。

段链表

InnoDB 为每个段中的区对应的 XDES Entry 结构建立了三个链表:

  • FREE 链表:同一个段中,所有页面都是空闲的区对应的 XDES Entry 结构会被加入到这个链表。注意和直属于表空间的FREE链表区别开了,此处的 FREE 链表是附属于某个段的。
  • NOT_FULL 链表:同一个段中,仍有空闲空间的区对应的 XDES Entry 结构会被加入到这个链表。
  • FULL 链表:同一个段中,已经没有空闲空间的区对应的 XDES Entry 结构会被加入到这个链表。

每个段都会维护上述的三个链表。

不论是系统表空间还是独立表空间,都可以看成是由若干个区组成的,每 256 个区被划分成一组。对于 16KB 的页来说,连续的 64 个页就是一个区,也就是说一个区默认占用 1MB 空间大小。

一个区中的页,在物理空间中是连续的,所以在读取时可以减少随机 IO 的次数,从而提高读取速度。

在这里插入图片描述

第一个组最开始的三个页面的类型是固定的,分别是:

  • FSP_HDR:登记整个表空间的一些整体属性以及本组所有的区。整个表空间只有一个 FSP_HDR 类型的页面。
  • IBUF_BITMAP:存储本组所有的区的所有页面关于 INSERT BUFFER 的信息。
  • INODE:存储了许多称为 INODE 的数据结构。

其余各组最开始的两个页面的类型是固定的,分别是:

  • XDES:全称是 extent descriptor,用来登记本组各个区的属性。
  • IBUF_BITMAP:同上。

XDES Entry

为了方便管理区,MySQL 设计了 XDES Entry(Extent Descriptor Entry)结构,每个区都对应着一个 XDES Entry,这个结构记录了对应的区的一些属性。

XDES Entry 结构有 40 字节,大致分为 4 个部分:

在这里插入图片描述

  • Segment ID:每一个段都有一个唯一的 ID。Segment ID 字段表示该区所在的段,前提是该区已经被分配给某个段了,不然该字段的值没有意义;
  • ListNode:这个部分可以将若干个 XDES Entry 结构串联成一个双向链表;
  • State:区的状态,总共有四种类型。处于 FREE、FREE_FRAG、FULL_FRAG 这三种状态的区都是独立的,算是直属于表空间;而处于 FSEG 状态的区是附属于某个段的
    • FREE,空闲的区:现在还没有用到这个区中的任何页面;
    • FREE_FRAG,有剩余空间的碎片区:表示碎片区中还有可用的页面;
    • FULL_FRAG,没有剩余空间的碎片区:表示碎片区中的所有页面都被使用,没有空闲页面;
    • FSEG,附属于某个段的区:每一个索引都可以分为叶子节点段和非叶子节点段,除此之外 InnoDB 还会另外定义一些特殊作用的段,在这些段中的数据量很大时将使用区来作为基本的分配单位。
  • Page State Bitmap:这个部分共占用 16 字节,也就是 128 位,一个区默认有 64 个页,这 128 位被划分为 64 个部分,每个部分有 2 位,对应区中的 1 个页。第一个位表示对应的页是否是空闲的,第二个比特位还没有用。

区链表

  • 把状态为 FREE 的区对应的 XDES Entry 结构通过 List Node 来连接成一个链表,即 FREE 链表;
  • 把状态为 FREE_FRAG 的区对应的 XDES Entry 结构通过 List Node 来连接成一个链表,即 FREE_FRAG 链表。
  • 把状态为 FULL_FRAG 的区对应的 XDES Entry 结构通过 List Node 来连接成一个链表,即 FULL_FRAG 链表。

碎片区

段是以区为单位申请存储空间的,一个区默认占用 1MB 的空间,如果为段中很小的数据量申请区,则会造成大量空间的浪费,所以 InnoDB 提出了碎片区的概念,碎片区中的页并不都是为了存储同一个段中的数据而存在的,比如有的页用于段 A,有的用于段 B,有的页甚至哪个段也不属于。碎片区直属于表空间,并不属于任何一个段。

为段分配存储空间的策略是:

  1. 起初向表中插入数据的时候,段是从某个碎片区以单个页面来分配存储空间的;
  2. 当某个段已经占用了 32 个碎片区页面(半个区)之后,就会申请以完整的区为单位来分配存储空间。

数据页

记录是按照行来存储的,但是数据库的读取并不是以行为单位的。

InnoDB 的数据是按数据页为单位来读写的,即数据库的 I/O 操作的最小单位是页,是磁盘和内存之间交互的基本单位,默认大小为 16KB。

在这里插入图片描述

在这里插入图片描述

在 File Header 中有两个指针,分别指向上一个数据页和下一个数据页,连接起来的页相当于一个双向的链表。

数据页中的记录(User Record)按照主键顺序组成单向链表

数据页中有一个页目录,起到记录的索引作用。

在这里插入图片描述

每个记录组的最后一条记录就是组内最大的那条记录,并且最后一条记录的头信息中会存储该组一共有多少条记录,作为 n_owned 字段(上图中粉红色字段)

页目录用来存储每组最后一条记录的地址偏移量,这些地址偏移量会按照先后顺序存储起来,每组的地址偏移量也被称之为槽(slot),每个槽相当于指针指向了不同组的最后一个记录

InnoDB 对每个分组中的记录条数都是有规定的,槽内的记录就只有几条:

  • 第一个分组中的记录只能有 1 条记录;
  • 最后一个分组中的记录条数范围只能在 1-8 条之间;
  • 剩下的分组中记录条数范围只能在 4-8 条之间。

数据行

MySQL 有四种行格式:Redundant,Compact,Dynamic,Compressed。我们可以在创建表的时候指定字符集和行格式。

MySQL 5.7 默认使用的是 Dynamic 的行格式。

Redundant

Redundant 是 MySQL5.0 版本之前的行格式,现在一般不会使用,只有 MySQL 的一些系统表会使用。

在这里插入图片描述

整个行格式分为记录的额外信息和记录的真实数据两部分,其中记录的额外信息又分为两部分:

  • 字段长度偏移列表:会把所有字段的真实数据占用的字节长度都存放在记录的开头部位,从而形成⼀个字段长度偏移列表,字段长度占用的字节数按照列的顺序逆序存放
  • 记录头信息:占用 6 字节,即 48 位,具体如下:
名称大小(bit)描述
预留位11未使用
预留位21未使用
delete_mask1该记录是否删除
min_rec_mask1B+树每层非叶子节点小记录都会添加该标记
n_owned4当前记录组拥有记录数
heap_no13当前记录在页面堆位置信息
n_field10记录中列数量
1byte_offs_flag1字段长度偏移列表中每个列对应的偏移量是使用1字节还是2字节表示的
next_record16下一条记录的相对位置

记录的真实数据除了列数据以外,还会为每个记录默认添加一些列(也称为隐藏列),具体如下:

  • DB_ROW_ID(row_id) :占 6 个字节。当表没有定义主键,则选择 unique 键作为主键;如果仍没有,则默认添加一个名为 DB_ROW_ID 的隐藏列作为主键。也就是说这个列只有当没有主键也没有索引时才存在;

  • DB_TRX_ID(transaction_id):占 6 字节。事务 id;

  • DB_ROLL_PTR(roll_pointer):占 7 字节。回滚指针,MVCC 的时候会用到;

  • DB_DELETED_BIT(deleted_bit):占 1 字节,用于标识记录是否已删除;

Compact

Compact 是一种紧凑的行格式,设计的初衷就是为了让一个数据页中可以存放更多的数据行。

Compact 描述信息的格式如下:

在这里插入图片描述

变长字段长度列表中存储的是每个非空的变长字段的数据长度,同样也是逆序存放的。如果表中没有变长的数据类型的话,就不需要该列表了。如果长度小于 255B,就用 1B 表示,否则用 2B 表示,最大不会超过 2B,因为 MySQL 中变长类型的最大字节长度限制为 65535。

如果表中没有允许 NULL 的列,那么 NULL 标记位就不存在了。否则将每个允许存储 NULL 的列对应一个二进制位并且逆序存放,然后将允许为 NULL 的位标为 1,不允许的标为 0;且必须用整数个字节的位表示,否则在高位补 0。

Compact 格式的记录头具体信息如下:

名称大小(bit)描述
预留位11未使用
预留位21未使用
delete_mask1该记录是否删除
min_rec_mask1B+树每层非叶子节点小记录都会添加该标记
n_owned4当前记录组拥有记录数
heap_no13当前记录在页面堆位置信息
record_type3表⽰当前记录的类型,0表⽰普通记录,1表⽰B+树⾮叶⼦节点记录,2表⽰⼩记录,3表⽰⼤记录
next_record16下一条记录的相对位置

相比 Redundant,多了一个 record_type 字段,少了 n_field 和 1byte_offs_flag 两个字段。

Compact 的隐藏列和 Redundant 相同。

Compact 格式下,具有 65535 字节的最大行大小限制,但是设置允许 NULL 需要占用 1 个字节,设置为变长(varchar)需要占用两个字节。在不同的编码格式下,一个字符占用的字节是不同的,比如 encode=utf8 时,三个字节表示一个字符。

数据页默认大小为 16KB,即 16*1024=16384byte,所以当要存储地单行数据超过 16384byte 时,会发生行溢出。

但在 MySQL 的设定中,会提早发生行溢出。当列的类型为 VARCHAR、 VARBINARY、 BLOB、TEXT 时,该列超过 768byte 的数据溢出存放到溢出页中,然后通过一个偏移量指针将两者关联起来,这就是行溢出机制。这样可以有效地防止单个列太大导致单个数据页中存放的行记录过少而让 IO 飙升的情况。

在这里插入图片描述

Compact 能比 Redundant 格式节约 20% 的存储空间。

Dynamic

Dynamic 和 Compact 行格式类似,只是在处理行溢出数据时有区别。Dynamic 的数据页不会存储真实数据的前 768 字节,而是存储 20 个字节的指针来指向溢出页,即将所有的溢出数据都存在溢出页中。

Compressed

Compressed 与 Dynamic 相似,区别是 Compressed 存储的行数据会以 zlib 的算法进行压缩以节省空间,因此对于 BLOB、TEXT、VARCHAR 这类大长度类型的数据能够进行非常有效的存储。

数据字典

系统表中保存了许多额外的信息,包括表结构、数据库名或表名、字段的数据类型、视图、索引、表字段信息、存储过程、触发器等内容,这些数据统称为元数据(元信息),这些元数据的集合(系统表)就成为数据字典

在 MySQL8.0 以前,数据字典是以文件的形式存储的,在 MySQL8.0 以后,全都存放到数据库系统表中了。

最后

本文介绍了 MySQL 底层的逻辑存储结构,每一条数据都是一个数据行,数据行是存储数据的基本单位。若干条数据会组成一个数据页,数据页是读写的基本单位,可以减少磁盘 IO 的次数。若干个连续的页会组成一个区,可以降低随机 IO 的次数,来提高查询效率,同时也便于进行管理。不同的区可以按照逻辑分布到不同的段中,可以更好地进行数据管理以及提高空间利用率。

下一节将介绍 MySQL 的逻辑架构和 SQL 语句的具体执行流程。

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

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

相关文章

华为OD机试 - 有效的括号(Java)

一、题目描述 给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。…

力扣习题+——单链表

宝子,你不点个赞吗?不评个论吗?不收个藏吗? 最后的最后,关注我,关注我,关注我,你会看到更多有趣的博客哦!!! 喵喵喵,你对我真的很重要…

Redis 学习笔记

一、redis中的常见数据结构 Redis共有5种常见数据结构,分别字符串(STRING)、列表(LIST)、集合(SET)、散列(HASH)、有序集合(ZSET)。 二、redis中字符串(String)介绍 String 类型是…

[ 云计算 华为云 ] 华为云开天 aPaaS:构建高效的企业数字化平台(上)

文章目录 前言一、 什么是 aPaaS1.1 初识 aPaaS 二、华为云开天 aPaaS2.1 华为云服务类型与种类2.1.1 基础 aPaaS2.1.2 行业 aPaaS(一)工业 aPaaS(二)政务 aPaaS(三)电力 aPaaS(四)矿…

“深圳十大金口碑人物”优必选科技创始人兼CEO周剑获此殊荣

深圳晚报社联合深圳市诚商信用评级有限公司、深圳市诚信营商促进会和中国善网,共同举办了首届“金口碑”评选活动。活动涵盖多个领域,历经多个环节的评定和实地走访,最终有10名个人、20家企业和70家商户成功获得“金口碑人物”、“金口碑企业…

苹果13白屏无法正常开机怎么办?可以尝试这2种解决方法!

iPhone13莫名重启卡在白苹果,iPhone13开机却一直卡在开机画面,无法正常开机启动。 iPhone13出现以上问题,虽然都是处于苹果logo的状态,都可称为白苹果。 苹果13出现白屏、黑屏、白苹果等故障时,我们该怎么进行强制重启…

网络基础知识2—网络

文章目录 1.IP地址1.1概念1.2作用1.3组成1.4格式1.5举例1.6分类1.7特殊的IP 2.子网掩码2.1作用2.2是否是同一网段 3.Mac地址4.网络数据传输—一跳一跳的过程4.1网络节点4.2IP地址和Mac地址的区别 5.网络硬件设备及技术5.1集线器5.2交换机5.3主机5.4ARP缓存表:主机和…

玩机搞机----rom中build.prop文件的修改 优化 删除 开启等等

对于安卓机型系统中的build.prop文件是必不可少的,网络也很多这类的优化教程。但大多都是老机型的解释,今天这个帖子针对一些新版机型build.prop文件的一些操作实例来分析下。友情提示,修改这个文件有风险。严重会进不去系统。而且机型不同。…

Windows11关闭Edge/Chrome浏览器触摸板双指前进后退手势(防止误触切换页面)

编辑了好久的东西,就因为手势误触一下子切换页面了,难受啊! Mac版本的设置参考这篇(他这个是通过终端命令做的): Mac Chrome浏览器,关闭双指前进、后退手势 我现在身边没有Mac,不知道…

在线UI设计工具都有哪些?如何选择?

做UI设计不仅要阅读UI设计技巧文章,还要掌握好用的UI设计工具,增加UI设计的实践经验,从而熟能生巧,提高UI设计效率。 找到一个好的在线UI设计工具,比如鱼遇到水,就会开启高效的UI设计。因此,掌…

【ONE·C++ || set和map(二):AVL树和红黑树】

总言 主要介绍AVL树和红黑树的部分框架结构和特性 文章目录 总言1、AVL树1.1、基本概念介绍1.2、重要框架与特性实现1.2.1、如何搭建AVL树的结点 :AVLTreeNode,引入三叉链1.2.2、如何搭建AVL树1.2.3、AVL树插入讲解 1.3、AVL树插入1.3.1、step1&#xf…

超越ChatGPT?新发布:“讯飞星火认知大模”到底行不行?

国内又一巨头发布 大语言模型,是 PPT 融资还是真材实料 ? 作为程序员,到底面对这一趋势,我们何去何从 ? 目录 讯飞星火,5月6日如约而至 一、你真的了解科大讯飞吗? 二、讯飞星火大模型将超越…

C++ 面向对象特征4 多态(Polymorphism)

目录 1、对多态的理解 2、实现多态的例子 3、多态的意义 4、静态联编与动态联编 1、对多态的理解 同一对象可以有多重层级递进身份 在不同的场合中,被外界所关注的是不同的身份,但本质和应有的行为并不会因外界眼光而改变。 比如说我自己 kali-Myon…

Dev C++中出现 undefined reference to XXX 错误的解决方式

出现 undefined reference to XXX 错误的现象: 主函数中调用在其他文件中定义的函数,编译报错:未定义的引用xxx。 原理:编译器在生成可执行文件的过程包括预处理、编译、汇编、链接,这4个过程,这个问题一般…

【AI】YOLOv3原理详解

1、前言 YOLOv1~3作者是约瑟夫雷德蒙(Joseph Chet Redmon),他的网站:https://pjreddie.com/ YOLOv1网站:https://pjreddie.com/darknet/yolov1/ YOLOv2网站:https://pjreddie.com/darknet/yolov2/ YOLOv3网站:https://pjreddie.com/darknet/yolo/ YOLOv4作者是Alexeyab…

KingbaseES V8R6 集群运维案例--备库timeline not contain minimum recovery point故障

​ 案例现象: KingbaseES V8R6集群备库启动后,加入集群失败,sys_log日志信息提示,如下图所示: 适用版本:kingbaseES V8R6 一、问题分析 在timeline对应的history文件中会记录每次timeline切换时所对应的lsn…

【C++起飞之路】初级——命名空间、输入输出流

C&#xff1a;命名空间、输入输出流 一、命名空间1、命名空间的定义2、命名空间中成员的使用非嵌套命名空间&#xff1a;嵌套命名空间&#xff1a; 3、命名空间 三种展开方式<1>指定命名空间访问<2> using声明a、using引入某个成员b、using展开命名空间❗注意&…

离散化(算法)

目录 一、离散化的概念二、离散化的模板三、离散化的应用题目思路分析代码实现 一、离散化的概念 离散化是一种将连续数据映射到离散值的过程。它通常用于优化某些算法&#xff0c;尤其是与区间查询相关的问题。 在离散化过程中&#xff0c;我们将一组实数转换为一组整数&#…

美团企业版背后,费控和「know-how」的双重苦战

在企业消费管理的赛道里&#xff0c;美团不能算新手&#xff0c;但客观来看&#xff0c;也并不算是熟练的玩家。对这家中国骨灰级的玩家而言&#xff0c;这不仅是一场费控报销的仗&#xff0c;更是一个从C到B的观念转型之路。 作者|思杭 编辑|皮爷 出品|产业家 美团再出招。 …

从零构建自己的脚手架

从零构建自己的脚手架 简介 什么是CLI CLI 全称是 Command Line Interface&#xff0c;是一类通过命令行交互的终端工具。日常工作中常用的脚手架有 vue-cli、create-react-app、angular-cli 等&#xff0c;都是通过简单的初始化命令&#xff0c;完成内容的快速构建。 为什…