InnoDB文件物理结构解析1 - 概述

这篇具有很好参考价值的文章主要介绍了InnoDB文件物理结构解析1 - 概述。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

之前立下的Flag, 经过这段时间断断续续的折腾,总算告一段落。写的分析程序(库)已经可以对InnoDB的表空间的数据文件(.ibd)进行的基本的解析,通过程序编写也对ibd文件的文件结构有了一定的了解。程序已经放在github上:
https://github.com/Li-Xiang/mysql-file-parser

项目用Java编写maven管理, 你可以clone下来后导入自己的IDE, 本文后续我基于该程序进行介绍。

在查阅资料过程发现网上已经有大量的关于ibd文件结构的文章,不想大量摘抄, 选了一些文章质量比较高连接,推荐给大家:

  • Jeremy Cole的blog以及他的项目innodb_diagrams: 主要是"InnoDB_Structures.pdf"这个文件。

  • 淘宝的"PolarDB 数据库内核月报"上面的文章质量也很高,相关的文章有:
    http://mysql.taobao.org/monthly/2016/02/01/
    http://mysql.taobao.org/monthly/2017/11/01/
    http://mysql.taobao.org/monthly/2018/04/03/
    http://mysql.taobao.org/monthly/2019/10/01/

1. InnoDB表空间和数据文件

与Oracle数据库类似,在InnoDB存储引擎中,所有的用户数据(InnoDB tables and associated indexes)都存放在表空间(Tablespace)中,有两种类型的表空间:General(or Shared) Tablespaces和File-Per-Table Tablespace。类似的, 表空间只是逻辑概念,表空间的数据文件存储在datadir目录中, 以.ibd做为扩展名,本文要讨论的就是这些.ibd文件,也就是InnoDB表空间的(数据文件的)物理存储结构。

这里讨论的是InnoDB中普通(用户创建)表及相关索引的文件存储结构, 如果你查阅MySQL官方文档, InnoDB中还包含有"System Tablespace", “Undo Tablespaces”, “Temporary Tablespace”, 这些表空间是MySQL内部使用的, 存储数据库系统相关信息, 不在文讨论范围, 也不适用本文的内容。

InnoDB默认将表创建在file-per-table表空间中,也就是一个Table独立存储为一个单独的".ibd"文件。该行为受innodb_file_per_table=on (default)参数控制, 如果将该参数值设置为off, 将导致InnoDB将表创建在系统表空间内。一个表空间至少包含一个数据文件,一个数据文件(.idb)只属于一个表空间,不同的表空间使用一个space_id来唯一标识。可以通过系统试图I_S.innodb_tables和I_S.innodb_tablespaces查看:

root@localhost [information_schema]> select table_id,name,space, row_format,space_type from information_schema.innodb_tables where name in ('mysql/db','mysql/user','jforum/jforum_config');
+----------+----------------------+------------+------------+------------+
| table_id | name                 | space      | row_format | space_type |
+----------+----------------------+------------+------------+------------+
|     1025 | mysql/db             | 4294967294 | Dynamic    | General    |
|     1026 | mysql/user           | 4294967294 | Dynamic    | General    |
|     1400 | jforum/jforum_config |        343 | Dynamic    | Single     |
+----------+----------------------+------------+------------+------------+
3 rows in set (0.00 sec)

root@localhost [information_schema]> select space,name, row_format, page_size,space_type,server_version,state from information_schema.innodb_tablespaces where space in (4294967294,343 );
+------------+----------------------+------------+-----------+------------+----------------+--------+
| space      | name                 | row_format | page_size | space_type | server_version | state  |
+------------+----------------------+------------+-----------+------------+----------------+--------+
| 4294967294 | mysql                | Any        |     16384 | General    | 8.0.18         | normal |
|        343 | jforum/jforum_config | Dynamic    |     16384 | Single     | 8.0.18         | normal |
+------------+----------------------+------------+-----------+------------+----------------+--------+
2 rows in set (0.14 sec)

这里可以看到, mysql库下的db, user系统表的space_id都为4294967294, 他们的表空间类型General(共享空间)。用户表jforum库下的jforum_config表的space_id为343, 表空间类型为Single, 也就是File-Per-Table。这些表的行格式都是Dynamic, 后面会进行介绍。

file-per-table=on是MySQL默认设置, 也是大部分最佳实践推荐设置, 不同的表空间类型的物理存储结构会不同,目前写的程序也能解析file-per-table表空间的数据文件。

