当前位置:   article > 正文

《The Art of InnoDB》第二部分|第4章:深入结构-磁盘结构-表空间

《The Art of InnoDB》第二部分|第4章:深入结构-磁盘结构-表空间

第4章:磁盘结构-表空间

4.1 表空间

        表空间是一个数据库术语,它在关系型数据库管理系统(RDBMS)中用于描述数据存储的物理和逻辑结构。在 InnoDB 存储引擎中,表空间特指存储和管理表数据、索引、系统信息等内容的文件或文件集合。表空间的概念对数据库管理和性能优化至关重要。它允许数据库管理员(DBA)精细地控制磁盘空间的分配、数据的组织和事务日志的管理。通过合理配置和维护表空间,可以提升数据库的性能、可靠性和扩展性。它是数据库架构的基石,它们不仅为数据提供了存储容器,而且定义了数据的高效管理和访问方式。无论是日常运维还是性能调优,理解表空间的工作原理都是优化数据库环境的关键一步。

4.1.1 表空间层次结构

表空间是最顶层的单位,代表整个文件或文件集合,它内部包含多个段(Segments)。每个段都是一个特定类型的数据结构,执行特定的功能,并且由多个区(Extents)组成。每个区又包含固定数量的页(Pages),而每个页可能包含多个行(Rows)或索引条目,表空间是存储数据的物理文件,而在表空间内部,数据是按照一定的层次结构组织的。在InnoDB存储引擎中,从大到小可以细分为以下几个层级:

Segments:段是由一个或多个区(Extents)组成的,用于存储特定类型的数据,如索引数据或实际的行数据。索引段用于存储表的索引数据,包括聚集索引(主键索引)和辅助索引(二级索引)。数据段通常指的是聚集索引段,一般来说InnoDB的表数据是按照聚集索引(即主键)顺序存储的。段的设计解决了如何逻辑组织和管理数据库中不同类型对象的存储问题。将数据按类型组织到不同的段中可以优化相关数据对象的管理和访问。例如,索引段可以优化以支持快速的查找操作,而表段可优化以支持大量的行插入。此外,段内的数据可以独立于其他段进行管理、备份或恢复,提供了更好的灵活性和维护性。

Extents:一个区是由一组连续的页组成的,通常一个区包含64个页。区的单位大小通常为1MB,因为每个页默认大小为16KB,64个页便是1MB。区是由连续页组成的一段空间,区的设计让InnoDB能够一次性分配或回收大块连续的磁盘空间,而不是逐页处理,这样不仅减少了空间管理的复杂度,还显著提高了磁盘I/O的性能,因为连续的磁盘区域减少了磁盘头移动的次数,从而加快了数据的读写速度。此外,使用区作为分配单元还有助于防止碎片化,因为它们以较大的、整齐划一的块进行管理,当数据被删除时,整个区可以被整体回收,而不是留下零散的未使用页。这种管理方式也便于数据库扩容,因为可以按需逐区增加存储空间。在数据恢复方面,区提供了一个有序和高效的恢复单元,简化了恢复流程,使得在系统崩溃后能够更快速地定位和修复数据。通过这些设计,InnoDB确保了即使在面对大量事务和庞大数据量的情况下,仍能保持高效的读写性能和稳定运行。

Pages:页是InnoDB存储数据的基本单位。InnoDB支持不同大小的页,但默认大小是16KB。页内部有不同的类型,如数据页、索引页、撤销日志页等。设计页的目的是为了与操作系统的内存管理和磁盘I/O系统配合,使得数据可以高效地从磁盘读取到内存中。页的大小通常是操作系统页大小的整数倍,这样设计可以减少磁盘I/O的次数并且减少碎片,因为数据库系统通常一次性读取或写入一个页的数据。

Rows:实际的表数据按行格式存储在页中。在页中,行可以采用不同的格式,如COMPACT、REDUNDANT、DYNAMIC或COMPRESSED,这些格式影响行数据的存储方式和空间利用效率。

下图描述了一个表空间文件在磁盘中存储的逻辑分层结构


4.1.2 页格式

        在InnoDB存储引擎中,数据被组织成多个"页",每个也中除了保存每一行的数据还维护了每一页本身的信息数据,例如,[页头信息] | [索引项1][索引项2][索引项3] | [空闲空间] | [页脚信息] 。在数据库中页大小通常被设置成文件系统的整数倍,4k-16k,InnoDB中每个页通常有16KB的大小。这意味着在常规操作中,数据在磁盘和内存之间的最小交换单元是16KB。也就是说,系统会至少读取16KB的数据从磁盘到内存,同时至少会把内存中的16KB数据写回磁盘。如果一条数据很大,比如一个VARCHAR字段超过这个大小时,就不能全部放进一个页里。对于一些行格式,比如Compact和Redundant,如果一个字段很长,它会只在原本的位置存前768字节,剩下的数据会放在别的页里,记录里会有个地址指向那些页。而在Dynamic和Compressed行格式中,长字段的数据不会存任何字节在原位置,所有内容都放在其他页,原位置只留个地址。通过将相关的数据存储在物理位置相近的页中,可以减少磁盘I/O操作,从而提高数据库操作的性能。同时,由于页和区是连续分配的,这种结构也利于大范围的扫描操作和大量数据的连续写入。DBA需要了解这些组织结构,同时根据不同业务场景,合理配置InnoDB的缓冲池大小等其他参数,确保频繁访问的页被有效地缓存。

以下是InnoDB页的一般格式描述:

  1. File Header(文件头): 每个页开头都包含一个文件头,用来存储页的元数据,例如页的类型(如:数据页、索引页等)、页号、上一个页和下一个页的链接等。
  2. Page Header(页头): 紧接着文件头的是页头,它包含了页级别的信息,例如记录数、堆顶指针(指向页中可用空间的起始位置)、最后修改事务的事务ID等。
  3. Infimum and Supremum Records(最小和最大记录): InnoDB在每个索引页中包含两个特殊的记录:Infimum(最小)和Supremum(最大)。它们是页中记录的边界标记,用于索引扫描。
  4. User Records(用户记录): 这部分包含了实际存储的数据记录。在数据页中,这些记录就是表中的行。在索引页中,记录是索引条目。
  5. Free Space(空闲空间): 页中未被使用的部分是空闲空间。随着记录的插入和删除,空闲空间的大小会变化。
  6. Page Directory(页目录): 页目录存储了指向页中记录的指针,用于快速定位记录。这是一个小型的索引结构,位于页的底部(在物理存储中接近于页尾部)。
  7. File Trailer(文件尾):每个页的末尾包含一个文件尾,通常是用于检验页的完整性的校验和(checksum)或空间ID(space id)。

        看到这里相信有些读者会有疑问,为啥页目录是在底部的,一般书籍的目录都是在开头的,是的一般的页结构是这样的,为了克服以上的问题,设计出一种名为分槽页的结构,和JVM分配内存空间一模一样,这样的结构就是,定长数据块,管理动态数据空间的典型结构,可以迁移到其他应用场景中。有同学又问了,装不下怎么办,如果数据不能通过一页结构整个装下,且数据还不是顺序的,那就涉及到页分裂的问题,我们后面会讲到。


4.1.3 处理溢出数据

InnoDB存储引擎在处理单条记录超出页面大小(例如16KB)的情况时,采用外部存储或“溢出”页面来存储大型字段。以下是InnoDB如何管理这些大型记录的关键点:

  • 行格式:InnoDB提供了不同的行格式,例如COMPACT、REDUNDANT、DYNAMIC和COMPRESSED。DYNAMIC和COMPRESSED格式支持将超出页容量的大字段存储到单独的溢出页中。
  • 外部存储:当字段内容无法完全放入一个页面内时,InnoDB将部分或全部内容移至外部页,并在原记录中保留一个指向该外部页的指针。
  • 页链接:超出单个溢出页容量的大字段会分散到多个页上,这些页通过链表链接。原记录中的指针指向链表的首个页。
  • 读取和更新性能:访问或修改含有大字段的记录时,必须按需加载相关的溢出页,这可能引发更多的磁盘I/O操作,影响性能。
  • 配置优化:通过选择适当的行格式和调整InnoDB的配置参数,例如页面大小,可以针对存储大型字段的性能进行优化。
  • 碎片管理:频繁更新大型字段可能导致溢出页产生碎片。使用OPTIMIZE TABLE命令可以重组表和优化页使用,减少碎片。

为了高效管理大型数据,设计数据库时应考虑使用DYNAMIC或COMPRESSED行格式,并对可能包含大字段的表进行合理设计。在应用层面,对大数据进行分割和管理也是一种解决方案。这些措施有助于确保即使在单条记录大小超过页面限制的情况下,InnoDB也能有效地存储和访问数据。


4.1.4 System Tablespace

系统表空间是InnoDB的核心存储区,通常命名为ibdata1,位于MySQL数据目录的根位置。它是一个全局共享的表空间,主要用途如下:

  • 存储InnoDB的内部数据字典,记录所有InnoDB表的结构信息。
  • 记录撤销日志(Undo Logs),为事务提供必要的回滚信息。
  • 保存系统级别的DDL操作日志以及其他事务元数据。
  • 维护双写缓冲区(Doublewrite Buffer),确保数据页的安全写入。
  • 存储其他内部结构,如插入缓冲区(Insert Buffer)、自适应哈希索引和锁信息等。

虽然系统表空间在早期的MySQL版本中也用于存储所有用户表和索引,但这种做法会随着时间的推移导致系统表空间不断增长,从而影响数据库的维护和性能。

系统表空间文件 (ibdata1, ibdata2, ...)系统表空间是InnoDB的默认存储区域,通常存储在名为ibdata1的文件中,可能还有ibdata2、ibdata3等续集文件。这个表空间存储了全局InnoDB数据,如内部数据字典、系统表、撤销日志、插入缓冲等。在较老的MySQL版本中,系统表空间也可能包含所有用户表的数据和索引。它通常位于MySQL数据目录中,可以配置为单个或多个文件,并且可以设置其初始大小和自动扩展的选项。

表现 InnoDB 的系统表空间结构,包括段和区的概念。这个表示旨在可视化表空间中的组织结构,请注意它是一个简化的示意图,帮助读者理解,并不包含所有的细节。

在这个示意图中:

  • 最外层的大框代表整个系统表空间文件(如 ibdata1)。
  • 内部的 段 (Segment) 表示不同的数据存储区,例如系统段、UNDO 段以及可能的其他段,比如数据段、索引段等。
  • 每个段由若干 区 (Extent) 组成。每个区是一个预分配的空间块,包含了固定数量的页,通常是64个,总共大小1MB。
  • 每个区包含了具体的 页 (Page),页是存储实际数据的单元,通常是16KB大小。
  • 每页内部分为 文件头部、页面头部、Infimum & Supremum 记录(对于数据页)或 Undo Log Records(对于 UNDO 页),以及 文件尾部,这些是页的标准结构组成部分。

这个视图提供了一个层次化的理解方式,从文件到段,再到区和页的组织。不同类型的段存储不同类型的数据,比如 UNDO 段专门用于存储事务的回滚信息。系统表空间可以包含所有这些不同的数据类型,因而它的结构可能非常复杂。


4.1.5 File-Per-Table Tablespace

文件每表表空间模型从MySQL 5.6.6版本起被引入,为每个新建的InnoDB表提供了一个独立的.ibd文件,其中包含该表的数据和索引。这种模式带来了多种好处:

  • 实现了表数据的隔离,与系统表空间完全解耦,简化了单表的管理。
  • 提高了I/O性能,特别是在多磁盘系统上,因为表的I/O操作不再与其他表或系统表空间争抢资源,提升了性能。
  • 便于对单个表进行备份和恢复操作,同时在表被删除时自动释放磁盘空间,减缓了系统表空间膨胀的问题。

文件每表表空间文件 (tablename.ibd) 从MySQL 5.6版本开始,默认情况下,每个InnoDB表都会在它所属数据库的目录中创建自己的表空间文件,格式为tablename.ibd。这个文件包含了特定表的数据段和索引段。文件每表表空间的好处包括更好的I/O性能、更简单的备份和恢复、以及更好的磁盘空间利用率。

每表空间在 InnoDB 中是针对单个表格创建的独立文件,当 innodb_file_per_table 选项被启用时,默认为每个新创建的表生成一个新的表空间文件。以下是每表空间的简化视觉表示:


4.1.6 General Tablespace

作为系统表空间和文件每表表空间之间的一种折中方案,通用表空间允许管理员将多个表存储在一个共享的表空间文件中。这提供了组织表的更大灵活性,有助于节约磁盘空间,并且可以更好地管理磁盘空间碎片和空间利用率。通用表空间可以跨数据库存储表,提供了更高的数据组织自由度。

通用表空间文件 (tablespace_name.ibd) 通用表空间是MySQL 5.7及之后版本引入的一个特性,允许创建一个可以由多个表共享的表空间文件。这些文件不局限于数据目录的特定位置,可以放在文件系统中的任何位置。通用表空间可以让用户更灵活地管理存储。

通用表空间在 InnoDB 中是一个可以存储多个表数据和索引的共享文件,这可以让用户将多个表组织在同一个物理文件中。以下是通用表空间的简化视觉表示:


4.1.7 Temporary Tablespace

临时表空间专门用于存储查询操作中产生的临时表和其他临时数据结构,如用于排序和哈希操作的临时数据。它支持事务隔离,确保事务中创建的临时表只对该事务可见。数据库重启后,临时表空间中的内容会被自动清除,避免了长期占用磁盘空间。

通过合理地配置和使用这些不同的表空间类型,InnoDB能够为数据库管理员提供灵活的数据存储解决方案,以优化性能并适应各种工作负载和存储需求。每种表空间类型都根据其特定的用途进行了优化,以确保InnoDB数据库可以高效运行和易于维护。适当的表空间配置和管理策略对于确保数据库的持续性能和可扩展性至关重要。

临时表空间文件 (ibtmp1) 临时表空间文件用于存储InnoDB临时表和查询操作期间的中间结果。文件通常命名为ibtmp1,位于MySQL数据目录中。临时表空间文件在MySQL实例重启时会被清空。

临时表空间在 InnoDB 中是用于存储临时表和查询操作中产生的临时数据的特殊表空间。这些数据通常只在当前会话中有效,不需要持久化到磁盘上。以下是临时表空间的简化视觉表示:


4.1.8 Undo Tablespace

撤销表空间是InnoDB存储引擎中专门用于存储撤销日志(undo logs)的表空间。这些日志记录了事务在修改数据库中的数据时所做的变更,以便在事务回滚时撤销这些变更或支持多版本并发控制(MVCC),撤销日志是InnoDB事务处理的一个关键部分,用于记录数据修改前的版本信息。这使得InnoDB可以在以下情况下执行特定的数据恢复操作:

  • 事务回滚:如果一个事务被显式地回滚或由于某些错误而终止,撤销日志将被用来恢复事务开始之前的数据状态。
  • 多版本并发控制(MVCC):InnoDB通过维护数据的不同版本来支持并发读取,撤销日志记录了数据的旧版本,从而允许在不锁定资源的情况下进行事务隔离。
  • 数据库恢复:在数据库崩溃后,撤销日志有助于崩溃恢复过程,它可以用来撤销崩溃发生时未提交的事务或重做已提交的事务。

撤销表空间可以配置为单独的文件,这样做的好处是可以更好地控制撤销日志的大小和管理,尤其是在高负载系统中,这有助于提高系统的整体性能和可维护性。在MySQL 5.7及以后的版本中,可以创建多个撤销表空间,进一步提升了撤销日志的管理和扩展性。

撤销表空间文件 (undo001, undo002, ...) MySQL 5.7及之后版本允许创建独立的撤销表空间文件,用于存储撤销日志。这些文件通常命名为undo001等,可以设定固定大小,也可以配置自动扩展。

以下是撤销表空间的简化视觉表示:

阅读后我们可能会产生一个疑问?Undo Log 为什么同时存在与系统表空间和撤销表空间?

        Undo Logs在InnoDB中是事务处理和MVCC的核心元素,最初它们被设计为存储在单一的系统表空间中,这一设计便于集中管理数据、确保事务的原子性和持久性,并支持MVCC的高并发需求。同时,所有的恢复信息位于一处也简化了数据库的崩溃恢复过程。然而,随着时间的推移,将所有数据和Undo Logs存储在系统表空间的做法逐渐暴露出其缺陷,如表空间的不断膨胀和I/O竞争等问题。为了克服这些限制,现代版本的InnoDB提供了配置独立撤销表空间的选项,将Undo Logs与系统表空间的其他内容分离,以降低I/O竞争,提高性能,并使得Undo Logs管理更加灵活。因此,Undo Logs可能同时存在于系统表空间和独立的撤销表空间中,这一现象反映了InnoDB存储引擎设计进化的历史轨迹。


4.1.9 MySQL文件目录结构

下图是一个描述 MySQL数据库使用一个标准的目录层次结构来组织其数据文件,包含InnoDB表空间文件,这是表空间存在在磁盘上的实际结构,形成一个整体方便读者理解。在一个MySQL实例中,通常会有一个主数据目录(也称为数据目录或数据文件路径),每个数据库都有一个自己的子目录。

  1. mysql_data_directory/ <-- MySQL 数据目录
  2. ├── dbname/ <-- 数据库名对应的目录
  3. │ ├── tablename.ibd <-- 表的文件每表表空间文件(如果启用了文件每表表空间)
  4. │ └── tablename.frm <-- 存储表结构的文件(MySQL 5.7及之前版本)
  5. ├── ibdata1 <-- 系统表空间文件
  6. ├── ib_logfile0 <-- 第一个重做日志文件
  7. ├── ib_logfile1 <-- 第二个重做日志文件
  8. ├── ibtmp1 <-- 临时表空间文件
  9. ├── undo001 <-- 可选的撤销表空间文件
  10. ├── undo002 <-- 可选的撤销表空间文件
  11. ├── general_tablespace_name.ibd <-- 通用表空间文件(如果有创建)
  12. ├── mysql-bin.000001 <-- 第一个二进制日志文件
  13. ├── mysql-bin.000002 <-- 第二个二进制日志文件
  14. ├── mysql-bin.index <-- 二进制日志索引文件
  15. ├── mysql/ <-- 系统数据库的目录
  16. │ └── ...
  17. ├── performance_schema/ <-- 性能模式数据库的目录
  18. │ └── ...
  19. ├── sys/ <-- sys schema的目录
  20. │ └── ...
  21. └── #innodb_temp/ <-- 临时表空间数据的目录(可能的命名方式)
  22. └── ...

        在MySQL 5.6及之后的版本中,InnoDB存储引擎采用了文件每表表空间的模式,使得每个InnoDB表的数据和索引都存储在独立的.ibd文件中,这些文件位于与表所属数据库同名的子目录里。在MySQL 5.7及之前,表结构信息存储在.frm文件中,但从MySQL 8.0开始,这些信息转移到了数据字典中,该字典是系统表空间的一部分,从而废弃了.frm文件。系统表空间文件ibdata1承载了InnoDB的数据字典、系统表信息、撤销日志,以及在未启用文件每表表空间时的用户表数据。InnoDB的重做日志文件,通常为ib_logfile0和ib_logfile1,它们记录事务修改操作,确保即使在系统崩溃后也能恢复数据库状态;默认配置下会有两个文件,但可以通过配置文件调整数量。临时表空间文件ibtmp1是用于存储InnoDB临时表和查询操作中产生的临时结果。如果配置了单独的撤销表空间,文件如undo001和undo002等则专门用于存储撤销日志信息。除此之外,数据目录下还有一些如mysql、performance_schema、sys等系统数据库使用的目录,这些目录存储MySQL服务器所需的元数据和系统信息。这样的文件和目录结构有助于读者理解InnoDB存储引擎的工作原理,需要注意的是,实际的文件结构可能会因特定的配置和MySQL版本而有所不同。


4.1.10 小结

在本节中,我们深入探讨了MySQL的InnoDB存储引擎是如何在物理层面上组织和管理数据的。InnoDB使用了严谨的数据层次结构来优化数据存储、访问和恢复的性能。

表空间和数据层次:表空间可以被认为是数据库文件存储的最顶层单位,由多个段、区和页构成。

  • 段(Segments):包含一种特定类型的数据结构,如索引或行数据。
  • 区(Extents):由一组连续的页构成,通常是64个页,加起来大约1MB。
  • 页(Pages):基本的数据存储单元,默认大小为16KB,内部按需存储不同类型的数据,如表数据、索引、撤销日志等。

InnoDB的页格式:InnoDB页内部结构包括文件头、页头、Infimum & Supremum记录、用户记录和页尾等。这种组织方式有助于InnoDB高效进行磁盘I/O操作,减少了数据访问的时间。

溢出数据InnoDB中是对于过长用户数据的处理方式。

表空间类型:InnoDB提供了多种表空间类型来满足不同的性能和管理需求:

  • 系统表空间(System Tablespace):默认的全局表空间,存储内部数据字典、撤销日志等。
  • 文件每表表空间(File-Per-Table Tablespace):为每个表创建独立的.ibd文件,提供了更好的性能和管理灵活性。
  • 通用表空间(General Tablespace):允许管理员将多个表存储在同一个表空间文件中。
  • 临时表空间(Temporary Tablespace):存储临时表和查询过程中的临时数据。
  • 撤销表空间(Undo Tablespace):专门存储撤销日志的表空间,支持事务回滚和MVCC。

MySQL数据目录结构:MySQL数据目录采用标准的目录层次结构来组织其数据文件。每个数据库拥有自己的子目录,在其中存储表空间文件。系统表空间文件、重做日志文件、通用表空间文件以及二进制日志文件等均位于数据目录中。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/139434
推荐阅读
相关标签
  

闽ICP备14008679号