1. 前言

目前为止我们已经知道,「行格式」决定了记录在磁盘中的存储格式,记录通过头信息里的指针串联成单向链表。为了更好的管理记录,InnoDB使用「页」为基本单位来存储记录,页与页之间串联成双向链表。同时,为了提高记录的检索效率,InnoDB借鉴了页中Page Directory的设计,给所有叶子节点页建立目录项记录,存储在内节点中,以此来构建一棵树,也就是我们熟悉的B+树索引。

这一切看起来好像都没有问题,InnoDB可以正常工作,但是在性能上会存在一些问题。我们往表中插入记录,本质上是往聚簇索引B+树和二级索引B+树插入记录,往这些树插入记录本质上是往树的节点插入记录,树的每一个节点都对应一个索引页。页的File Header部分有PREVNEXT指针记录上一个页和下一个页的页号,以此来构建双向链表,这样就可以使得逻辑上相邻的页,在物理上可以不连续。但也会带来一个问题,如果InnoDB以页为基本单位来申请磁盘空间,那么很可能会导致逻辑上相邻的页在物理上距离很远,这必然会导致大量的随机IO,机械硬盘的随机IO性能是很差的,磁头每次都需要重新寻址。为了解决这个问题,InnoDB才引入了区、组、段的概念,致力于将逻辑上相邻的页在物理上也尽可能的相邻,将随机IO尽量转化为顺序IO。注意,这里说的是“尽量”,InnoDB会尽量保证页连续,即使不连续也可以正常工作,只是性能会差一点。

2. 表空间

InnoDB支持多种表空间:

  • 系统表空间
  • 独立表空间
  • undo表空间
  • 临时表空间
  • 通用表空间

其中系统表空间和独立表空间可以用于存储我们的表数据和索引数据,因此我们重点关注。
系统表空间对应文件系统里的一个或多个文件,默认情况下,InnoDB会有一个共享表空间ibdata1,所有的数据都存放在这个表空间里,查看系统表空间配置的命令:

SHOW VARIABLES LIKE 'innodb_data_file_path';
结果: ibdata1:12M:autoextend

可以看到,默认的系统表空间对应磁盘上的ibdata1文件,默认初试大小是12M,这也太小了,没多少数据就装满了。别担心,后面的autoextend代表它是一个自增长文件,容量不够用了会自动扩容。

另外,你也可以通过参数innodb_file_per_table为每个表单独创建表空间,也就是独立表空间,查看是否开启独立表空间的命令如下:

SHOW VARIABLES LIKE 'innodb_file_per_table';
结果: NO

笔者的MySQL实例是开启了独立表空间的,这么一来InnoDB就会为每个表单独创建表名.frm表名.ibd两个文件。前者是表结构定义文件,通常很小,后者用来存储表中的数据和索引,会随着记录数的增多而变大。

简单总结:系统表空间对应着文件系统上的一个或多个文件,独立表空间对应着表名.ibd文件,大家可以把表空间看作是一个许多页的大池子,当我们需要插入数据时,就会从这个池子里拿出一些页来使用。

3. 区的概念

前面已经说过,InnoDB通过页来管理数据没什么不妥,也可以正常工作,只是性能不太理想,因为逻辑上相邻的页物理上可能距离很远,导致大量的随机IO。为了改善这个问题,InnoDB引入了区(extent)的概念。

InnoDB规定,物理上连续的64个页就是一个区,即一个区的大小是16KB*64=1MB,无论是系统表空间还是独立表空间,都是由若干个连续的区组成的。连续的256个区就是一个组,即一个组的大小是256MB。

有了区以后,InnoDB就可以以「区」为单位来给表中的数据和索引分配空间了,当表中数据量很大时,甚至会一次性分配好几个连续的区,这么做的好处就是这些页在物理上也是连续的,随机IO可以优化为顺序IO,缺点是可能这些页用不完,有点浪费空间,不过总得来说是值得的。

InnoDB将「区」分为四种状态,分别是:

状态名 说明
FREE 空闲区
FREE_FRAG 有空闲页面的碎片区
FULL_FRAG 没有空闲页面的碎片区
FSEG 隶属于某个段的区

段是一个逻辑上的概念,一棵B+树会有两个段,分别是叶子节点段和非叶子节点段。后面的文章会详细介绍段,这里先跳过。

只有FSEG状态的区直接隶属于段,其它三种状态的区只属于表空间,不属于任何段。段隶属于表,更准确点说,段隶属于某个具体的B+树索引,也就是说,一旦某个区状态为FSEG,那就意味着它里面的页只会存储这棵B+树的记录。

