首页 > 其他分享 >innodb表空间和索引初探

innodb表空间和索引初探

时间:2023-11-01 17:04:57浏览次数:45  
标签:space id 索引 innodb 初探 空间 page

概述

innodb 是 MySQL 主要的存储引擎, innodb 包含缓存页、事务系统和存储系统。本篇文章主要涉及最底层的物理存储进行分析,讲解了表空间的概念、数据字典、借助工具从用户表空间读取数据和观察索引的数据结构。


这个主要针对 MySQL5.7.40, 具体版本差异可能略微有不一致的地方。

在讲述前,我们先创建一个测试表和测试库。

CREATE DATABASE test;use test;CREATE TABLE IF NOT EXISTS `user`(   `id` INT UNSIGNED AUTO_INCREMENT,   `name` VARCHAR(100) NOT NULL, DEFAULT CURRENT_TIMESTAMP,   PRIMARY KEY ( `id` ))ENGINE=InnoDB DEFAULT CHARSET=utf8;

然后尝试插入若干条数据。

INSERT INTO test.user (name) VALUES ("1");...

表空间和索引存储

本章节的目录主要是为了描述表空间的物理文件构成和并借助innodb_ruby工具直接从表空间中读取数据记录。

表空间简述

MySQL表空间是指在MySQL数据库中用于存储表和索引数据的逻辑结构单元。每个表都会被分配到一个或多个表空间中,表空间是由一个或多个数据文件组成的。在MySQL中,表空间的主要作用包括:存储表和索引数据、管理存储空间、优化性能。MySQL 包含如下表空间:


系统表空间:系统表空间是用于存储数据库管理系统的系统数据和元数据的特殊表空间。它包含系统目录、系统表和其他系统数据等。ibdata1 就是一个用来构建innodb系统表空间的文件。


每表文件表空间:每表文件表空间(也称为用户表空间)是用于存储用户创建的表和索引的表空间。每个表和索引都可以属于不同的表空间。以<database>/.ibd文件的形式存在,每个表都独立占用一个表空间文件。


通用表空间:通用表空间是一种特殊类型的表空间,用于存储多个用户的表和索引。与每表文件表空间不同,通用表空间提供了更高的灵活性和可伸缩性,因为多个用户可以共享同一个表空间。它还简化了数据库维护和管理的工作。


撤消表空间:撤消表空间(也称为回滚表空间)用于存储事务撤消或回滚数据。撤消表空间通常是系统表空间的一部分。


临时表空间:临时表空间用于存储临时数据,如排序操作和临时表的创建。


通过控制台 sql 也可以从INFORMATION_SCHEMA 库中的 INNODB_SYS_TABLESPACES 表查看表空间的信息,当前版本系统表空间没有展示出来,系统表空间 ID 为 0.

mysql> select SPACE,NAME from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES;+-------+---------------------------------+| SPACE | NAME                            |+-------+---------------------------------+|     2 | mysql/plugin                    |....|    25 | test/user                       |+-------+---------------------------------+

表空间物理结构

每个空间被分为多个页面,通常每个页面 16 KiB。如下图,每个 page 通常有不同的作用。


数据页

页(Page):页是InnoDB存储引擎中管理数据的最小单元。InnoDB将表的数据和索引按页的形式进行存储。每个页的大小通常为16KB。InnoDB的缓冲池(Buffer Pool)中存储着页数据,当需要读取或写入数据时,InnoDB会将对应的页加载到缓冲池中进行操作。

一个页中除去页头和页尾,实际可用的大小为 16338 byte. 不同类似的页实际内容都不太一样。



innodb分析工具innodb_ruby

innodb_ruby是一个用于操作 InnoDB 存储引擎的 Ruby 语言库。它提供了在 Ruby 代码中访问和操作 InnoDB 存储引擎的功能。其中共包含 2 个命令行工具innodb_space和innodb_log分别可以分析 innodb 表空间和日志,这里主要用到innodb_space 工具对innodb 表空间和数据存储进行分析。

innodb_space 可以使用选项--system-space-file (-s) 指定系统空间,也可以使用选项--space-file (-f)指定普通的表空间进行分析。

