【MySQL技术内幕】4.2-InnoDB逻辑存储结构

简介: 【MySQL技术内幕】4.2-InnoDB逻辑存储结构

从 InnoDB存储引擎的逻辑存储结构看,所有数据都被逻辑地存放在一个空间中,称之为表空间(tablespace)。表空间又由段(segment)、区(extent)、页(page)组成。页在一些文档中有时也称为块(block), InnoDB存储引擎的逻辑存储结构大致如图所示。

image.png

1、表空间

表空间可以看做是InnoDB存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。在默认情况下 InnoDB存储引擎有一个共享表空间 ibdata1,即所有数据都存放在这个表空间内。如果用户启用了参数 innodb_file_per_table,则每张表内的数据可以单独放到一个表空间内。

如果启用了 innodb_file_per_table的参数,需要注意的是每张表的表空间内存放的只是数据、索引和插入缓冲 Bitmap页,其他类的数据,如回滚(undo)信息,插入缓冲索引页、系统事务信息,二次写缓冲(double write buffer)等还是存放在原来的共享表空间内。这同时也说明了另一个问题:即使在启用了参数 innodb_file_per_table之后,共享表空间还是会不断地增加其大小。可以来做一个实验,在实验之前已经将 innodb_file_per_table设为ON了。现在看看初始共享表空间文件的大小:

image.png

可以看到,共享表空间 ibdata1的大小为58MB,接着模拟产生undo的操作,利用生成的表 mytest,并把其存储引擎更改为 InnoDB,执行如下操作: image.png

这里首先将自动提交设为0,即用户需要显式提交事务(注意,在上面操作结束时,并没有对该事务执行 commit或 rollback)。接着执行会产生大量undo操作的语句 update mytest set salary=0,完成后再观察共享表空间,会发现 ibdata1已经增长到了114MB。这个例子虽然简单,但是足以说明共享表空间中还包含有undo信息有用户会问,如果对这个事务执行 rollback, ibdata1这个表空间会不会缩减至原来的大小(58MB)?这可以通过继续运行下面的语句得到验证:

image.png

很“可惜”,共享表空间的大小还是114MB,即 InnoDB存储引擎不会在执行 rollback时去收缩这个表空间。虽然 InnoDB不会回收这些空间,但是会自动判断这些undo信息是否还需要,如果不需要,则会将这些空间标记为可用空间,供下次undo使用。

回想一下,在master thread每10秒会执行一次的 full purge操作,很有可能的一种情况是:用户再次执行上述的 UPDATE语句后,会发现 ibdata1不会再增大了,那就是这个原因了。

py_innodb_page_info小工具(github.com/jameslcj/da…),用来查看表空间中各页的类型和信息。使用方法如下: image.png

可以看到共有83584个页,其中插人缓冲的空闲列表有204个页、5467个可用页、38675个undo页、39233个数据页等。用户可以通过添加v参数来查看更详细的内容。由于该工具还在开发之中,因此并不保证在本书出版时此工具最终显示结果的变化。

2、段

表空间是由各个段组成的,常见的段有数据段、索引段、回滚段等。

因为前面已经介绍过了 InnoDB存储引擎表是索引组织的( index organized),因此数据即索引,索引即数据。那么数据段即为B+树的叶子节点(的 Leaf node segment),索引段即为B+树的非索引节点(图的 Non-leaf node segment)。回滚段较为特殊,将会在后面的章节进行单独的介绍。

在 InnodB存储引擎中,对段的管理都是由引擎自身所完成,DBA不能也没有必要对其进行控制。这和 Oracle数据库中的自动段空间管理(ASSM)类似,从一定程度上简化了DBA对于段的管理。

3、区

区是由连续页组成的空间,在任何情况下每个区的大小都为1MB。为了保证区中页的连续性, InnoDB存储引擎一次从磁盘申请4~5个区。在默认情况下, InnoDB存储引擎页的大小为16KB,即一个区中一共有64个连续的页。

InnoDB 1.0.x版本开始引人压缩页,即每个页的大小可以通过参数 KEY_BLOCK_SZE设置为2K、4K、8K,因此每个区对应页的数量就应该为512、256、128。

