InnoDB数据存储结构

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

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

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

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

InnoDB将数据划分为若干个页,InnoDB中页的大小默认为16KB。

以 页 作为磁盘和内存之间的交互的 基本单位,也就是一次最少从磁盘中读取16KB的数据到内存中,一次最少把内存中16KB的内容刷新到磁盘中。也就是说,在数据库中,不论读一行,还是都多行,都是将这些行所在的页进行加载。也就是说,数据库管理存储空间的基本单位是页,数据库IO操作的最小单位是页。一个页中可以存储多个行记录。

记录是按照行存储的,但是数据库的读取并不以行为单位,否则一次读取只能处理一行数据,效率会非常低。

1.2 页结构描述

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

1.3 页的大小

不同的数据库管理系统的页大小不同。比如在mysql中InnoDB存储引擎中,默认页的大小是16KB,我们可以通过下面的命令来进行查看,大概16KB大小:

show variables like '%innodb_page_size%';

1.4 页的上层结构

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

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

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

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

2. 页的内部结构

页如果按照类型划分的话,常见的有 数据页,系统页,Undo页 和 事务数据页 等。数据页是我们最常使用的页。

数据页的16KB大小的存储空间被划分为七部分,分别是文件头,页头,最大最小记录,用户记录,空间空间,页目录和文件尾。

页结构的示意图如下所示:

 这7个部分作用分别如下,我们简单梳理如下表所示:

名称占用大小说明
File Header38字节文件头,描述页的信息
Page Header56字节页头,页的状态信息
Infimum+Supermum26字节最大和最小记录,这是两个虚拟的行记录
User Records不确定用户记录,存储行记录内容
Free Space不确定空闲记录,页中还没有被使用的空间
Page Directory不确定页记录,存储用户记录的相对位置
File Trailer8字节文件尾,校验页是否完整

 2.3 从数据页的角度看B+树如何查询

一棵B+树按照节点类型可以分成两个部分:

1. 叶子节点,B+树最底层的节点,节点的高度为0,存储行记录。

2. 非叶子节点,节点的高度大于0,存储索引键和页面指针,并不存储行记录本身。

 当我们从也结构来理解B+树的结构时候,可以帮我们理解一些通过索引进行检索的原理。

1. B+树时如何进行记录检索的?

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

2. 普通索引和唯一索引再查询效率上有什么不同?

我们创建索引的时候可以时普通索引,也可以是唯一索引,那么这两个索引在查询效率上有什么不同呢?

唯一索引就是在普通索引上增加了约束性,也就是关键字唯一,找到了关键字就停止检索了。而普通索引,肯能会存在用户记录中的关键字相同的情况,根据也结构的原理,当我们读取一条记录的时候,不是单独将这条记录从磁盘中读出去,而是将这个记录所在的也加载到页内存中进行读取。InnoDB存储引擎的页大小为16KB,在一个页中可能存储着上千个记录,因此在普通索引的字段上进行查找也就是在内存中多几次判断下一条记录的操作,对于CPU来说,这些操作所消耗的时间是可以忽略不计的。所以对一个索引字段进行检索,采用普通索引还是唯一索引在检索效率上基本没什么区别。

4. 区,段与碎片区

4.1 为什么要有区?

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

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

4.2 为什么要有段?

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

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

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

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

4.3 为什么要有碎片区?

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

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

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

        ~ 在刚开始向表中插入数据的时候,段是从某个碎片区以单个页面为单位来分配的存储空间的。

        ~ 当某个段已经占用了32个碎片区页面之后,就会申请以完整的区为单位来分配存储空间。

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

4.4 区分的类

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

        ~ 空闲的区:现在还没有用这个区中的任何页面。

        ~ 有剩余空间的碎片区:表示碎片区中还有可用的页面。

        ~ 没有剩余空间的碎片区:表示碎片区中的所有页面都被使用,没有空闲页面。

        ~ 附属于某个区:每一个索引都可以分页叶子节点节点段和非叶子节点段。

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

5.表空间

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

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

5.1 独立表空间

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

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

 

独立表空间结构

独立表空间由 段,区,页组成。前面已经讲解过了。

真实表空间对应的文件大小

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

查看InnoDB的表空间类型:

show variables like 'innodb_file_per_table';

你能看到 innodb_file_per_table=ON, 这就意味着每张表都会单独保存为一个 .ibd 文件。

5.2 独立表空间

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

InnoDB数据字典

每当我们像一个表中插入一条记录的时候,MySQL校验过程如下

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

~ 某个表属于哪个空间,表里边有多少列

~ 表对应的每一个列的类型是什么

~ 该表有多少索引,每个索引对应那几个字段,该索引对应的根页面在那个表空间的哪个页面

~ 该表有哪些外键,外键对应哪个表的哪些列

~ 某个表空间对应文件系统上文件路径是什么


上述这些数据并不是我们使用 INSERT 语句插入的用户数据,实际上是为了更好的管理我们这些用户数据而不得已引入的一些额外数据,这些数据也成为 元数据。InnoDB存储引擎特意定义了一些列的 内部系统表 来记录这些元数据。

附录:数据页加载的三种方式


InnoDB 从磁盘中读取数据的 最小单位 是数据页。而你想得到的id=xxx的数据,就是这个数据页中众多行的一行。对于MySQL存放的数据,逻辑概念上我们称之为表,在磁盘等物理层面而言是按照 数据页 形式进行存放的,当其加载到 mysql中我们称之为 缓存页

1. 内存读取

如果该数据存在于内存中,基本上执行时间在1ms左右,效率还是很高的

2. 随机读取

如果数据没有存在内存中,就需要在磁盘上对该页进行查找,整体时间预估在10ms左右,着10ms中有6ms是磁盘的实际繁忙时间(包括 寻道和半圈旋转时间), 有3ms是对可能发生的排队时间估计值,另外还有1ms的传输时间 ,将页从磁盘服务器缓冲区传输到数据库缓冲区中。这10ms看起来很快,但实际上对于数据库来说消耗的时间已经非常久了,因为这还只是一页的读取时间。

 3. 顺序读取

顺序读取其实是一种批量读取的方式,因为我们请求的 数据在磁盘上往往都是相邻存储的,顺序读取可以帮我们批量读取页面,这样的话,一次性加载到缓冲池中就不需要在对其他页面单独进行磁盘IO操作了。如果一个磁盘的吞吐量是 40MB/S,那么对于一个16KB大小的页来说,一次可以顺序读取 2560(40MB/16KB)个页,相当于一个页的读取时间为0.4MS。采用批量读取的方式,即使是从磁盘上进行读取,效率也比从内存中只单独读取一个页的效率要高。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值