区、段与碎片区

区、段与碎片区

为什么要有区?

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

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

为什么要有段?

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

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

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

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

为什么要有碎片区

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

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

  • 在刚开始向表总插入数据的时候,段是从某个碎片区以单个页面为单位来分配存储的。
  • 当某个字段已经占用了32个碎片区页面之后,就会申请以完整的区为单位来分配存储空间。
    所以现在段不能仅定义为是某些区的集合,更精确的应该是某些零散的页面以及一些完整的区的集合。

区的分类

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

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

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

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

表空间

表空间可以看做是InnoDB存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。
表空间是一个逻辑容量,表空间存储的对象是段,在一个表空间中可以有一个或多个段,但是一个段只能属于一个表空间。表空间数据库由一个或多个表空间组成,表空间从管理上可以划分为系统表空间(System Tablespace)、独立表空间(File-per-table Tablespace)、撤销表空间(Undo Tablespace)和临时表空间(Temporary Tablespace)等。

独立表空间

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

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

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

真实表空间对于的文件大小
我们到数据目录里看,会发现一个新建的表对应的.ibd文件只占用了96k(Mysql5.7),才6个页面的大小,这是因为一开始表空间占用的空间很小,因为表里边都没有数据,不过别忘了这些.ibd文件是自扩展的,随着表中的数据增多,表空间对应的文件也逐渐增大。

查看InnoDB的表空间类型

SHOW VARIABLES LIKE 'INNODB_FILE_PER_TABLE'
Variable_nameValue
innodb_file_per_tableON

系统表空间

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

InnoDB数据字典
每当我们向一个表中插入一条记录的时候,MySQL校验过程如下:
先要校验一下插入语句对应的表存不存在,插入的列和表中的列是否符合,如果语法没有问题的话,还需要知道该表的聚簇索引和所有二级索引对应的根页面
是哪个表空间的哪个页面,然后把记录插入对应索引的B+树种。所以说,MySQL除了保存着我们插入的用户数据之外,还需保存许多额外的信息,比方说:

  • 某个表属于哪个表空间,表里边有多少列
  • 表对应的每一个列的类型是什么
  • 该表有多少索引,每个索引对应哪几个字段,该索引对应的根页面在哪个表空间的哪个页面
  • 该表有哪些外键,外键对应哪个表的哪些列
  • 某个标记对应文件系统上文件路径是什么
    上述这些数据并不是我们使用INSERT语句插入的用户数据,实际上是为了更好的管理我们这些用户数据而不得已引入的一些额外数据,这些数据也称为元数据。InnoDB存储引擎特意定义了一些列的内部系统表(internal system table)来记录这些元数据。
表名描述
SYS_TABLES整个InnoDB存储引擎中所有的表的信息
SYS_COLUMNS整个InnoDB存储引擎中所有的列的信息
SYS_INDEXS整个InnoDB存储引擎中所有的索引的信息
SYS_FIELDS整个InnoDB存储引擎中所有的索引对应的列的信息
SYS_FOREIGN整个InnoDB存储引擎中所有的外键的信息
SYS_FOREIGN_COLS整个InnoDB存储引擎中所有的外键对应列的信息
SYS_TABLESPACES整个InnoDB存储引擎中所有的表空间信息
SYS_DATAFILES整个InnoDB存储引擎中所有的表空间对应文件系统的文件路径信息
SYS_VIRTUAL整个InnoDB存储引擎中所有的虚拟生成列的信息

这些系统表也被称为数据字典,它们都是以B+树的形式保存在系统表空间的某些页面中,其中SYS_TABLES、SYS_COLUMNS、SYS_INDEXS、SYS_FIELDS这四个表尤其重要,称之为基本系统表(basic system tables),我们先看看这个4个表的结构;

SYS_TABLES表结构

列名描述
NAME表的名称、主键
IDInnoDB存储引擎中每个表都有一个唯一的ID。(二级索引)
N_COLS该表拥有列的个数
TYPE表的类型,记录了一些文件格式、行格式、压缩等信息
MIX_ID已过时、忽略
MIX_LEN表的一些额外的属性
CLUSTER_ID未使用、忽略
SPACE该表所属表空间的ID

SYS_COLUMNS表结构

列名描述
TABLE_ID该列所属表对应的ID。(与POS一起构成联合主键)
SOS该列在表中是第几列
NAME该列的名字
MTYPEmain data type,主数据类型,就是那堆INT、CHAR、VARCHAR、FLOAT、DOUBLE之类东东
PRTYPEprecise type,精确数据类型,就是修饰主数据类型的那堆东东,比如是否允许NULL值,是否允许负数啥的
LEN该列最多占用存储空间的字节数
PREC该列的精度,不过这列貌似都没有使用,默认值都是0

SYS_INDEXS表结构

列名描述
TABLE_ID该列所属表对应的ID。(与POS一起构成联合主键)
IDInnoDB存储引擎中每个索引都有一个唯一的ID
NAME该索引的名字
M_FIELDS该索引包含列的个数
TYPE该索引的类型、比如聚簇索引、唯一索引、更改缓冲区额度索引、全文索引、普通的二级索引等等各种类型
SPACE该索引根页面所在的表空间ID
PAGE_NO该索引根页面所在的页面号
MERGE_THRESHOLD如果页面中的记录被删除到某个比例,就把该页面和相邻页面合并,这个值就是这个比例

SYS_FIELDS表结构

列名描述
INDEX_ID该索引列所属的索引的ID。(与POS一起构成联合主键)
POS该索引列在某个索引中是第几列
COL_NAME该列的名字

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

Tables_in_information_schema (INNODB%)
INNODB_BUFFER_PAGE
INNODB_BUFFER_PAGE_LRU
INNODB_BUFFER_POOL_STATS
INNODB_CACHED_INDEXES
INNODB_CMP
INNODB_CMP_PER_INDEX
INNODB_CMP_PER_INDEX_RESET
INNODB_CMP_RESET
INNODB_CMPMEM
INNODB_CMPMEM_RESET
INNODB_COLUMNS
INNODB_DATAFILES
INNODB_FIELDS
INNODB_FOREIGN
INNODB_FOREIGN_COLS
INNODB_FT_BEING_DELETED
INNODB_FT_CONFIG
INNODB_FT_DEFAULT_STOPWORD
INNODB_FT_DELETED
INNODB_FT_INDEX_CACHE
INNODB_FT_INDEX_TABLE
INNODB_INDEXES
INNODB_METRICS
INNODB_SESSION_TEMP_TABLESPACES
INNODB_TABLES
INNODB_TABLESPACES
INNODB_TABLESPACES_BRIEF
INNODB_TABLESTATS
INNODB_TEMP_TABLE_INFO
INNODB_TRX
INNODB_VIRTUAL

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值