认真学习InnoDB的数据存储结构中的区、段与表空间
关联博文:
认真学习InnoDB的数据存储结构
认真学习InnoDB的行格式
认真学习InnoDB的数据存储结构中的区、段与表空间
前文认真学习InnoDB的数据存储结构我们学习了页并引入了区、段与表空间的概念。认真学习InnoDB的行格式一文中我们学习了数据页的每一行如何存储。本文我们就着重学习区、段与表空间。
首先回顾一下InnoDB的存储结构。基本结构为页,也就是由一个个数据页构成。在数据库中,还存在着区(Extent)、段(Segment)和表空间(Tablespace)的概念。行、页、区、段、表空间的关系如下图所示:
区(Extent)是比页大一级的存储结构,在InnoDB存储引擎中,一个区会分配64个连续的页。因为InnoDB中的页大小默认是16KB,所以一个区的大小是64*16KB=1MB。
段(Segment)由一个或多个区组成,区在文件系统是一个连续分配的空间(在InnoDB中是连续的64个页),不过在段中不要求区与区之间是相邻的。段是数据库中的分配单位,不同类型的数据库对象以不同的段形式存。当我们创建数据表、索引的时候,就会相应创建对应的段,比如创建一张表时会创建一个表段,创建一个索引时会创建一个索引段。
表空间(Tablespace)是一个逻辑容器,表空间存储的对象是段,在一个表空间中可以有一个或多个段,但是一个段只能属于一个表空间。数据库由一个或多个表空间组成,表空间从管理上可以划分为系统表空间、用户表空间、撤销表空间、临时表空间。
【1】为什么要有区?
B+树中的每一层中的页都会形成一个双向链表
,如果是以页为单位来分配存储空间的话,双向链表相邻的两个页之间的物理位置可能离得非常远。
我们学习B+树索引的适用场景的时候特别提到范围查询只需要定位到最左边的记录和最右边的记录,然后沿着双向链表一直扫描就可以了。而如果链表中相邻的两个页物理位置离得非常远,就是所谓的随机IO。再一次强调,磁盘的速度和内存的速度差了好几个数量级,随机IO是非常慢的。所以我们应该尽量让链表中相邻的页的物理位置也相邻,这样进行范围查询的时候才可以使用所谓的顺序IO。
引入区的概念,一个区就是在物理位置上连续的64个页
。因为InnoDB中的页大小默认是16KB,所以一个区的大小是64*16KB=1MB
。在表中数据量大的时候,为某个索引分配空间的时候就不再按照页为单位分配了,而是按照区位单位分配。甚至在表中的数据特别多的时候,可以一次性分配多个连续的区。虽然可能造成一点点空间的浪费(数据不足以填充满整个区),但是从性能角度来看,可以消除很多随机IO。
区的分类
区大体上可以分为4种类型:
- 空闲的区(FREE):现在还没有用到这个区中的任何页面
- 有剩余空间的碎片区(FREE_FRAG):表示碎片区中还有可用的页面
- 没有剩余空间的碎片区(FULL_FRAG):表示碎片区中的所有页面都被使用,没有空闲页面。
- 附属于某个段的区(FSEG):每一个索引都可以分为叶子节点段和非叶子节点段
处于FREE、FREE_FRAG以及FULL_FRAG这三种状态的区都是独立的,直属于表空间。而处于FSEG状态的区是附属于某个段的。
【2】为什么要有段
对于范围查询,其实是对B+树叶子节点中的记录进行顺序扫描,而如果不区分叶子节点和非叶子节点,统统把节点代表的页面放到申请到的区中的话,进行范围扫描的效果就大打折扣了。所以InnoDB对B+树的叶子节点和非叶子节点进行了区别对待,也就是说叶子节点有自己独有的区,非叶子节点也有自己独有的区。
存放叶子节点的区的集合就算是一个段(segment),存放非叶子节点的区的集合也算是一个段。也就是说一个索引会生成2个段,一个叶子节点段,一个非叶子节点段。
除了索引的叶子节点段和非叶子节点段之外,InnoDB中还有为存储一些特殊的数据而定义的段,比如回滚段。所以常见的段有数据段、索引段、回滚段
。数据段即为B+树的叶子节点,索引段即为B+树的非叶子节点
。
在InnoDB存储引擎中,对段的管理都是由引起自身完成的,DBA不能也没有必要对其进行控制,这从一定程度上简化了DBA对于段的管理。
段其实不对应表空间中某一个连续的物理区域,而是一个逻辑上的概念,由若干个零散的页面以及一些完整的区组成。
【3】为什么要有碎片区
默认情况下,一个使用InnoDB存储引擎的表只有一个聚簇索引,一个索引会生成2个段。而段是以区位单位申请存储空间的,一个区默认占用1MB(64*16KB=1024KB)存储空间,所以默认情况下一个只存了几条记录的小表也需要2M的存储空间么?以后每次添加一个索引都要多申请2M的存储空间么?
这对于存储记录比较少的表简直是天大的浪费。这个问题的症结在于到现在为止我们介绍的区都是非常纯粹的,也就是一个区被整个分配给某一个段,或者说区中的所有页面都是为了存储同一个段的数据而存在的,即使段的数据填不满区中所有的页面,那余下的页面也不能挪作他用。
为了考虑以完整的区位单位分配给某个段对于数据量小的表太浪费存储空间的这种情况,InnoDB提出了一个碎片(fragment)区的概念。
在一个碎片区中,并不是所有的页都是为了存储同一个段的数据而存在的,而是碎片区中的页可以用于不同的目的,比如有些页用于段A,有些页用于段B,有些页甚至哪个段都不属于。碎片区直属与表空间,并不属于任何一个段。
所以此后为某个段分配存储空间的策略是这样的:
- 在刚开始向表中插入数据的时候,段是从某个碎片区以单个页面为单位来分配存储空间的。
- 当某个段已经占用了
32个碎片区页面
之后,就会申请以完整的区位单位来分配存储空间。
所以现在段不能仅定义为是某些区的集合,更精确的应该是某些零散的页面以及一些完整的区的集合。
【4】表空间
表空间可以看做是InnoDB存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。
表空间是一个逻辑容器,表空间存储的对象是段,在一个表空间中可以有一个或多个段,但是一个段只能属于一个表空间。表空间数据由一个或多个表空间组成,表空间从管理上可以划分为系统表空间(System tablespace)、独立表空间(File-per-table tablespace)、撤销表空间(Undo Tablespace)和临时表空间(Temporary tablespace)等。
① 独立表空间
独立表空间,即每张表有一个独立的表空间,也就是数据和索引信息都会保存在自己的表空间中。独立的表空间(即单表)可以在不同的数据库之间进行迁移。独立表空间是由段、区、页组成的。
空间可以回收(drop table 操作可自动回收表空间,其他情况表空间不能自己回收)。如果对于统计分析或是日志表,删除大量数据后可以通过:alter table tablename engin=innodb;
回收不用的空间。对于使用独立表空间的表,不管怎么删除,表空间的碎片不会太严重的影响性能,而且还有机会处理。
真实表空间对应的文件大小
我们到数据目录里看,会发现一个新建的表对应 .ibd
文件只占用了96K,才6个页面大小(MySQL5.7中)。这是因为一开始表空间占用的空间很小,因为表里边都没有数据。不过这些 .ibd
文件是自扩展的,随着表中数据的增多,表空间对应的文件也逐渐增大。
MySQL8中,一个新建的表对应
.ibd
文件占用了112K,也就是7个页面。因为MySQL8中将 frm 与 ibd 合在了一起为一个 ibd 。
查看InnoDB的表空间类型:
show variables like 'innodb_file_per_table';
如果可以看到 innodb_file_per_table=ON ,这就意味着每张表都会单独保存为一个 .ibd 文件。
② 系统表空间
系统表空间的结构和独立表空间基本类似,只不过由于整个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_INDEXES 、SYS_FIELDS这四个表尤其重要,称之为基本系统表(basic system tables)
。
SYS_TABLES表结构
列名 | 描述 |
---|---|
NAME | 表的名称。主键 |
ID | InnoDB存储引擎中每个表都有一个唯一的ID,二级索引 |
N_COLS | 该表拥有列的个数 |
TYPE | 表的类型,记录了一些文件格式、行格式、压缩等信息 |
MIX_ID | 已过时,忽略 |
MIX_LEN | 表的一些额外的属性 |
CLUSTER_ID | 未使用,忽略 |
SPACE | 该表所属表空间ID |
SYS_COLUMNS表结构
列名 | 描述 |
---|---|
TABLE_ID | 该列所属表对应的ID(与POS一起构成联合主键) |
POS | 该列在表中是第几列 |
NAME | 该列的名称 |
MTYPE | main data type,主数据类型,比如int char varchar等等 |
PRTYPE | precise type,精确数据类型,就是修饰主数据类型的,比如是否允许null值 |
LEN | 该列最多占用存储空间的字节数 |
PREC | 该列的精度,不过这列貌似都没有使用,默认值都是 0 |
SYS_INDEXES表结构
列名 | 描述 |
---|---|
TABLE_ID | 该索引所属表对应的ID(与ID一起构成联合主键) |
ID | InnoDB存储引擎中每个索引都有一个唯一的ID |
NAME | 该索引的名称 |
N_FIELDS | 该索引包含列的个数 |
TYPE | 该索引的类型,比如聚簇索引、唯一索引、更改缓冲区的索引、全文索引、普通的二级索引等各种类型 |
SPACE | 该索引根页面所在的表空间ID |
PAGE_NO | 该索引根页面所在的页面号 |
MERGE_THRESHOLD | 如果页面中的记录被删除到某个比例,就把该页面和相邻页面合并,这个值就是这个比例 |
SYS_FIELDS表结构
列名 | 描述 |
---|---|
INDEX_ID | 该索引列所属的索引ID(与POS一起构成联合主键) |
POS | 该索引列在某个索引中是第几列 |
COL_NAME | 该索引列的名称 |
注意用户是不能直接访问InnoDB的这些内部系统表,除非你直接去解析系统表空间对应文件系统上的文件。不过考虑到查看这些表的内容可能有助于大家分析问题,所以在系统数据库information_schema中提供了一些以 innodb_sys
开头的表。
use information_schema;
show tables like '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
在information_schema 数据库中的这些以INNODB_SYS开头的表并不是真正的内部系统表(内部系统报就是我们上边以SYS开头的那些表),而是在存储引擎启动时读取这些以SYS开头的系统表,然后填充到这些以INNODB_SYS开头的表中。以INNODB_SYS开头的表和以SYS开头的系统表中的字段并不完全一样,但供大家参考已经足矣。
【5】数据页加载的三种方式
InnoDB从磁盘读取数据的最小单位是数据页,而你想得到的id=xxx 的数据,就是这个数据页众多行中的一行。
对于MySQL存放的数据,逻辑概念上我们称之为表,在磁盘等物理层面而言是按数据页形式进行存放的,当其加载到MySQL中我们称之为缓存页。
如果缓冲池中没有该页数据,那么缓冲池有以下三种读取数据的方式,每种方式的读取效率都是不同的。
① 内存读取
如果该数据存在于内存中,基本上执行的时间在1ms ,效率还是很高的。
② 随机读取
如果数据没有在内存中,就需要在磁盘上对该页进行查找,整体时间预估在10ms 左右。这10ms中有 6ms 是磁盘的实际繁忙时间(包括了寻道和半圈旋转时间),有3ms是对可能发生的排队时间的估计值,另外还有1ms的传输时间,将页从磁盘服务器缓冲区传输到数据库缓冲区中。这10ms看起来很快,但实际上对于数据库来说消耗的时间已经非常长了,因为这还只是一个页的读取时间。
③ 顺序读取
顺序读取其实是一种批量读取的方式,因为我们请求的数据在磁盘上往往都是相邻存储的,顺序读取可以帮我们批量读取页面。这样的话,一次性加载到缓冲池中就不需要再对其他页面单独进行磁盘IO操作了。
如果一个磁盘的吞吐量是40MB/S,那么对于一个16KB大小的页来说,一次可以顺序读取2560(40mb/16kb)个页,相当于一个页的读取时间为0.4ms。采用批量读取的方式,即使是从磁盘上进行读取,效率也比从内存中只单独读取一个页的效率要高。
认真学习InnoDB的数据存储结构中的区、段与表空间相关推荐
- Tableau学习Step2一数据文件的读取与统计图、表的概述
Tableau学习Step2一数据文件的读取与统计图.表的概述 本文首发于博客冰山一树Sankey,去博客浏览效果更好. 一. 前言 本教程通过一个案例从浅到深来学习Tableau知识 案例概述: 二 ...
- c语言 浮点型数据怎么存放,C语言学习之浮点型数据存储
C语言学习之浮点型数据 浮点数 浮点型数据分为单精度浮点型(float)和双精度浮点型(double). 单精度(float) 单精度浮点值 取值范围:1.2E-38 到 3.4E+38 精度:6 位 ...
- Android学习笔记---android数据存储与访问
数据存储与访问 --------------------------------------- 一个在手机和sd卡上存储文件的例子 1.a.文件名称:lable b.一个text框 c.文件内 ...
- 【计组原理学习小结】数据存储与传输的定义及单位问题(数据传输率与带宽;MB与Mbps;字、字节、字长、位的区别与联系)
数据传输率.带宽.吞吐量的关系 数据传输率和带宽在数值上相同 数值含义:每秒种传输构成数据代码的二进制比特数 单位:比特/秒(b/s),记作bps 吞吐量:单位时间内CPU从存储设备读取->处理 ...
- Android学习羁绊之数据存储
数据持久化就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不会丢失.保存在内存中的数据是处于瞬时状态的,而保存在存储设备中的数据是处于持久状态的,持久化技术 ...
- oracle导入备份失败怎么办,ORACLE 数据备份、恢复以及导入时表空间不存在的解决方案...
一.数据备份(导出) 1.exp命令导出dmp文件(exp -help查看帮助信息) 命令:exp username/userpasswd@orcl file=G:/test.dmp log=G:/t ...
- mysql innodb引擎数据存储方式和索引的概念
索引 主键索引 二级索引(非主键的索引) mysql两个引擎的区别在上面的图中体现出来了. B+tree索引 下面的图是从网上找的,来源已经不可知了.通过下图可以简单理解数据查找的过程. 还有一个点就 ...
- mysql数据存储到指定位置_Mysql数据库表分区存储到指定磁盘路径
. 前提: mysql5.6.6以上的版本以上的版本才支持单表指定目录,且目录权限是MySQL:mysql. 在mysql中数据文件存放于在my.cnf中datadir指定的路径,使用的表引擎不同产 ...
- 【mysql】-【innodb数据存储结构】
文章目录 数据库的存储结构:页 磁盘与内存交互基本单位:页 页结构概述 页的大小 页的上层结构 页的内部结构 File Header(文件头部)和File Trailer(文件尾部) File Hea ...
最新文章
- MPB:北大口腔陈峰、陈智滨等-​口腔微生物组研究主要取样部位及方法
- 实战DeviceIoControl 之中的一个:通过API訪问设备驱动程序
- 深入理解幂等性及Restful风格API的幂等性问题详解
- 【CF 应用开发大赛】JEECG 基于代码生成器J2EE智能开发框架
- 再严重的感冒,马上就好!!
- Codeforces 892E Envy
- 转: ant condition使用
- Docker 环境:Nexus3.x 的私有仓库
- zookeeper安装和使用 windows环境
- cocos2d-x的定时器
- 正则表达式的一些符号基础和实例解析
- Delphi 7 定义你自己的事件
- 在mysql中存储生日,php中计算今天是否为用户生日
- printf()、sprintf()的二次封装
- 苗族php动态网页设计作业
- 播布客LINUX视频笔记
- vue脚手架下载及使用
- python使用opencv如何保存图片_OpenCV Python 保存图片
- TimescaleDB的结构初识
- mysql mmm优缺点_mysql之MMM总结