InnoDB 磁盘 I/O 和 文件空间管理

发布时间 2023-08-25 11:20:16作者: LARRY1024

InnoDB 磁盘 I/O

InnoDB 尽可能使用异步磁盘 I/O,通过创建多个线程来处理 I/O 操作,同时允许在 I/O 仍在进行时继续进行其他数据库操作。

  • 在 Linux 和 Windows 平台上,InnoDB使用可用的操作系统和库函数来执行“本机”异步 I/O。

  • 在其他平台上,InnoD B仍然使用 I/O 线程,但线程实际上可能会等待 I/O 请求完成,该技术称为“模拟” 异步 I/O。

预读(Read-Ahead)

如果 InnoDB 可以确定很快可能需要数据的可能性很高,它会执行预读(read-ahead)操作以将该数据放入缓冲池,以便它在内存中可用。对连续数据发出一些大型读取请求比发出几个小型分散请求更有效。

预读请求是异步预取缓冲池中的多个页面的 I/O 请求 ,以预测即将需要这些页面。这些请求将所有页面引入某一 范围内。 InnoDB使用两种预读算法来提高 I/O 性能:

  • 线性预读(linear read-ahead),是一种根据缓冲池中按顺序访问的页面来预测可能很快需要哪些页面的技术。

    如果 InnoDB 注意到表空间中某个段的访问模式是顺序的,它会提前将一批数据库页读取发送到 I/O 系统。

    我们可以 InnoDB 使用配置参数 innodb_read_ahead_threshold 调整触发异步读取请求所需的顺序页面访问次数来控制何时执行预读操作。在添加该参数之前,InnoDB 只会在读取当前 extent 的最后一页时才计算是否对整个下一个 extent 发出异步预取请求。

    innodb_read_ahead_threshold 可以控制检测顺序页面访问模式的敏感程度,默认值:56,即必须从一个扩展区顺序读取至少 56 个页面才能启动后续扩展区的异步读取,可选值范围:[0, 64],值为 0 表示禁用预读,值越高,访问模式检查越严格。

    InnoDB 如果从一个盘区顺序读取的页数大于或等于 innodb_read_ahead_threshold,InnoDB 则启动整个后续盘区的异步预读操作。

  • 随机预读(random read-ahead),是一种根据缓冲池中已有的页面来预测何时可能很快需要页面的技术,而不管这些页面的读取顺序如何。

    如果 InnoDB 注意到表空间中的某些区域似乎正在被完全读入缓冲池,它将剩余的读取发送到 I/O 系统。

    如果在缓冲池中找到来自同一盘区的 13 个连续页面, InnoDB 则异步发出请求以预取该盘区的剩余页面。要启用此功能,需要配置变量设置 innodb_random_read_ahead 为 ON。

双写缓冲区(Doublewrite Buffer)

InnoDB使用一种新颖的文件刷新技术,涉及一种称为双写缓冲区的结构 ,默认是启用的(innodb_doublewrite=ON)。它增加了意外退出或断电后恢复的安全性,并通过减少 fsync() 操作需求来提高大多数 Unix 系统的性能。

在将页面写入数据文件之前,InnoDB 首先将它们写入称为双写缓冲区的存储区域。只有在双写缓冲区的写入和刷新完成后,才会将 InnoDB 页面写入数据文件中的正确位置。如果在页面写入过程中,出现操作系统、存储子系统或者 mysqld 进程意外地退出,就会导致页面撕裂(torn page),InnoDB 可以在恢复期间从双写缓冲区中找到该页面的良好副本。

文件空间管理

我们可以使用 innodb_data_file_path 配置选项在配置文件中定义的数据文件形成 InnoDB 系统表空间。这些文件在逻辑上串联起来形成系统表空间。使用中没有条带化。您无法定义表在系统表空间内的分配位置。在新创建的系统表空间中,InnoDB 从第一个数据文件开始分配空间。

为了避免在系统表空间内存储所有表和索引所带来的问题,我们可以启用 innodb_file_per_table 配置选项,它将每个新创建的表存储在单独的表空间文件中(扩展名为 .ibd)。对于以这种方式存储的表,磁盘文件内的碎片较少,并且当表被截断时,空间将返回给操作系统,而不是仍然由 InnoDB 在系统表空间中保留。

我们还可以将表存储在通用表空间中,通用表空间是使用CREATE TABLESPACE 语法创建的共享表空间,它们可以在 MySQL 数据目录之外创建,能够保存多个表,并支持所有行格式的表。

页、范围、段和表空间

Page

每个表空间都是由数据库页面(page)组成的,MySQL 实例中的每个表空间都具有相同的页面大小。

