Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL InnoDB存储结构

MySQL InnoDB行记录存储结构分析

作者:小许code

工作中我们基本上都是用MySQL的InnoDB存储引擎,但是大家有去了解过它的底层存储结构吗,想必绝大部分人不知道或者说不知道怎么查相关知识,刚好来看这篇文章就对了

数据表的文件构成

Mysql的存储行为是由Innodb存储引擎去具体实现的,在windows下安装Mysql后有data(数据库存放的地方)的文件夹,linux一般在/var/lib/mysql文件件。

创建数据库和表后我们可以在data目录先看到数据库对应名称文件夹,文件夹有opt、frm、ibd三种文件:

表空间的组成结构

先看图,先对表空间结构做个大概了解,形成一个概念

InnoDB存储引擎中,对段的管理都是由引擎自身所完成,我们已看到段有几种类型,它是不同类型的区组成的集合,一般分为索引段(B+树非叶子节点区)、数据段(B+树非叶子节点区)、回滚段(回滚数据区)。

也就是说InnoDB 对 B+ 树的叶节点和叶子节点进行了区别对待,也就是说叶子节点有自己独有的区,非叶子节点也有自己独有的区,如果不区分叶子节点和非叶子节点,统统把节点代表的页面放到申请到的区中的话,进行范围扫描的效率就大幅降低,而不同的区的集合就组成了不同的段。

我们知道B+树的每一层中的页都会形成一个双向链表,如果是以页为单位来分配存储空间的话,双向链表相邻的两个页之间的物理位置可能不是连续的,也许离得非常远,这种情况下进行 随机I/O 是会很慢的。

因此,应该尽量让链表中相邻的页的物理位置也相邻,这样进行范围查询的时候才可以使用所谓的 顺序I/O。

区在物理位置上由连续的64个页组成,InnoDB 中的页大小默认是 16KB,所以一个区的大小是 64*16KB= 1MB,这样使得页的双向链表在物理位置也是相邻的,从而进行顺序I/O,加快了查询效率!

在表数据量大的时候,为某个索引分配空间的时候就不再按照页为单位分配了,而是按区为单位分配,甚至在表中的数据特别多的时候,可以一次性分配多个连续的区。

Innodb读取数据的时候,并不是按照行来读取数据的,InnoDB 的数据是按【页】为单位来读写的,当需要读一条记录的时候,并不是将这个行记录从磁盘读出来,而是以页为单位,将其整体读入内存。

MySQL也是以【行 row】进行存储的,图中对于行的描画图是 COMPACT格式,这也是重点需要了解的格式,而不同的行格式,存储的结构也不同。

InnoDB 行格式类型

行格式:就是记录在磁盘上的存放形式或者说存储结构

InnoDB 存储引擎设计了 4 种行格式,分别是 Redundant、Compact、Dynamic和 Compressed ,后三个都是紧凑型行格式,为的是存放更多的行记录。

Redundant 行格式比较古老了, MySQL 5.0 版本之前用的行格式,现在基本不用了,我们知道有这个格式就行了

Compact 行格式在MySQL 5.0 之后引入,在MySQL5.1版本中,默认设置为Compact行格式,一条完整的记录其实可以被分为记录的额外信息和记录的真实数据两大部分。

Dynamic 和 Compressed 它们的行格式都和 Compact 挺像,只是在 处理溢出列数据和Compact不同 ,MySQL5.7 版本之后,默认使用 Dynamic 行格式。

Compact 行格式图解

从上面我们知道Compact和Dynamic 和 Compressed很像,那么我们就Compact行格式展开进一步了解,了解了Compact就等同于对其他也做了了解。

从图中我们可以看到Compact行格式下,一条记录分为 【记录的额外信息】和【记录的真实数据】两部分,我们的列数据是在真实数据部分,我们再分别对这些内容进行更具体的描述。

记录的额外信息

额外信息为的是更好的管理记录,分为变长字段长度列表、NULL值列表、记录头信息

我们来创建一个表来看看变长字段具体是存的,表结构如下,行格式 Compact,本文对于行记录的实际存储案例基于这张表:

CREATE TABLE `demo1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `col1` varchar(45) COLLATE utf8_bin DEFAULT NULL,
  `col2` varchar(45) COLLATE utf8_bin DEFAULT NULL,
  `col3` int(11) DEFAULT NULL,
  `col4` char(5) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=ascii ROW_FORMAT=COMPACT;

并插入三条数据,demo1表中的各个列都使用的是ascii字符集(每个字符只需要1个字节来进行编码)

1:变长字段列信息

针对VARCHAR、TEXT、BLOB这类变长字段,列中实际存储了多少数据是不固定的,因此除了要把数据本身存下来,还需要记下它的长度,COMPACT将变长列的实际长度按照字段的顺序,逆序存储在变长字段长度列表里。

变长字段存储空间分为两部分:真正的数据部分、该数据占用的字节数

从demo1表的第一条记录来看各个字段占用的字节数,因为是变长字段, id、col3(int)、col(char)这三个字段可以不用管

第一行行记录填入变长字段长度列表后的示意图如下:

逆序排列的目的是为了让位置靠前的记录的真实数据和数据对应的字段长度信息可以同时在一个 CPU Cache Line 中,这样就可以提高 CPU Cache 的命中率

2:NULL值列表

要注意的是null值列表并不是固定的1个字节,如果一条记录中有9个字段的值都是null,那么null值列表大小将是两个字节大小,依次类推。

结合这些特性,我们来看看一条记录中存在null值和不存在null值在null值列表中的样子,我们记录使用上面表demo1的结构和数据,其中id是主键不能为null,不在讨论范围内,表中null字段不超过8个,这三条记录对应的null值列表如下:

第一条记录:

第二条记录:

第三条记录:

3:记录头信息

记录头其实包含了很多信息,如图,我们着重了解红色部分几个比较重要的。

记录的真实数据

我们看隐藏字段 row_id、trx_id、roll_ptr 感觉是不是在哪里遇到过,只要你了解过Mysql的MVCC机制就很熟悉这几个字段

其他字段就是我们创建表的时候定义的各个列字段了。

总结

通篇下来,感觉对InnoDB实际的存储结构有了更深的认识,当然也会产生不少问题,比如:

1:一行记录除了 TEXT、BLOBs 类型的列,限制最大为 65535 字节,那么能具体分析分析吗?

2:行溢出了会怎么样,因为一页就16kb,16384字节,是小于65535 字节的

3:为什么设计表的时候字段会选择not null?

等等,这些问题将会在下次进行总结,就不在这里用大篇幅展开了。

到此这篇关于MySQL InnoDB行记录存储结构分析的文章就介绍到这了,更多相关MySQL InnoDB存储结构内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文