系统表空间

ibdata 即系统空间,ibdata1是一个用来构建innodb系统表空间的文件,这个文件包含了innodb表的元数据、undo日志、修改buffer和双写buffer。 其中 ibdata 的大部分 page 页都是固定类型如下图。


从这张innodb 的结构图就可以看出来,系统表空间中大部分页的物理位置都是固定的.

使用innodb_space -s ibdata1 space-summary 可以看到 ibdata1 中每一页的信息,其中 type 属性代表该页的具体类型。

[root@sky data]# innodb_space -s ibdata1 space-summarypage        type                prev        next        lsn      0           FSP_HDR             0           0           12217552 1           IBUF_BITMAP         0           0           1199510  2           INODE               0           0           886508   3           SYS                 0           0           11196    4           INDEX               0           0           11196    5           TRX_SYS             0           0           12218071 6           SYS                 0           0           1196046  7           SYS                 0           0           12219210..。

其中从上图的命令行输出中可以看到以下一些页

1. FSP_HDR (Filespace Header): 这是文件空间头部页,用于存储关于整个文件系统的信息。

2. IBUF_BITMAP (Insert Buffer Bitmap): 这是插入缓冲区位图页,用于跟踪插入缓冲区中哪些位置有数据。

3. INODE: 这是索引节点页,用于存储文件和目录的元数据,例如权限、所有者和时间戳等。

4. SYS: 这是系统页,可能用于存储与文件系统或数据库相关的其他系统信息。

5. INDEX: 这是索引页,用于存储和管理文件系统中的索引数据,从而加快文件系统的访问速度。

6. TRX_SYS (Transaction System): 这是事务系统页,用于存储和管理数据库中的事务相关信息。

...


innodb_space 命令还支持直接查看系统表空间中记录的表空间信息, 通过system-spaces 参数可以打印出来。 user 表空间一共有 6 个 page,有 1 个索引 page.

[root@sky data]# innodb_space -s ibdata1 system-spacesname                            pages       indexes  ...test/user                       6           1           

InnoDB数据字典

我们目前关心的数据字典,也就是 SYS: Data Dirctionary 就是存储数据字典的相关信息,

数据字典信息是数据库表的元信息,包含了表的结构、列、索引的描述。

InnoDB由于历史原因,数据字典元数据与表元数据文件(文件)中存储的信息存在一定程度的重叠.frm 文件,在 MySQL 8 中已经取消.frm 文件


可以通过以下命令从 ibdata1 文件中读取每个表的元数据,包含表信息、列信息、索引信息和字段信息

innodb_space -s ibdata1 data-dictionary-tables
innodb_space -s ibdata1 data-dictionary-columns
innodb_space -s ibdata1 data-dictionary-indexes


查看当前 test/user 表的信息

[root@sky data]# innodb_space -s ibdata1 data-dictionary-tablesname      id   n_cols      type mix_id  mix_len   cluster_name   space test/user 44   2147483651  33   0           80                   25

查看 test/user列的信息

[root@sky data]# innodb_space -s ibdata1 data-dictionary-columnstable_id   pos   name         mtype       prtype      len   prec44          0     id          6           1795        4     0     44          1     name        12          2167055     300   0     44          2     create_time 3           525575      4     0

查看 test/user索引信息,从这里可以看到page_no =3 , 代表索引页的根页位于test/user表空间的第三页

[root@sky data]# innodb_space -s ibdata1 data-dictionary-indexestable_id    id          name       n_fields  type  space       page_no 44          41          PRIMARY    1         3     25          3

   

用户表空间

用户表空间通常一个表属于一个空间,位于<数据库名称>/表名.ibd 文件。 

通过space-summary 命令查看用户表空间的每一页信息,需要-f 指定相应的表空间 ibd 文件。

和系统表空间中的结构比较类型,其中根索引页一般都在第三页,如果超过一个索引页,会往下增加索引页,上面记录的 table page_no 一致

