cms建站系统 下载,精准广告投放,汕头市平台网络推广公叿,wordpress 导航图片尺寸1. 数据库的存储结构#xff1a;页
索引结构给我们提供了高效的索引方式#xff0c;不过索引信息以及数据记录都是保存在文件上的#xff0c;确切说是存储在页结构中。另一方面#xff0c;索引是在存储引擎中实现的#xff0c;MySQL服务器上的存储引擎负责对表中数据的读…
1. 数据库的存储结构页
索引结构给我们提供了高效的索引方式不过索引信息以及数据记录都是保存在文件上的确切说是存储在页结构中。另一方面索引是在存储引擎中实现的MySQL服务器上的存储引擎负责对表中数据的读取和写入工作。
不同的存储引擎中存放的格式一般是不同的甚至有的存储引擎比如Memory都不用磁盘来存储数据。
由于InnoDB是MySQL的默认存储引擎索引本章主要介绍InnoDB存储引擎的数据存储结构。
1.1 磁盘与内存交互基本单位页 1.2 页结构概述
页a、页b、页c . . .页n 这些页可以不在物流结构上相连只要通过双向链表相关联即可。每个数据页中的记录会按照主键值从小到大的顺序组成一个单向链表每个数据页都会为存储在它里面的记录生成一个页目录在通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽然后再遍历该槽对应分组中的记录即可快速找到指定的记录。
1.3 页的大小
不同的数据库管理系统简称DBMS的页大小不同。比如在 MySQL 的 InnoDB 存储引擎中默认页的大小是 16KB我们可以通过下面的命令来进行查看
show variables like %innodb_page_size%SQL Server 中页的大小为 8KB而在 Oracle 中我们用术语 “块” Block来表示 “页”Oracle 支持的快大小为2KB, 4KB, 8KB, 16KB, 32KB 和 64KB。
1.4 页的上层结构
另外在数据库中还存在着区Extent、段Segment和表空间Tablespace的概念。行、页、区、段、表空间的关系如下图所示 区Extent是比页大一级的存储结构在InnoDB存储引擎中一个区会分配64 个连续的页。因为InnoDB中的页大小默认是16KB所以一个区的大小是 64 * 16KB 1MB 段Segment由一个或多个 区 组成区在文件系统是一个连续分配的空间在InnoDB中是连续的 64 个页不过在段中不要求区与区之间是相邻的。段是数据库中的分配单位不同类型的数据库对象以不同的段形式存在。当我们创建数据表、索引的时候就会相应创建对应的段比如创建一张表时会创建一个表段创建一个索引时会创建一个索引段。 表空间Tablespace是一个逻辑容器表空间存储的对象是段在一个表空间中可以有一个或多个段但是一个段只能属于一个表空间。数据库由一个或多个表空间组成表空间从管理上可以划分为系统表空间、用户表空间、撤销表空间、临时表空间等。
2. 页的内部结构
页如果按类型划分的话常见的有 数据页保存B树节点、系统表、Undo 页 和 事务数据页 等。数据页是我们最常使用的页。
数据页的 16KB 大小的存储空间被划分为七个部分分别是文件头File Header、页头Page Header、最大最小记录Infimum supremum、用户记录User Records、空闲空间Free Space、页目录Page Directory和文件尾File Tailer。
页结构的示意图如下所示 如下表所示 我们可以把这7个结构分为3个部分。
第一部分File Header (文件头部) 和 File Trailer (文件尾部) 见文件InnoDB数据库存储结构.mmap 第二部分User Records (用户记录)、最大最小记录、Free Space (空闲空间) 见文件InnoDB数据库存储结构.mmap 第三部分Page Directory (页目录) 和 Page Header (页面头部) 见文件InnoDB数据库存储结构.mmap 2.3 从数据库页的角度看B树如何查询
一颗B树按照字节类型可以分为两部分
叶子节点B 树最底层的节点节点的高度为0存储行记录。非叶子节点节点的高度大于0存储索引键和页面指针并不存储行记录本身。 当我们从页结构来理解 B 树的结构的时候可以帮我们理解一些通过索引进行检索的原理 3. InnoDB行格式 (或记录格式) 见文件InnoDB数据库存储结构.mmap 4. 区、段与碎片区
4.1 为什么要有区
B树的每一层中的页都会形成一个双向链表如果是以页为单位来分配存储空间的话双向链表相邻的两个页之间的物流位置可能离得非常远。介绍B树索引的适用场景的时候特别提到范围查询只需要定位到最左边的记录和最右边的记录然后沿着双向链表一直扫描就可以了而如果链表中相邻的两个页物理位置离得非常远就是所谓的随机I/O。再一次强调磁盘的速度和内存的速度差了好几个数量级随机I/O是非常慢的所以我们应该尽量让链表中相邻的页的物流位置也相邻这样进行范围查询的时候才可以使用所谓的顺序 I/O。
引入区的概念一个区就是在物理位置上连续的64个页。因为 InnoDB 中的页大小默认是 16KB所以一个区的大小是 64 * 16KB 1MB。在表中数据最大的时候为某个索引分配空间的时候就不再按照页为分单位分配了而是按照区位单位分配甚至在表中的数据特别多的时候可以一次性分配多个连续的区。虽然可能造成一点点空间的浪费数据不足以填充满整个区但是从性能角度看可以消除很多的随机I/O功大于过
4.2 为什么要有段
对于范围查询其实是对B树叶子节点中的记录进行顺序扫描而如果不区分叶子节点和非叶子节点统统把节点代表的页面放到申请到的区中的话进行范围扫描的效果就大打折扣了。所以InnoDB对B树的叶子节点 和 非叶子节点 进行了区别对待也就是说叶子节点有自己独有的区非叶子节点也有自己独有的额区。存放叶子节点的区的集合就算是一个 段Segment存放非叶子节点的区的集合也算是一个段。也就是说一个索引会生成2个段一个叶子节点段一个非叶子节点段。
除了索引的叶子节点段和非叶子节点段之外InnoDB中还有为存储一些特殊的数据而定义的段比如回滚段所以常见的段有数据段、索引段、回滚段。数据段即为B树的叶子节点索引段即为B树的非叶子节点
在InnoDB存储引擎中对段的管理都是由引擎自身所完成DBA不能也没有必要对其进行控制。这从一定程度上简化了DBA对于段的管理。
段其实不对应表空间中某一个连续的物理区域而是一个逻辑上的概念由若干个零散的页面以及一些完整的区组成。
4.3 为什么要有碎片区
默认情况下一个使用InnoDB存储引擎的表只有一个聚簇索引一个索引会生成2个段而段是以区为单位申请存储空间的一个区默认占用1M64 * 16KB 1024KB存储空间所以默认情况下一个只存了几条记录的小表也需要2M的存储空间以后每次添加一个索引都要多申请2M的存储空间。这对于存储记录比较少的表太过于浪费。这个问题的症结在于到现在为止介绍的区都是非常纯粹的也就是一个区被整个分配给某个段或者说区中的所有页都是为了存储同一个段的数据而存在的即使段的数据填不满区中所有的页面那剩余的页也不能挪作他用。
为了考虑以完整的区为单位分配给某个段对于数据量较小的表太浪费存储空间的这种情况InnoDB提出了一个碎片Fragment区的概念。在一个碎片区中并不是所有的页都是为了存储同一个段的数据而存在的而是碎片区中的页可以用于不同的目的比如有些页用于段A有些页用于段B有些页甚至哪个段都不属于。碎片区直属于表空间并不属于任何一个段。
所以以后为某个段分配存储空间的策略如下 在刚开始想表中插入数据的时候段是从某个碎片区以单个页面为单位来分配存储空间的。 当某个段已经占用了32个碎片区页面之后就会申请以完整的区为单位来分配存储空间。
现在段不能仅定义为是某些区的集合更精确的应该是 某些零散的页面以及一些完整的区的集合。
4.4 区的分类
区大体上可以分为4种类型
空闲的区 (FREE) : 现在还没有用到这个区中的任何页面。有剩余空间的碎片区 (FREE_FRAG)表示碎片区中还有可用的页面。没有剩余空间的碎片区 (FULL_FRAG)表示碎片区中的所有页面都被使用没有空闲页面。附属于某个段的区 (FSEG)每一个索引都可以分为叶子节点段和非叶子节点段。
处于FREE、FREE_FRAG 以及 FULL_FRAG 这三种状态的区都是独立的直属于表空间。而处于 FSEG 状态的区是附属于某个段的。 如果把表空间比作是一个集团军段就相当于师区就相当于团。一般的团都是隶属于某个师的就像是处于 FSEG 的区全部隶属于某个段而处于 FREE、FREE_FRAG 以及 FULL_FRAG 这三种状态的区却直接隶属于表空间就像独立团直接听命于军部一样。 5. 表空间
表空间可以看做是InnoDB存储引擎逻辑架构的最高层所有的数据都存放在表空间中。表空间是一个逻辑容器表空间存储的对象是段在一个表空间中可以有一个或多个段但是一个段只能属于一个表空间。表空间数据库由一个或多个表空间组成,表空间从管理上可以划分为系统表空间System Tablespace、独立表空间File-Per-Table Tablespace、撤销表空间Undo Tablespace和临时表空间Temporary Tablespace
5.1 独立表空间
独立表空间即每张表有一个独立的表空间也就是数据和索引信息都会保存在自己的表空间中。独立的表空间 (即单表) 可以在不同的数据库之间进行 迁移。
空间可以回收 (DROP TABLE 操作可自动回收表空间其他情况表空间不能自己回收) 。如果对于统计分析或是日志表删除大量数据后可以通过alter table TableName engineinnodb; 回收不用的空间。对于使用独立表空间的表不管怎么删除表空间的碎片不会太严重的影响性能而且还有机会处理。
独立表空间结构
独立表空间由段、区、页组成。
真实表空间对应的文件大小
我们到数据目录里看会发现一个新建的表对应的 .ibd 文件只占用了 96K才6个页面大小 (MySQL5.7中)这是因为一开始表空间占用的空间很小因为表里边都没有数据。不过别忘了这些 .ibd 文件是自扩展的随着表中数据的增多表空间对应的文件也逐渐增大。
查看 InnoDB 的表空间类型
show variables like innodb_file_per_table你能看到 innodb_file_per_tableON, 这就意味着每张表都会单词保存一个 .ibd 文件。
5.2 系统表空间
系统表空间的结构和独立表空间基本类似只不过由于整个MySQL进程只有一个系统表空间在系统表空间中会额外记录一些有关整个系统信息的页面这部分是独立表空间中没有的。
InnoDB数据字典
每当我们向一个表中插入一条记录的时候MySQL校验过程如下先要校验一下插入语句对应的表存不存在插入的列和表中的列是否符合如果语法没有问题的话还需要直到该表的聚簇索引和所有二级索引对应的根页面是哪个页面然后把记录插入对应索引的B树中。所以说MySQL除了保存着我们插入的用户数据之外还需要保存许多额外的信息比方说 某个表属于哪个表空间表里边有多少列。 表对应的每一个列的类型是什么。 该表有多少索引每个索引对应哪几个字段该索引对应的根页在哪个表空间的哪个页。 该表有哪些外键外键对应哪个表的哪些列。 某个表空间对应文件系统上文件路径是什么。
删除这些数据并不是我们使用 INSERT 语句插入的用户数据实际上是为了更好的管理我们这些用户数据而不得以引入的一些额外数据这些数据页称为 元数据。InnoDB 存储引擎特意定义了一些列的 内部系统表 (internal system table) 来记录这些元数据 这些系统表也称为 数据字典它们都是以 B 树的形式保存在系统表空间的某个页面中。其中 SYS_TABLES、SYS_COLUMNS、SYS_INDEXES、SYS_FIELDS 这四个表尤其重要称之为基本系统表 (basic system tables) 我们先看看这4个表的结构 注意用户不能直接访问 InnoDB 的这些内部系统表除非你直接去解析系统表空间对应文件系统上的文件。不过考虑到查看这些表的内容可能有助于大家分析问题所以在系统数据库 information_schema 中提供了一些以 innodb_sys 开头的表:
USE information_schema;SHOW TABLES LIKE innodb_sys%;在 information_scheme 数据库中的这些以 INNODB_SYS 开头的表并不是真正的内部系统表 (内部系统表就是我们上边以 SYS 开头的那些表)而是在存储引擎启动时读取这些以 SYS 开头的系统表然后填充到这些以 INNODB_SYS 开头的表中。以 INNODB_SYS 开头的表和以 SYS 开头的表中的字段并不完全一样但仅供大家参考已经足矣。
附录数据页加载的三种方式
InnoDB从磁盘中读取数据 最小单位 是数据页。而你想得到的 id xxx 的数据就是这个数据页众多行中的一行。
对于MySQL存放的数据逻辑概念上我们称之为表在磁盘等物理层面而言是按 数据页 形式进行存放的当其加载到 MySQL 中我们称之为 缓存页。
如果缓冲池没有该页数据那么缓冲池有以下三种读取数据的方式每种方式的读取速率是不同的
1. 内存读取
如果该数据存在于内存中基本上执行时间在 1ms 左右效率还是很高的。 2. 随机读取
如果数据没有在内存中就需要在磁盘上对该页进行查询整体时间语句在 10ms 左右这 10ms 中有 6ms是磁盘的实际繁忙时间包括了寻道和半圆旋转时间有 3ms 是对可能发生的排队事件的估计另外还有 1ms 的传输时间将页从磁盘服务器缓冲区传输到数据库缓冲区中。这 10ms 看起来很快但实际上对于数据库来说消耗的时间已经非常长了因为这还是一个页的读取时间。 3. 顺序读取
顺序读取其实是一种批量读取的方式因为我们请求的数据在磁盘上往往都是相邻存储的顺序读取可以帮助我们批量读取页面这样的话一次性加载到缓冲池中就不需要再对其他页单独进行磁盘I/O操作了。如果一个磁盘的吞吐量是 40MB/S那么对于一个 16KB大小的页来说一次可以顺序读取 256040MB/16KB个页相当于一个页的读取时间为 0.4ms。采用批量读取的方式即使是从磁盘上进行读取效率也比从内存中只单独读取一个页的效率要高。