一张表最少有一棵B+树,也就是最少有2个段,每个段分配一个区,也就意味着即使表里只有1条记录也会占用2MB的空间,这未免也太浪费了,为了解决这个问题,InnoDB采用“先分配零散页再分配区”的策略,所以,InnoDB为表记录分配空间的策略是这样的:一开始,所有的区都是隶属于表空间的FREE状态,当我们向表中插入记录时,InnoDB会拿出一个FREE区,并把它的状态改为FREE_FRAG,然后从区里拿出一个页来存储记录,当区里没有空闲空间了就会把它的状态改为FULL_FRAG,然后继续拿FREE区重复前面的过程。此时表的数据相当于是零散的存储在这些碎片区的,我们暂且称这些页为「零散页」,当某个段占用的零散页数量超过32时,InnoDB将不再以页为单位分配空间了,而是以区为基本单位,这样就避免了少量数据也会占用2个区的问题。

3.1 XDES Entry

如何知道某个区隶属于哪个段呢?以及区里页的使用情况?为了更好的管理区,InnoDB会为每个区创建一个XDES Entry节点,每个XDES Entry节点占用固定的40个字节,结构如下表:

名称 大小 说明
Segment ID 8Byte 所属的段ID
List Node 12Byte XDES Entry形成双向链表的指针
State 4Byte 区的状态(共4种)
Page State Bitmap 16Byte 区内页的状态位图
  • List Node

List Node由两个指针组成,用于将多个XDES Entry节点串联成双向链表,结构如下:

名称 大小 说明
Prev Node Page Number 4字节 上一个XDES Entry节点所在的页号
Prev Node Offset 2字节 上一个XDES Entry节点在页内的地址偏移量
Next Node Page Number 4字节 下一个XDES Entry节点所在的页号
Next Node Offset 2字节 下一个XDES Entry节点在页内的地址偏移量

XDES Entry也是存储在页里的,通过页号+地址偏移量就可以快速定位到具体的XDES Entry节点。默认页大小16KB,2字节记录地址偏移量够用了。
Tips:组由256个连续的区组成,对应着256个XDES Entry节点,这些XDES Entry节点会被固定的存储到组内的第1个页里,物理结构上是连续的,压根就不需要指针,该指针是根据区的State串联起来的逻辑上的一个链表。

  • Page State Bitmap

记录了区内64个页面是否空闲,占用16字节,也就是128位。每2个位代表一个页,第1位表示对应的页是否空闲,第2位暂时还没使用。

3.2 XDES页

组由256个连续的区组成,也就对应着256个XDES Entry节点,那这些节点存储在哪里呢?
我们之前说过,InnoDB为了不同的目的,设计了很多不同类型的页,其中就包括专门用来存储XDES Entry节点的页,页类型是FIL_PAGE_TYPE_XDES,简称XDES页,它的页结构如下所示:

名称 大小 说明
File Header 38字节 所有页的通用文件头信息
Empty Space 112字节 没有使用
XDES Entry 40*256字节 256个XDES Entry节点
Empty Space 5986字节 没有使用
File Trailer 8字节 所有页的通用文件尾信息,校验页是否完整

XDES页固定为每个组的第1个页面,也就是说,每个组都会拿出最前面的一个页面来记录组内256区对应的XDES Entry节点。

表空间第一个组的第1个页面类型为FIL_PAGE_TYPE_FSP_HDR,它和XDES页极为相似,只是多了File Space Header部分来记录表空间相关的状态信息。

3.3 XDES Entry链表

现在我们已经知道,当表中数据很少时,InnoDB会先从隶属于表空间的碎片区来分配零散页。当段使用的零散页数量超过32开始以区为单位来分配空间。这里有两个问题:

  1. 如何知道表空间里哪些区是FREE?哪些是FREE_FRAG?
  2. 隶属于段的FSEG区,哪些是有空闲空间的?哪些是未使用的?

这两个问题其实本质是一样的,解决方式也是一样的。当然,遍历所有的XDES Entry也是一种解决方案,只不过这种方案太笨了,当表中数据达到1GB,XDES Entry数量就上千了,遍历的效率太低了,InnoDB当然不会这么做。
还记得XDES Entry的List Node部分吗?它是两个指针,可以把多个XDES Entry构建成双向链表。我们可以根据区的State来构建链表,那么隶属于表空间的区就会形成三条链表:

  • FREE链表:将所有FREE区串联起来的链表。
  • FREE_FRAG链表:将所有FREE_FRAG区串联起来的链表。
  • FULL_FRAG链表:将所有FULL_FRAG区串联起来的链表。

如此一来,当我们要分配零散页时,就从FREE_FRAG链表中拿出一个区开始分配,这个区用完了就将它的状态改为FULL_FRAG,然后从FREE_FRAG链表移除并添加到FULL_FRAG链表。重复前面的过程,当FREE_FRAG链表没有可用的区了,就从FREE链表拿出一个区并把它的状态改为FREE_FRAG,同时加入到FREE_FRAG链表,再重复前面的操作。

再来看第二个问题,本质是一样的,还是将FSEG区根据State串联成链表。一棵B+树有两个段,叶子节点和非叶子节点是分开存储的,每个段又对应三条链表:

  • FREE链表:隶属于同一个段的所有未使用的区自动加入到该链表。
  • NOT_FULL链表:隶属于同一个段所有已使用但还有空闲空间的区自动加入到该链表。
  • FULL链表:隶属于同一个段所有已经用完没有空闲空间的区自动加入到该链表。

分配方式和表空间的链表一样,这里不再赘述了。

也就是说,当我们创建一张只有主键没有二级索引的表,它内含了9条XDES Entry链表。隶属于表空间的三条,主键索引B+树的两个段各三条,一共9条。

3.4 链表基节点

一张只包含主键的最简单的表也会有9条XDES Entry链表,当我们向表中插入记录时,就需要找到对应的链表,从区里面取出页来存储记录,那如何找到这些链表呢?

为了解决这个问题,InnoDB设计了一个叫作「List Base Node」(链表基节点)的结构,每个链表基节点占用固定的16字节,结构如下:

名称 大小 说明
List Length 4字节 链表节点总数
First Node Page Number 4字节 链表头XDES Entry节点所在页的页号
First Node Offset 2字节 链表头XDES Entry节点所在页的地址偏移量
Last Node Page Number 4字节 链表尾XDES Entry节点所在页的页号
Last Node Offset 2字节 链表尾XDES Entry节点所在页的地址偏移量

链表基节点结构很简单,前4个字节记录链表里的节点数量,后12个字节指向头尾节点。另一个问题又来了,链表基节点又存在哪里呢?

我们已经知道,XDES Entry链表有两类,一种是隶属于表空间的,一种是隶属于段的。
隶属于表空间的XDES Entry链表基节点存放在表空间第1组的第1个页面的File Space Header里,16*3个字节存储了三个链表基节点,分别指向前面说的三条链表。InnoDB会为每个段创建一个INODE Entry节点,隶属于段的XDES Entry链表基节点自然也就存放在INODE Entry节点里了,占用16*3个字节,三个链表基节点分别指向前面说的三条链表。

4. 总结

InnoDB索引页通过两个指针将逻辑上相邻的页串联成双向链表,使得这些索引页不需要物理上连续,但是可能会导致逻辑上相邻的页物理上距离很远,这样在读取数据时就会导致大量的随机IO。为了将随机IO尽可能的优化为顺序IO来提升性能,InnoDB引入了区、组、段的概念。物理上连续的64个页为一个区,连续的256个区为一组。如此一来,InnoDB就可以以“区”为单位分配空间了。为了更好的管理这些区,InnoDB会为每个区都创建一个XDES Entry节点,这些节点被专门存放在XDES页中,固定为每个组的第1个页。通过XDES Entry节点就可以知道区属于哪个段、区内页面的使用情况,以及将区按照State串联成链表,方便InnoDB更快的查找指定State的区。XDES Entry节点根据页的使用情况可以拆分成三条链表,为了找到这些链表,InnoDB专门设计了「链表基节点」结构,它记录了链表的节点数和头尾节点指针。这些链表基节点如果是隶属于表空间的,会存放在表空间第1组的第1个页面里;如果是隶属于段的,会存放在段对应的INODE Entry节点里。均占用16*3字节,分别指向了三条不同状态的链表。如此一来,当InnoDB要为记录分配零散页时,就去表空间对应的链表里的找到碎片区并进行分配;当段占用的零散页个数超过32时,InnoDB就去段对应的链表里找到有空闲页的区并进行分配。