[root@sky test]# innodb_space -f test/user.ibd space-summarypage        type                prev        next        lsn            0           FSP_HDR             0           0           12221415       1           IBUF_BITMAP         0           0           12218171       2           INODE               0           0           12221415       3           INDEX               0           0           12221415       4           ALLOCATED           0           0           0              5           ALLOCATED           0           0           0

索引页

通过page-illustrate 参数可以打印出当前页的详细使用情况和分布情况,如下就是一个索引页的详情。

[root@sky data]# innodb_space -f test/user.ibd  -p 3  page-illustrate                                       Page 3 (INDEX)                                Offset ╭────────────────────────────────────────────────────────────────╮           0 │█████████████████████████████████████▋██████████████████████████│          64 │█████████▋███████████████████▋████████████▋████████████▋████▋▋▞▞│         128 │▞▞▞▞▞████▋▋▞▞▞▞▞▞▞████▋▋▞▞▞▞▞▞▞████▋▋▞▞▞▞▞▞▞████▋▋▞▞▞▞▞▞▞▞▞▞▞▞▞▞│         192 │▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞│              ....       16256 │▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞│       16320 │▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞▞█▋█▋█████▋│Legend (█ = 1 byte):  Region Type                         Bytes    Ratio█ FIL Header                           38    0.23%█ Index Header                         36    0.22%█ File Segment Header                  20    0.12%█ Infimum                              13    0.08%█ Supremum                             13    0.08%█ Record Header                        25    0.15%█ Record Data                           5    0.03%█ Page Directory                        4    0.02%█ FIL Trailer                           8    0.05%░ Garbage                               0    0.00%▞ Unknown (no data dictionary)      16222   99.01%

其实索引页的结构如下,Record Data 就是实际存放索引数据的地方。

1. FIL Header:文件头部,存储了索引页的一些基本信息,比如页号和页类型等。

2. Index Header:索引头部,存储了索引的元数据,包括索引的结构和属性等信息。它提供了索引的定义和配置,用于索引的创建和维护。

3. File Segment Header:文件段头部,用于管理数据库文件的段(segment)的头部信息。一个数据库文件可以被分为多个段,文件段头部记录了每个段的属性和状态等信息。

4. Infimum:最小值,是在索引页中的特殊记录,代表索引键值的最小值。它用于保证索引的完整性和范围查询的正确性。

5. Supremum:最大值,是在索引页中的特殊记录,代表索引键值的最大值。和Infimum类似,它也用于保证索引的完整性和范围查询的正确性。

6. Record Header:记录头部,存储了每个记录的元数据,包括记录的长度、类型等信息。它用于索引页中记录的解析和访问。

7. Record Data:记录数据,存储了每个记录的具体数据。根据不同的索引类型,记录数据可以是实际的索引键值,或者是指向实际数据行的指针。

8. Page Directory:页面目录,存储了索引页中每个记录的位置和偏移量等信息。它提供了索引页内记录的快速查找和访问。

9. FIL Trailer:文件尾部,存储了索引页的结尾信息,如校验和等。它用于保证索引页的完整性和正确性。

10. Garbage:垃圾数据,表示在索引页中没有被使用或不再有效的数据区域。这些数据区域被标记为垃圾,可能由于索引的修改或数据的删除而产生。

11. Unknown (no data dictionary):未知类型(未分配空间),表示在索引页中没有被分配给具体数据类型或者未被使用的空间。这些空间可能是留作扩展或未使用的保留空间


可以直接通过指定user.ibd 执行 page-records 获取记录,但是由于没有数据字典,只能获取每个数据记录在页中的偏移量,无法仅仅靠用户表空间解析数据本身。

[root@sky examples]# innodb_space -f test/user.ibd   -p 3  page-recordsRecord 126: () → ()Record 154: () → ()Record 182: () → ()Record 210: () → ()Record 238: () → ()Record 266: () → ()Record 294: () → ()Record 322: () → ()Record 350: () → ()Record 378: () → ()Record 406: () → ()

利用数据字典,加上用户表空间就能读取到数据库数据了。这里尝试使用page-records 命令读取用户表空间中page_no=3 的数据记录,能够顺利地读取表空间的真实数据。