2. InnoDB行格式

我们知道InnoDB支持多种行格式(row format),行格式决定了InnoDB如何物理存储行数据(Page的格式),目前InnoDB支持4种行格式: REDUNDANT, COMPACT, DYNAMIC, and COMPRESSED.

创建表时默认的行格式受innodb_default_row_format参数控制,MySQL5.6默认值为COMPACT,MySQL5.7开始默认值为DYNAMIC。在创建表时是可以通过ROW_FORMAT选项指定表的行格式的, 也是可以通过Alter table修改的:

root@localhost [testcase]> select @@innodb_default_row_format;
+-----------------------------+
| @@innodb_default_row_format |
+-----------------------------+
| dynamic                     |
+-----------------------------+
1 row in set (0.00 sec)

### 指定行格式为REDUNDANT
root@localhost [testcase]> CREATE TABLE tab (
    ->    id int,
    ->    str varchar(50)
    -> ) ENGINE=InnoDB ROW_FORMAT=REDUNDANT;
Query OK, 0 rows affected (0.03 sec)

root@localhost [testcase]> select name,space,row_format,space_type from information_schema.innodb_tables where name='testcase/tab';
+--------------+-------+------------+------------+
| name         | space | row_format | space_type |
+--------------+-------+------------+------------+
| testcase/tab |  1503 | Redundant  | Single     |
+--------------+-------+------------+------------+
1 row in set (0.00 sec)

### 将行格式改成DYNAMIC
root@localhost [testcase]> alter table testcase.tab ROW_FORMAT=DYNAMIC;
Query OK, 0 rows affected (0.20 sec)
Records: 0  Duplicates: 0  Warnings: 0

root@localhost [testcase]> select name,space,row_format,space_type from information_schema.innodb_tables where name='testcase/tab';
+--------------+-------+------------+------------+
| name         | space | row_format | space_type |
+--------------+-------+------------+------------+
| testcase/tab |  1504 | Dynamic    | Single     |
+--------------+-------+------------+------------+
1 row in set (0.00 sec)

不同的行格式有不同的特性,如CPU/空间特性,详细可参考官方文档:

The COMPACT row format reduces row storage space by about 20% compared to the REDUNDANT row format, at the cost of increasing CPU use for some operations. If your workload is a typical one that is limited by cache hit rates and disk speed, COMPACT format is likely to be faster. If the workload is limited by CPU speed, compact format might be slower.

行格式还有对应的文件格式概念(InnoDB File Format):

InnoDB有两种文件格式: Antelope and Barracuda。Antelope是最早期的文件格式(Antelope is the original InnoDB file format), 仅支持COMPACT和REDUNDANT行格式, 不支持DYNAMIC和COMPRESSED行格式。Barracuda是InnoDB后续引入的新文件格式, 支持COMPACT, REDUNDANT, DYNAMIC和COMPRESSED行格式。InnoDB的文件格式通过innodb-file-format参数指定,MySQL 5.7版本该参数已经废弃,在MySQL 8.0版本开始已经被移除。 官方文档的解释是该参数的目的是版本降级,但降级的目标版本已经到达了产品的生命周期(end of their product lifecycles),所以移除该参数。

行格式对ibd文件的Page解析是决定性的,例如,REDUNDANT格式记录的Extra Bytes部分占6个字节,DYNAMIC/COMPACT占5个字节。通常我们使用的都是COMPACT/DYNAMIC格式(MySQL默认),在网上大部分的文章也是基于这两种格式的,后续的介绍也只讨论这两种行格式。

DYNAMIC与COMPACT有相同的存储特性,DYNAMIC对大字段(BLOB, TEXT, VARCHAR字段,且字段的数据量非常大)的存储进行了增强。

The DYNAMIC row format offers the same storage characteristics as the COMPACT row format but adds enhanced storage capabilities for long variable-length columns and supports large index key prefixes.

当一个可变长字段太大(单个Page无法放下整行),将发生行溢出(off-page/overflow page), COMPACT行格式行溢出时,主键(Cluster Key)会存储行中的前768字节加20字节指向存放溢出数据的页的指针,而DYNAMIC行格式仅存储20字节的指针, 使用的是完全的行溢出。

本文及和解析程序也不会考虑行溢出的情况,mysql-file-parser后续也许会进行完善。

3. ibd文件基本结构