InnoDB表空间之区的概念相关推荐

  1. MySQL怎么运行的系列(五)Innodb表空间(table space)、区(extent)和段(segment)

    本系列文章目录 展开/收起 MySQL怎么运行的系列(一)mysql体系结构和存储引擎 MySQL怎么运行的系列(二)Innodb缓冲池 buffer pool 和 改良版LRU算法 Mysql怎么运 ...

  2. MySQL(九):InnoDB 表空间(Tables)

    本节着重分析一下表空间,通过本节我们将清楚以下几个问题: 1.什么是表空间(Tablespace)? 2.InnoDB 存储引擎有哪些表空间(Tablespace)? 3.InnoDB 存储引擎中的表 ...

  3. mysql创建数据库时候同时创建表空间_MySQL 创建InnoDB表空间_编程学问网

    15.2.5. 创建InnoDB表空间 假设你已经安装了MySQL,并且已经编辑了选项文件,使得它包含必要的InnoDB配置参数.在启动MySQL之前,你应该验证你为InnoDB数据文件和日志文件指定 ...

  4. mysql表空间权限_MySQL InnoDB表空间加密示例详解

    前言 从 MySQL5.7.11开始,MySQL对InnoDB支持存储在单独表空间中的表的数据加密 .此功能为物理表空间数据文件提供静态加密.该加密是在引擎内部数据页级别的加密手段,在数据页写入文件系 ...

  5. mysql 查看表v空间自增涨_MySQL InnoDB表空间加密

    从 MySQL5.7.11开始,MySQL对InnoDB支持存储在单独表空间中的表的数据加密 .此功能为物理表空间数据文件提供静态加密.该加密是在引擎内部数据页级别的加密手段,在数据页写入文件系统时加 ...

  6. MYSQL 5.7 INNODB 表空间

    背景介绍 由于InnoDB 引擎支持ACID.良好的读写性能,还有许多其他对数据库服务具有重要意义的特性,InnoDB已经成为MySQL最受欢迎的存储引擎. 在本文中,我们将介绍InnoDB表空间和它 ...

  7. mysql表空间不足_MySQL Innodb表空间不足的处理方法 风好大

    官方给出的解决方案: 添加和删除 InnoDB 数据和日志文件 这一节描述在InnoDB表空间耗尽空间之时,或者你想要改变日志文件大小之时,你可以做的一些事情. 最简单的,增加InnoDB表空间大小的 ...

  8. mysql5.7.16 表空间加密,MySQL :: MySQL 5.7参考手册:: A.16 MySQL 5.7 FAQ:InnoDB表空间加密...

    A.16.1. 数据是否被授权查看的用户解密? 是.InnoDB表空间加密旨在为客户提供在数据库中透明地应用加密而不影响现有应用程序的能力.以加密格式返回数据会破坏大多数现有的应用程序.InnoDB表 ...

  9. mysql5.7.16 表空间加密_技术分享 | InnoDB 表空间加密

    本文目录: 一.表空间加密概述 应⽤场景 加密插件 加密限制 注意事项 二.加密表空间 安装加密插件 配置表空间加密 查看表空间被加密的表 三.更新 master encryption key 四.导 ...

最新文章

  1. 记录一次简单、高效、无错误的linux上安装pytorch的过程
  2. 《精通自动化测试框架设计》—第2章 2.3节测试数据交互基本方法
  3. 内存泄露从入门到精通三部曲之常见原因与用户实践
  4. href 一个正则表达式的解析 ? 号解析
  5. The Design and Implementation of Open vSwitch
  6. vst3插件_Blue Cat Audio Blue Cat PatchWork mac(蓝猫桥接插件)
  7. JSF Struts Spring Hibernate 整合
  8. Linux unshare命名的一些例子
  9. 图论:弦图最小点染色
  10. Elasticsearch 简介入门
  11. 容器编排技术 -- 安装和设置kubectl
  12. iOS 开发----Xcode 因为证书问题经常报的那些错
  13. vue多个根节点上的属性继承
  14. BZOJ4480[JSOI2013]快乐的jyy
  15. MySQL内存----使用说明全局缓存+线程缓存) 转
  16. php 判断来访客户端是否移动设备
  17. jquery 时间选择插件-jedate
  18. 长风破浪正其时,Python天堑变通途(3)(令人智熄的分支循环,优先级问题)
  19. android入门之系统架构和环境搭建
  20. 计算机科学期末网页大作业快看漫画源码(纯享免费版)需要自取免费配置环境

热门文章

  1. 2021 蓝桥杯省赛第一场 C++ 大学 B 组
  2. 菲尔兹奖得主再次突破数论难题:多少整数能写成2个有理数立方和?结论直接影响“千禧难题”之七...
  3. Mysql使用on update current_timestamp
  4. 22.12.1的学习笔记
  5. 重新定义一个全新的区块链运行架构:他们的技术有何不同?
  6. Unity初级教程贪吃蛇实现(Snake)带工程源码
  7. 【Cpp】《Effective C++》第一章-让自己习惯C++
  8. 以AI绩效管理切入产研赛道,方云智能获近千万元天使轮融资
  9. 手机APP开发(安卓、IOS)logo图标在线生成工具上线啦。
  10. 微信开放平台创建android应用时怎么获取应用签名