这也说明了本地磁盘并没有做静态加密,即使不需要任何认证只要能够访问数据字典就能读取数据。

[root@sky examples]# innodb_space -s ./ibdata1  -T test/user -p 3  page-recordsRecord 126: (id=1) → (name="1", create_time="2023-07-23 14:07:16")Record 154: (id=2) → (name="1", create_time="2023-07-23 14:07:17")Record 182: (id=3) → (name="1", create_time="2023-07-23 14:07:17")Record 210: (id=4) → (name="1", create_time="2023-07-23 14:07:18")Record 238: (id=5) → (name="1", create_time="2023-07-23 14:07:19")Record 266: (id=6) → (name="1", create_time="2023-07-23 14:07:19")Record 294: (id=7) → (name="1", create_time="2023-07-23 14:07:20")Record 322: (id=8) → (name="1", create_time="2023-07-23 14:07:21")Record 350: (id=9) → (name="1", create_time="2023-07-23 14:07:21")Record 378: (id=10) → (name="1", create_time="2023-07-23 14:07:22")Record 406: (id=11) → (name="1", create_time="2023-07-23 15:07:51")
[root@sky test]# innodb_space -f test/user.ibd  -p 3  page-directory-summaryslot    offset  type          owned   key索引页组织方式

本章节主要讲表空间数据页中和跨数据页的索引排列方式。

页内索引-页目录

一个页内部的索引,通常是一个链表结构,一个元素指向下一个元素,单调递增。其中单个页中包含

● infimum: 代表索引最小的值,即链表头

● supremum: 代表索引最大的值,即链表尾

如果一个 page 中一共有 n 的索引,那么为了找到一个索引,只是通过链表的结构从infimum 到supremum,最大的时间复杂度就需要 O(n),为了提高单个 page 中的索引的查询数据。引入了一个叫 page directory 的数据结构,有一块专门的物理区域存放,也可以叫做页目录。


这个算法的原理是这样的

首先对索引进行分组,每一组叫数据槽 slots,其中 owned 为组中的索引数量,记录在该组的最后一个索引中,并且page directory 中记录的地址也是最后一个索引的地址。

如下图中 infimum 为第一组,索引数 1;中间每组索引数为 4;最后一组 supremum 索引数为 5;一共有 7 个 slot.

然后就可以通过对 page directory 二分法定位索引,时间复杂度为O(logN)

1. 先定位到中间的 slot 末尾索引地址,对比索引值大小

2. 如果需要查询的索引值大于它,则从当前 slot 和最后一个 slots 取中间 slot 的索引进行对比

3. 如果需要查询的索引值小于它,则从当前 slot 和第一个 slots 取中间 slot 的索引进行对比

4. 直到上下界限的 slot 相等,然后从末尾索引开始往前定位,直到找到相等的索引



可以通过page-directory-summary 查看当前索引页的 page-directory 信息,可以看出infimum 索引数固定为 1,中间 slot 索引数为 4,最后的supremum 为 4~7 之间,这里为 7.

0       99      infimum       1       1       210     conventional  4       ()2       112     supremum      7

跨页索引-B+树

页外就是我们常见的 B+树模型了,一个页只有 16k,存储的数据有限,当我们插入大量的数据时,会发生什么事情呢

尝试不断往测试表中插入数据,当前已经插入 1897 条记录。

mysql> select count(*) from test.user;+----------+| count(*) |+----------+|     1897 |+----------+1 row in set (0.00 sec)

此时观察索引页数量,已经由原来的 1 个变成了 7 个,这代表树开始进行分裂。

[root@sky data]# innodb_space -f test/user.ibd space-summarypage        type                prev        next        lsn      0           FSP_HDR             0           0           12511818 1           IBUF_BITMAP         0           0           12218171 2           INODE               0           0           12511818 3           INDEX               0           0           12511818 4           INDEX               0           5           12400985 5           INDEX               4           6           12455415 6           INDEX               5           7           12511818 7           INDEX               6           8           12511818 8           INDEX               7           0           12515126 9           ALLOCATED           0           0           0

