MySQL 中 InnoDB 的数据文件和线程模型

我们看下 InnoDB 文件的存储结构,如下图:

InnoDB 数据文件存储结构,我们可以看到一个 ibd 数据文件包含:

  • Segment(段)
  • Extent(区)
  • Page (页)
  • Row(行)

Tablespace

表空间,主要用于存储多个 idb 数据文件、表的记录和索引。一个文件中包含了多个段。

Segment(段)

用于管理多个 Extent,分为数据段(Leaf node segment)、索引段(Non-leaf node segment)、回滚段(Rollback segment)。一个表中至少会有两个 segment(一个用于管理数据,一个用于管理索引)。每多创建一个索引,都会多两个 segment。

Extent

区,一个区中固定包含 64 个连续的 Page(页),大小为 1 M。当空间不足时,需要重新分配的页资源,它不会一个页一个页的分配,而是直接分配一个区。

Page

页,主要用于存储多个 Row 行记录。页的大小为 16 K。

页分为很多类型:

  • 数据页

  • Undo 页

  • 系统页

  • 事务数据页

  • 大的 BLOB 对象页


Row

行,主要包含了记录的字段值,事务的 ID (Trx Id)、滚动指针(Roll pointer)、字段指针(Field pinters)等信息。

所以我们可以看到,Page 是文件存储的最基本的单位。无论是何种类型的 Page,都是由 Page Header、Page Trailer 和 Page Body 组成的。下图为 Page 的结构图:

show table status;

通过 information_schema 查看指定表的文件格式

select * from information_schema.innodb_sys_tables;

File 文件格式

目前 InnoDB 只支持两种文件格式:Antelope 和 Barracuda。

  • Antelope:最原始的 InnoDB 文件格式。支持两种行格式:COMPACT 和 REDUNDANT
  • Barracuda:新的文件格式。支持 InnoDB 的所有行格式,包括新的行格式:COMPRESSED 和 DYNAMIC

在创建表和索引时,文件格式都被用于每个 InnoDB 表数据文件(其名称与*.ibd匹配)。修改文件格式的方法是重新创建表及其索引,最简单方法是对要修改的每个表使用以下命令 :

ALTER TABLE 表名 ROW_FORMAT= 格式类型;
复制代码

ROW 行格式(ROW_FORMAT)

表的行格式决定了它的行是如何进行物理存储的。反过来又会影响查询的性能和 DML 操作的性能。如果在单个 Page 页中容纳更多的行,查询和索引查找可以更快地工作,缓冲池所需的内存更少,写入更新操作时所需的 IO 操作会更少。

InnoDB 存储引擎支持四种行格式:

  • REDUNDANT
  • COMPACT
  • DYNAMIC
  • COMPRESSED

每个表的数据分为如干个页来存储,每个页中采用 B 树结构存储数据。如果某些字段的信息过长,导致无法存储在 B 树节点中,这个时候会被单独分配空间,此时的页被称之为溢出页,该字段被称为页外列

REDUNDANT 行格式

使用 REDUNDANT 行格式,表会将变长列值的前 768 字节存储在 B 树节点的索引记录中。其余的存储在溢出页上。对于大于等待与 768 字节的固定长度字段,InnoDB 会转换为变长字段,以便能够在页外存储。

所以,列值的长度最大不能超过 768 个字节

COMPACT 行格式

与 REDUNDANT 行格式相比,COMPACT 行格式减少了约 20% 的行存储空间。但是代价是增加了某些操作的 CPU 使用量。如果系统赋值是受缓存命中率和磁盘速度限制。那么 COMPACT 格式可能更快。如果系统负载受到 CPU 速度的限制,那么 COMPACT 格式可能会慢一些。

DYNAMIC 行格式

使用 DYNAMIC 行格式,InnoDB 会将表中可变长度的列值完全存储在页外,而索引记录只包含指向溢出页的指针(20 个字节)。大于或等于 768 字节的固定长度字段编码为可变长度字段。DYNAMIC 行格式支持大索引前缀,最多可以为 3072 字节,可以通过 innodb_large_prefix 参数控制。

COMPRESSED 行格式

COMPRESSED 行格式提供与 DYNAMIC 行格式相同的存储特性和功能,但增加了对表和索引数据压缩的支持。

InnoDB 的线程模型

我们学习下 InnoDB 的线程模型。首先我们看一张图:

从图中我们能看到,InnoDB 中有四种线程,分别为:

  • Master Thread

  • IO Thread

  • Purge Thread

  • Page Cleaner Thread

IO Thread

在 InnoDB 中使用了大量的 AIO(Async IO)来做读写处理,这样可以极大提高数据库的性能。在 InnoDB1.0 版本之前共有 4 个 IO Thread,分别是 write,read,insert buffer 和 log thread,在后来的版本中将 read thread 和write thread 分别增大到了4个,所以一共就有 10 个线程了。

read thread

有 4 个线程。负责读取操作,将数据从磁盘中加载到缓存 page 也。

write thread

有 4 个线程。负责写操作,将缓存脏页刷新到磁盘中

log thread

有 1 个线程。负责将日志缓冲区内容刷新到磁盘中。

insert buffer thread

有 1 个线程。负责将写缓冲区的内容刷新到磁盘中。

Purge Thread

事务提交之后,其使用的 Undo Log 将不在需要,所以需要开启 Purge Thread 去回收已经分配的 Undo 页。可以通过参数 innodb_purge_thread 进行控制。

show variables like '%innodb_purge_thread%';
复制代码

Page Cleaner Thread

主要将脏数据刷新到磁盘中,脏数据刷盘后,其相应的 Redo Log 也就可以覆盖,既可以同步数据,又能够达到 Redo Log 循环使用的目的。这个过程会调用 write thread 进行处理。可以用参数 innodb_page_cleaner 进行配置。

show variables like '%innodb_page_cleaner%';
复制代码

Master Thread

Master Thread 是 InnoDB 的主线程,负责调度其它各线程,优先级最高。作用是将缓冲池中的数据异步刷新到磁盘,从而保证数据的一致性。

其处理内容有:

  • 脏页的刷新(page cleaner thread)
  • Undo 页的回收(purge thread)
  • Redo Log 刷新(log thread)
  • 合并缓冲区等

其内部有两种处理方式:

  • 每隔 1s 处理
  • 每隔 10s 处理

每隔 1s 处理

主要做以下工作:

  • 刷新日志缓冲区,刷到磁盘中
  • 合并写缓冲区中的数据,根据 IO 读写压力来决定是否操作
  • 刷新脏页数据到磁盘,当脏页的比例达到 75% 时才会操作。(innodb_max_dirty_pages_pct,innodb_io_capacity )

每 10s 处理

主要做以下工作:

  • 刷新脏页数据到磁盘中
  • 合并写缓冲区中的数据
  • 刷新日志缓冲区
  • 删除无用的 Undo 页

猜你喜欢

转载自juejin.im/post/7037115463574224910