InnodB 1.2.x版本新增了参数 innodb_page_size,通过该参数可以将默认页的大小设置为4K、8K,但是页中的数据不是压缩。这时区中页的数量同样也为256、128。总之,不论页的大小怎么变化,区的大小总是为1M。

但是,这里还有这样一个问题:在用户启用了参数 innodb_file_per_talbe后,创建的表默认大小是96KB。区中是64个连续的页,创建的表的大小至少是1MB才对啊?其实这是因为在每个段开始时,先用32个页大小的碎片页( fragment page)来存放数据,在使用完这些页之后才是64个连续页的申请。这样做的目的是,对于一些小表,或者是undo这类的段,可以在开始时申请较少的空间,节省磁盘容量的开销。这里可以通过一个很小的示例来显示 InnoDB存储引擎对于区的申请方式:

mysql> create table t1(
    -> col1 int not null auto_increment,
    -> col2 varchar(7000),
    -> primary key(col1)) engine=InnoDB;
Query OK, 0 rows affected (0.03 sec)
 
mysql> system sudo ls -lh /usr/local/mysql/data/test_mybatis/t1.ibd
-rw-r-----  1 _mysql  _mysql    96K 10 11 18:04 /usr/local/mysql/data/test_mybatis/t1.ibd

上述的SQL语句创建了t表,将col2字段设为 VARCHAR(7000),这样能保证个页最多可以存放2条记录。通过ls命令可以发现,初始化并创建tl表后,表空间默认大小为96KB,接着运行如下SQL语句:

mysql> insert into t1 select null,repeat('a',7000);
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0
mysql> insert into t1 select null,repeat('a',7000);
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0
mysql> system sudo ls -lh /usr/local/mysql/data/test_mybatis/t1.ibd
-rw-r-----  1 _mysql  _mysql    96K 10 11 18:18 /usr/local/mysql/data/test_mybatis/t1.ibd

插入两条记录,根据之前对表的定义,这两条记录应该位于同一个页中。如果这时通过 py_innodb_page_info工具来查看表空间,可以看到:

 ./py_innodb_page_info.py -v /usr/local/mysql/data/test_mybatis/t1.ibd

image.png

这次用-v详细模式来看表空间的内容,注意到了 page offset为3的页,这个就是数据页。 page leve表示所在索引层,0表示叶子节点。因为当前所有记录都在一个页中,因此没有非叶节点。但是如果这时用户再插入一条记录,就会产生一个非叶节点。 image.png

现在可以看到 page offset为3的页的 page level由之前的0变为了1,这时虽然新插入的记录导致了B+树的分裂操作,但这个页的类型还是B- tree Node接着继续上述同样的操作,再插入60条记录,也就是说当前表t中共有63条记录,32个页。

可以看到,在导入了63条数据后,表空间的大小还是小于1MB,即表示数据空间的申请还是通过碎片页,而不是通过64个连续页的区。

可以观察到B- ree Node页一共有33个,除去一个 page level为1的非叶节点页,共有32个 page level为0的页,也就是说,对于数据段,已经有32个碎片页了。之后用户再申请空间,则表空间按连续64个页的大小开始增长了。好了,接着就这样来操作,插入一条数据,看之后表空间的大小:

因为已经用完了32个碎片页,新的页会采用区的方式进行空间的申请,如果此时用户再通过 py_ innodb page info工具来看表空间文件tbd,应该可以看到很多类型为Freshly Allocated Page的页。

4、页

同大多数数据库一样, InnoDB有页(Page)的概念(也可以称为块),页是 InnoDB磁盘管理的最小单位。在 InnoDB存储引擎中,默认每个页的大小为16KB。而从InnoDB1.2.x版本开始,可以通过参数 innodb_page_size将页的大小设置为4K、8K、16K。若设置完成,则所有表中页的大小都为 innodb_page_size,不可以对其再次进行修改。除非通过 mysqldump导人和导出操作来产生新的库。

在 InnodB存储引擎中,常见的页类型有

  • 数据页( B-tree Node)
  • undo页( undo Log Page)
  • 系统页( System Page)
  • 事务数据页( Transaction system Page)
  • 插入缓冲位图页( Insert Buffer Bitmap)
  • 插入缓冲空闲列表页( Insert Buffer Free List
  • 未压缩的二进制大对象页( Uncompressed BLOB Page)
  • 压缩的二进制大对象页( compressed BLOB Page)

5、行

InnoDB存储引擎是面向列的(row- oriented),也就说数据是按行进行存放的。每个页存放的行记录也是有硬性定义的,最多允许存放16KB/2-200行的记录,即7992行记录。这里提到了 row-oriented的数据库,也就是说,存在有 column- oriented的数据库。

MySQL infobright存储引擎就是按列来存放数据的,这对于数据仓库下的分析类SQL语句的执行及数据压缩非常有帮助


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
缓存 关系型数据库 MySQL
在MySQL中处理高并发和负载峰值的关键技术与策略
采用上述策略和技术时,每个环节都要进行细致的规划和测试,确保数据库系统既能满足高并发的要求,又要保持足够的灵活性来应对各种突发的流量峰值。实施时,合理评估和测试改动对系统性能的影响,避免单一措施可能引起的连锁反应。持续的系统监控和分析将对维护系统稳定性和进行未来规划提供重要信息。
583 15
|
监控 关系型数据库 MySQL
10亿数据如何最快速插入MySQL:技术干货分享
【8月更文挑战第2天】在大数据时代,处理并快速插入数十亿条数据到MySQL数据库是许多企业面临的关键挑战。本文将深入分享一系列高效的技术策略和实战经验,帮助读者优化这一过程,确保数据能够快速、准确地进入数据库系统。
735 1
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
729 1
|
监控 前端开发 Java
【技术开发】接口管理平台要用什么技术栈?推荐:Java+Vue3+Docker+MySQL
该文档介绍了基于Java后端和Vue3前端构建的管理系统的技术栈及功能模块,涵盖管理后台的访问、登录、首页概览、API接口管理、接口权限设置、接口监控、计费管理、账号管理、应用管理、数据库配置、站点配置及管理员个人设置等内容,并提供了访问地址及操作指南。
|
监控 关系型数据库 MySQL
MySQL自增ID耗尽应对策略:技术解决方案全解析
在数据库管理中,MySQL的自增ID(AUTO_INCREMENT)属性为表中的每一行提供了一个唯一的标识符。然而,当自增ID达到其最大值时,如何处理这一情况成为了数据库管理员和开发者必须面对的问题。本文将探讨MySQL自增ID耗尽的原因、影响以及有效的应对策略。
902 3
|
XML 关系型数据库 MySQL
MySQL 导出某些数据的技术详解
MySQL 导出某些数据的技术详解
896 3
|
消息中间件 监控 关系型数据库
MySQL数据实时同步到Elasticsearch:技术深度解析与实践分享
在当今的数据驱动时代,实时数据同步成为许多应用系统的核心需求之一。MySQL作为关系型数据库的代表,以其强大的事务处理能力和数据完整性保障,广泛应用于各种业务场景中。然而,随着数据量的增长和查询复杂度的提升,单一依赖MySQL进行高效的数据检索和分析变得日益困难。这时,Elasticsearch(简称ES)以其卓越的搜索性能、灵活的数据模式以及强大的可扩展性,成为处理复杂查询需求的理想选择。本文将深入探讨MySQL数据实时同步到Elasticsearch的技术实现与最佳实践。
895 1
|
存储 关系型数据库 MySQL
技术解析:MySQL中取最新一条重复数据的方法
以上提供的两种方法都可以有效地从MySQL数据库中提取每个类别最新的重复数据。选择哪种方法取决于具体的使用场景和MySQL版本。子查询加分组的方法兼容性更好,适用于所有版本的MySQL;而窗口函数方法代码更简洁,执行效率可能更高,但需要MySQL 8.0及以上版本。在实际应用中,应根据数据量大小、查询性能需求以及MySQL版本等因素综合考虑,选择最合适的实现方案。
2030 6
|
安全 关系型数据库 MySQL
揭秘MySQL海量数据迁移终极秘籍:从逻辑备份到物理复制,解锁大数据迁移的高效与安全之道
【8月更文挑战第2天】MySQL数据量很大的数据库迁移最优方案
1731 17

推荐镜像

更多