通过index-digraphdigraph 命令打印出当前的索引结构。从以下打印出的内容中,可以当前 b+树已经分裂成 8 个树。

其中根节点是 page_3,其他 5 个节点是叶子树,这是一个二级的 B+树。

[root@sky data]# innodb_space -s ./ibdata1  -T test/user -I PRIMARY  index-digraphdigraph btree {  rankdir = LR;  ranksep = 2.0;  page_3 [ shape = 'record'; label = '<page>Page 3|(5 records)|<dir_4>(#<struct Innodb::Page::Index::FieldDescriptor name="id", type="INT UNSIGNED", value=1, extern=nil>)|...];    page_3:dir_4 → page_4:page:nw;    page_4 [ shape = 'record'; label = '<page>Page 4|(267 records)'; ];    page_3:dir_5 → page_5:page:nw;    page_5 [ shape = 'record'; label = '<page>Page 5|(534 records)'; ];    page_3:dir_6 → page_6:page:nw;    page_6 [ shape = 'record'; label = '<page>Page 6|(534 records)'; ];    page_3:dir_7 → page_7:page:nw;    page_7 [ shape = 'record'; label = '<page>Page 7|(534 records)'; ];    page_3:dir_8 → page_8:page:nw;    page_8 [ shape = 'record'; label = '<page>Page 8|(28 records)'; ];}

通过page-records 命令查看每一页的数据(由于记录太多, 我这边只记录关键的一些输出),

[root@sky data]# innodb_space -s ./ibdata1  -T test/user -p 3  page-recordsRecord 125: (id=1) → #4Record 138: (id=268) → #5Record 151: (id=802) → #6Record 164: (id=1336) → #7Record 177: (id=1870) → #8[root@sky data]# innodb_space -s ./ibdata1  -T test/user -p 4  page-recordsRecord 126: (id=1) → (name="1", create_time="2023-07-23 14:07:16")...Record 7574: (id=267) → (name="1", create_time="2023-07-26 12:57:35")[root@sky data]# innodb_space -s ./ibdata1  -T test/user -p 4  page-recordsRecord 126: (id=268) → (name="1", create_time="2023-07-26 12:57:35")...Record 15050: (id=801) → (name="1", create_time="2023-07-26 12:57:58")...

通过上面的命令可以判断出当前的 B+树模型为下图所示。

那么当我们需要通过索引寻找数据时

1. 首先从根索引开始查找,当然页内部会通过 page directory 机制进行查找

2. 当发现索引位于某 2 个索引区间,获取起始区间的索引中记录的下一个子树的地址,并且开始查找下一个子树

3. 子树内部依然通过page directory 机制进行查找,这里由于只有二级,所以就能直接找到索引,如果有更多级,则继续迭代往子树查找

请至钉钉文档查看「白板」

MYSQL 常规的 B+树模型分布可以参考下图。

● infimum 和 supremun 记录每页中的最小记录和最大记录

● 非叶子节点中的索引记录的是下一个页的地址,叶子节点的行记录直接跟在索引后面(聚簇索引)

● 定位索引需要从一级一级往下进行定位



总结

InnoDB是MySQL的一种存储引擎,它采用了B+树作为存储和索引的数据结构。了解InnoDB的物理结构对于系统管理员和业务开发人员都非常重要,可以提升MySQL的运维稳定性和优化业务的执行效率。

对于系统管理员来说,了解InnoDB的物理结构可以帮助他们更好地管理数据库的存储空间和性能。

对于业务开发人员来说,理解InnoDB的物理结构可以帮助他们优化SQL查询和提高业务的执行效率。

总而言之,了解InnoDB的物理结构对于系统管理员和业务开发人员都是非常有益的。系统管理员可以通过维护和管理数据库的物理结构提升系统的稳定性,而业务开发人员则可以通过优化SQL查询和索引结构提高业务的执行效率。这样可以使得MySQL的运维和业务开发更加高效和可靠。

参考资源

innodb_diagrams https://github.com/jeremycole/innodb_diagrams

innodb_ruby https://github.com/jeremycole/innodb_ruby

The basics of InnoDB space file layout  https://blog.jcole.us/2013/01/03/the-basics-of-innodb-space-file-layout/

innodb-tablespace https://dev.mysql.com/doc/refman/5.7/en/innodb-tablespace.html





标签:space,id,索引,innodb,初探,空间,page
From: https://blog.51cto.com/u_13431807/8129712

相关文章

  • elastic索引管理
    常用API2,索引压缩实际上是压缩的分片,并非在原有索引上压缩,而是生成了一个新的索引,由于使用了hash路由算法以及索引不可变的特性---操作步骤---reindex不会把mapping和seting复制过去 删除副本删除副本"index.numberof_replicas":0设置只读"index.blocks.write":true 案例......
  • 数据库系列:MySQL引擎MyISAM和InnoDB的比较
    1、数据库核心知识点数据库系列:MySQL慢查询分析和性能优化数据库系列:MySQL索引优化总结(综合版)数据库系列:高并发下的数据字段变更数据库系列:覆盖索引和规避回表数据库系列:数据库高可用及无损扩容数据库系列:使用高区分度索引列提升性能数据库系列:前缀索引和索引长度的取舍......
  • [20231026]bbed查看索引kd_off结构的问题.txt
    [20231026]bbed查看索引kd_off结构的问题.txt--//使用bbed查看索引kd_off结构时存在问题,前面两项指向的偏移不对,从kd_off[2]算起,而且记录的是相对偏移=绝对偏移-kdxle偏移.--//遗漏的两项可以通过最大的kd_off项记录的地址+2,+4获得.--//dumpoffsetkd_off[max]+2count2--//d......
  • 谷歌搜索引擎课程笔记
    1、bywave、lantem搜索引擎处理流程GoogleHackingDatabase:GHDB汇总了数千条谷歌搜索高级语法,涵盖了立足点、敏感路径、敏感文件、错误信息、漏洞文件、漏洞服务器、Web服务器检测等方方面面。2004年开始更名为GHDB,现在由网站exploit-db.com维护GoogleHacking操作符基础操作符:......
  • MySQL8.0新特性之:不可见索引
    概念描述  MySQL从8.0版本开始支持不可见索引(invisibleindex)也可叫隐式索引,隐形索引或者隐藏索引。不可见索引是不被MySQL优化器使用的,但是优化器会正常维护它。使用场景非主键上的索引使用测试删除索引对性能的影响,而无需进行破坏性更改(通常对于大表来说,删除或者重建索引的影响......
  • MySQL技术内幕InnoDB存储引擎学习笔记
    1、MYSQL体系结构: 2、INNODB存储引擎:支持事务,其设计目的主要是面向在线事务处理的应用。特点:行锁设计,支持外键,并支持类似oracle的非锁定读,同时设计用来最有效的利用使用内存和CPU;5.5.8开始默认使用innodb存储引擎使用多版本并发控制来获得高并发性,并实现了sql的4种隔离级......
  • 获取摄像头名字和索引
    #pipinstallwmiimportwmidefget_camera_name():camera_names=[]wmi_obj=wmi.WMI()cameras=wmi_obj.Win32_PnPEntity()forcameraincameras:ifcamera.Nameand'camera'incamera.Name.lower():camera......
  • mysql 索引
    1.索引介绍索引就是一种数据结构,类似于书的目录。意味着以后再查数据应该先找目录再找数据,而不是用翻页的方式查询数据索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。primarykeyuniquekeyindexkey注意:上面三种key前两种除了有加速查......
  • 数据库系列:前缀索引和索引长度的取舍
    数据库系列:MySQL慢查询分析和性能优化数据库系列:MySQL索引优化总结(综合版)数据库系列:高并发下的数据字段变更数据库系列:覆盖索引和规避回表数据库系列:数据库高可用及无损扩容数据库系列:使用高区分度索引列提升性能1背景有时候我们需要在字符类型的字段上建设索引,但是如果......
  • 解锁高效检索技能:掌握MySQL索引数据结构的精髓
    (文章目录)......