ibd文件由一个个固定长度的数据页(Page)组成, 页的默认大小为16384字节(16KB), 由Innodb_page_size参数控制, 该参数只能在数据库实例初始化时指定,之后不能修改。因为页大小是固定的,所以要读取一个页是非常简单的,只需要从文件的page-number*page-size位置开始,读取page-size个字节。

Page是InnoDB的最小存储单位,Page太小了,类似的,为提高空间管理效率,InnoDB还有区(extent)和段(segment)的逻辑概念。区是由若干连续的页组成,而段又由若干的连续的区组成,区是InnoDB分配空间的基本单位。更多关于extent和segment的介绍,请移步: File Space Management。

ibd中有多种不同类型的页,每种类型的页有各种不同的用途,但所有的页都有相同的基本结构(FIL Header和FIL Trailer):

    0 +-----------------------------+
      |    FIL Header (38 bytes)    |
   38 +-----------------------------+
      | Other headers and Page data |
      |  depending on Page Type.    |
16376 +-----------------------------+
      |    FIL Trailer (8 bytes)    |
16384 +-----------------------------+

其中FIL Header包含一个页的基本信息,解析FIL Header是解析整个数据页的第一步。FIL Header的结构如下:

 0 +-----------------------------------+		
   | CHECKSUM                          | //FIL_PAGE_SPACE_OR_CHKSUM
 4 +-----------------------------------+
   | FIL_PAGE_OFFSET                   | // PAGE NUMBER
 8 +-----------------------------------+
   | FIL_PAGE_PREV                     |
12 +-----------------------------------+
   | FIL_PAGE_NEXT                     |
16 +-----------------------------------+
   | FIL_PAGE_LSN                      |
24 +-----------------------------------+
   | FIL_PAGE_TYPE                     |
26 +-----------------------------------+
   | FIL_PAGE_FILE_FLUSH_LSN           |
34 +-----------------------------------+
   | SPACE_ID                          | //FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID
38 +-----------------------------------+

下面以一个具体的ibd文件来说明, 完整的测试程序放在org.littlestar.mysql.ibd.examples下

public class IbdFile1 {
	public static void main(String[] args) throws IOException, Exception {
		String fileName = "D:\\Data\\mysql\\8.0.18\\data\\sakila\\film.ibd";
		try (IbdFileParser parser = new IbdFileParser(fileName)) {
			StringBuilder buff = new StringBuilder();
			buff.append("PAGE   CHECKSUM PAGE_OFFSET  PAGE_PREV  PAGE_NEXT           PAGE_LSN              PAGE_TYPE           FLUSH_LSN SPACE_ID\n")
			    .append("---- ---------- ----------- ---------- ---------- ------------------ ----------------------- ------------------ --------\n");
			for (long i = 0; i < parser.getPageCount(); i++) {
				Page page = parser.getPage(i);
				FilHeader filHeader = page.getFilHeader();
				buff.append(String.format("%4d ", i))
					.append(String.format("0x%4s ", toHexString(filHeader.getCheckSumRaw())))
					.append(String.format("%11d ", filHeader.getPageOffset()))
					.append(String.format("%10d ", filHeader.getPreviousPage()))
					.append(String.format("%10d ", filHeader.getNextPage()))
					.append(String.format("0x%16s ", toHexString(filHeader.getPageLSNRaw())))
					.append(String.format("%23s ", filHeader.getPageTypeName()))
					.append(String.format("0x%16s ", toHexString(filHeader.getFlushLSNRaw())))
					.append(String.format("%8d", filHeader.getSpaceId()))
					.append("\n");
			}
			System.out.println(buff);
		}
	}
}

/ 程序执行的输出如下:
PAGE   CHECKSUM PAGE_OFFSET  PAGE_PREV  PAGE_NEXT           PAGE_LSN              PAGE_TYPE           FLUSH_LSN SPACE_ID
---- ---------- ----------- ---------- ---------- ------------------ ----------------------- ------------------ --------
   0 0xfaf2f6ae           0      80018          1 0x00000002fc051196   FIL_PAGE_TYPE_FSP_HDR 0x0000000000000000      387
   1 0x90b09fbd           1          0          0 0x00000002fc052bde    FIL_PAGE_IBUF_BITMAP 0x0000000000000000      387
   2 0xe86bb12b           2          0          0 0x00000002fc051196          FIL_PAGE_INODE 0x0000000000000000      387
   3 0x60b74d0c           3 4294967295 4294967295 0x00000002fbf0571c            FIL_PAGE_SDI 0x0000000000000000      387
   4 0x0f666da9           4 4294967295 4294967295 0x00000002fc051196          FIL_PAGE_INDEX 0x0000000000000000      387
   5 0x56c9b259           5 4294967295 4294967295 0x00000002fc0309a4          FIL_PAGE_INDEX 0x0000000000000000      387
   6 0xa0af9ead           6 4294967295 4294967295 0x00000002fc0541a9          FIL_PAGE_INDEX 0x0000000000000000      387
   7 0xacea1280           7 4294967295 4294967295 0x00000002fc0541b9          FIL_PAGE_INDEX 0x0000000000000000      387
   8 0xff743aa0           8 4294967295          9 0x00000002fbfe3efc          FIL_PAGE_INDEX 0x0000000000000000      387
   9 0x6a968cda           9          8         10 0x00000002fbff0c14          FIL_PAGE_INDEX 0x0000000000000000      387
  10 0x4bdf777b          10          9         11 0x00000002fbffd9f7          FIL_PAGE_INDEX 0x0000000000000000      387
  11 0x71aec053          11         10         12 0x00000002fc00a6b8          FIL_PAGE_INDEX 0x0000000000000000      387
  12 0xd01c16cf          12         11         13 0x00000002fc01726a          FIL_PAGE_INDEX 0x0000000000000000      387
  13 0x7db2fcf1          13         12         14 0x00000002fc02400c          FIL_PAGE_INDEX 0x0000000000000000      387
  14 0xf1b7f350          14         13         15 0x00000002fc037b0d          FIL_PAGE_INDEX 0x0000000000000000      387
  15 0xd4adea4e          15         14         18 0x00000002fc044660          FIL_PAGE_INDEX 0x0000000000000000      387
  16 0x0353247e          16 4294967295         17 0x00000002fc0309a4          FIL_PAGE_INDEX 0x0000000000000000      387
  17 0xa120c89b          17         16 4294967295 0x00000002fc054199          FIL_PAGE_INDEX 0x0000000000000000      387
  18 0xf3b509e5          18         15         19 0x00000002fc051196          FIL_PAGE_INDEX 0x0000000000000000      387
  19 0x764b4910          19         18         20 0x00000002fc051196          FIL_PAGE_INDEX 0x0000000000000000      387
  20 0xefc75205          20         19 4294967295 0x00000002fc054176          FIL_PAGE_INDEX 0x0000000000000000      387
  21 0x00000000           0          0          0 0x0000000000000000 FIL_PAGE_TYPE_ALLOCATED 0x0000000000000000        0

可以看到FIL_PAGE_OFFSET就是页数据文件中的编号,从0开始, 文件的第一个页为FIL_PAGE_TYPE_FSP_HDR, PAGE_PREV和PAGE_NEXT用于构建双向链表,后续会拿FIL_PAGE_INDEX做进一步介绍,4294967295对应的是0xffffffff,代表链表的头/尾,如果一个节点PAGE_PREV和PAGE_NEXT的是都是0xffffffff说明这是个根节点或者这个页类型没有链表结构。

预分配页FIL_PAGE_TYPE_ALLOCATED所有值都是0,在被时候后会被修改为对应的值。

因为是File-Per-Table, 所以SPACE_ID都是387, 可以通过innodb_tablespaces确认:

root@localhost [testcase]> select space,name, row_format, page_size,space_type,state from information_schema.innodb_tablespaces where space =387;
+-------+-------------+------------+-----------+------------+--------+
| space | name        | row_format | page_size | space_type | state  |
+-------+-------------+------------+-----------+------------+--------+
|   387 | sakila/film | Dynamic    |     16384 | Single     | normal |
+-------+-------------+------------+-----------+------------+--------+
1 row in set (0.14 sec)

每种页都有不同的用途,用于存储用户表和相关索引数据的是FIL_PAGE_INDEX页,后续会重点介绍该页,MySQL8.0后新增了FIL_PAGE_SDI页,存储了表和空间相关的元数据,因为存储结构与FIL_PAGE_INDEX页一样,也会一并介绍。文章来源地址https://www.toymoban.com/news/detail-641900.html

到了这里,关于InnoDB文件物理结构解析1 - 概述的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • mysql索引的数据结构(Innodb)

    首选要注意,这里的数据结构是存储在硬盘上的数据结构,不是内存中的数据结构,要重点考虑io次数. 一.不适合的数据结构: 1.Hash:不适合进行范围查询和模糊匹配查询.(有些数据库索引会使用Hash,但是只能精准匹配) 2.红黑树:可以范围查询和模糊匹配,但是和硬盘io次数比较多. 二

    2024年02月10日
    浏览(56)
  • MySQL 存储引擎 InnoDB 内存结构之缓冲池

    缓冲池是主存储器中的一个区域,在访问 table 和索引数据时 InnoDB 会对其进行缓存。缓冲池允许直接从内存中访问频繁使用的数据,从而加快处理速度。在专用服务器上,通常将高达 80% 的物理内存分配给缓冲池。 为了高效处理大量读取操作,缓冲池被划分为可以容纳多行

    2024年02月10日
    浏览(42)
  • 【MySQL索引与优化篇】InnoDB数据存储结构

    索引结构给我们提供了高效的索引方式,不过索引信息以及数据记录都是保存在文件上的,确切说是存储在页结构中。另一方面,索引是在存储引擎中实现的,MySQL服务器上的 存储引擎 负责对表中数据的读取和写入工作。不同存储引擎中 存放的格式 一般是不同的。 由于 I

    2024年02月07日
    浏览(53)
  • MySQL的varchar存储原理:InnoDB记录存储结构

    摘要: varchar(M) 能存多少个字符,为什么提示最大16383?innodb怎么知道varchar真正有多长?记录为NULL,innodb如何处理?某个列数据占用的字节数非常多怎么办?影响每行实际可用空间的因素有哪些?本篇围绕innodb默认行格式dynamic来说说原理。 本文分享自华为云社区《MySQL的v

    2024年02月04日
    浏览(44)
  • 一文带你了解MySQL之InnoDB 数据页结构

    前言 学完了记录结构,我们该学数据页的结构,前边我们简单的提了一下页的概念,它是Innodb管理存储空间的基本单位,页的大小默认16KB,InnoDB为了不同的目的而设计了许多种不同类型的页,比如存放表空间头部信息的页,存放Insert Buffer信息的页,存放INODE信息的页,存放

    2024年02月06日
    浏览(92)
  • MySQL 存储引擎 InnoDB 内存结构之更改缓冲区

    更改缓冲区(Change Buffer)是一种特殊的数据结构,用于缓存不在缓冲池中的二级索引(secondary index)页的更改。可能来自于 INSERT 、 UPDATE 或 DELETE 操作(数据操作语言,DML)的缓冲更改,会在后续通过其他读操作将这些页加载到缓冲池时被合并。 与聚簇索引(clustered indexe

    2024年02月10日
    浏览(41)
  • MySQL中InnoDB索引数据结构(B+树)详解

    B+ 树: 是由二叉查找树,平衡二叉树和B树演化而来 二叉查找树: 任何节点的左节点的值都小于该节点,右节点都大于该节点。 为了避免二叉查找树的极端情况,即太高瘦,引入了平衡二叉树。 平衡二叉树: 又称 AVL 树,在满足二叉查找树特性的基础上,要求每个节点的左

    2024年02月15日
    浏览(60)
  • MySQL之盛放记录的大盒子 【InnoDB 数据页结构】

    前言 学完了记录结构,我们该学数据页的结构,前边我们简单的提了一下页的概念,它是Innodb管理存储空间的基本单位,页的大小默认16KB,InnoDB为了不同的目的而设计了许多种不同类型的页,比如存放表空间头部信息的页,存放Insert Buffer信息的页,存放INODE信息的页,存放

    2024年02月05日
    浏览(40)
  • 【MySQL进阶-08】深入理解innodb存储格式,双写机制,buffer pool底层结构和淘汰策略

    MySql系列整体栏目 内容 链接地址 【一】深入理解mysql索引本质 https://blog.csdn.net/zhenghuishengq/article/details/121027025 【二】深入理解mysql索引优化以及explain https://blog.csdn.net/zhenghuishengq/article/details/124552080 【三】深入理解mysql的索引分类,覆盖索引(失效),回表,MRR https://bl

    2024年02月05日
    浏览(50)
  • MySQL物理文件----日志文件(错误日志、通用查询日志、二进制日志、慢查询日志)

    MySQL 错误日志记录 MySQL 运行过程中较为严重的警告和错误信息,以及 MySQL 每次启动和关闭的详细信息。 MySQL 错误日志默认是开启的。可以通过 MySQL 配置文件中的 log-error=/var/log/mysqld.log 配置,修改错误日志的配置信息。 可以通过如下 SQL 查看错误日志的详细信息: show vari

    2024年02月15日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包