默认情况下,所有表空间的页面大小均为 16KB,我们可以在创建 MySQL 实例时,通过 innodb_page_size 选项调整页面大小。

Extent

表空间中的一组连续的页面会被称为扩展区(extent)

对于16KB的页面,一个扩展区会包含64个页面,当页面 size 小于等于 16KB 时,扩展区 size 为 1MB。

page size extent size pages per extent
4KB 1MB 256
8KB 1MB 128
16KB 1MB 64
32KB 2MB 64
64KB 4MB 64

Segment

表空间内的“文件”在 InnoDB 中称为段(segments)

这些段与回滚段不同,回滚段实际上包含许多表空间段。

当表空间内的段增长时, InnoDB 会一次性为它分配前 32 页;之后,InnoDB 才将整个扩展区分配给该段。InnoDB 一次最多可以将 4 个扩展区添加到一个大段中,以保证数据良好的顺序性。

InnoDB 中为每个索引分配两个段:B-tree 的 非叶子节点(nonleaf nodes) 和 B-tree 的叶子节点(leaf nodes)。保持叶子节点在磁盘上的连续性可以实现更好的顺序 I/O 操作,因为这些叶节点包含实际的表数据

表空间中的某些页包含其他页的位图,因此 InnoDB 表空间中的一些扩展区不能作为整体分配给段,而只能作为单独的页。

通过 SHOW TABLE STATUS 语句可以查询表空间中的可用空间,InnoDB 会报告表空间中明确空闲的范围。InnoDB 总是保留一些范围用于清理和其他内部目的;这些保留区不包含在可用空间中。

当我们从表中删除数据时,InnoDB 会收缩相应的 B-tree 索引。释放的空间是否可供其他用户使用取决于删除模式是否将单个页或范围释放到表空间;删除表或删除其中的所有行可以保证将空间释放给其他用户。

注意,删除的行只能通过清除(purge)操作来物理删除,在事务回滚或一致性读(快照读)不再需要它们之后的一段时间,清除操作会自动发生。

配置保留文件段页面的百分比

变量 innodb_segment_reserve_factor 定义了表空间文件的段页中保留为空页的百分比。保留一定比例的页面以供将来增长,以便 B 树中的页面可以连续分配。修改保留页百分比的能力允许对 InnoDB 进行微调,以解决数据碎片或存储空间使用效率低下的问题。

innodb_segment_reserve_factor 的默认值为 12.5,即每个段中空页占 12.5%,可选值的范围:[0.03, 40]。该设置适用于每表文件和通用表空间。

页面如何与表行相关

最大行长度

最大行长度:

  • 对于 4KB、8KB、16KB 和 32KB 的页面大小,最大行长度略小于数据库页大小的一半;

    例如,对于默认的 16KB InnoDB 页面大小,最大行长度略小于 8KB。

  • 对于 64KB 的页面大小,最大行长度略小于 16KB。

行存储策略

  • 如果某行不超过最大行长度,则所有行都存储在本地页面内;

  • 如果某行超过最大行长度,则会选择可变长度列进行外部页外存储,直到该行符合最大行长度限制。

    可变长度列的外部页外存储因行格式而异:

    • 紧凑和冗余行格式(COMPACT and REDUNDANT Row Formats)

      当选择可变长度列用于外部页外存储时,InnoDB 将前 768 个字节存储在本地行中,其余部分存储在外部溢出页中,每个这样的列都有自己的溢出页面列表。768 字节前缀附带一个 20 字节值,该值存储列的真实长度并指向存储其余值的溢出列表。

    • 动态和压缩行格式(DYNAMIC and COMPRESSED Row Formats)

      当选择可变长度列用于外部页外存储时,InnoDB 将 20 字节指针本地存储在行中,其余部分在外部存储到溢出页中。

LONGBLOB 和 LONGTEXT 列必须小于 4GB,并且行的总长度(包括 BLOB 和 TEXT 列)必须小于 4GB。

InnoDB Checkpoints

将日志文件设置得非常大可能会减少检查点(checkpiont)期间的磁盘 I/O 。将日志文件的总大小设置为与缓冲池一样大甚至更大通常是有意义的。

检查点处理的工作原理

InnoDB 实现了称为模糊检查点(fuzzy checkpointing)的检查点机制,InnoDB 小批量地从缓冲池中刷新修改的数据库页面。无需在单个批次中刷新缓冲池,避免在检查点过程中中断用户 SQL 语句的处理。

在崩溃恢复期间, InnoDB 会查找写入日志文件的检查点标签,它知道标签之前对数据库的所有修改都存在于数据库的磁盘映像中。然后 InnoDB 从检查点向前扫描日志文件,将记录的修改应用到数据库。

参考: