MySQL InnoDB如何工作:底层如何存储数据?
开始阅读本文之前,先思考三个问题:
- innodb基础层是如何存储数据的?
- 表格中的隐藏列是什么?
- 用户注释如何交互?
请阅读以下内容并给出您的答案。
本文主要包含以下内容:
1.磁盘还是内存?
1.1磁盘
如何解决数据安全问题?
答案:将数据存储在磁盘上。
只要磁盘不损坏,数据就可以永久保存。
如果每次都从磁盘读取数据,数据会安全,但是频繁的IO请求会影响数据库的性能。
那么我们如何解决数据库性能问题呢?
1.2 内存
将数据存储在内存中。
内存可以满足我们快速读写数据的需求。
内存可以存储部分用户数据,但如果数据量太大,可能无法存储所有用户数据。此外,如果数据库服务器或部署节点崩溃或重新启动,则存在数据丢失的风险。
如何防止异常情况导致数据丢失。同时,能否保证数据读写的速度?
2。数据页
页是mysql中磁盘和内存交换的基本单位,也是mysql存储空间管理的基本单位。
同一数据库实例中的所有表空间具有相同的页大小;默认情况下,桌面上的页面大小为 16KB。当然,也可以通过更改 innodb_page_size 选项来更改默认大小。需要注意的是,不同的页面大小最终会导致不同的尺寸。
一次从磁盘读取至少16KB的内容到内存,一次从内存刷新至少16KB的内容到磁盘。
如下图书写时:
如下图阅读时:
数据页主要用于存储表格中的注释。它使用双向链表链接在磁盘上。方便搜索,您可以快速从一个数据页导航到另一页。
很多时候,因为我们的表中有很多数据,所以可能会在磁盘上存储很多页数据。
如果我们想要根据一定的条件查询数据,我们需要从一个数据页中找到另一个数据页。这时,双向链表就派上用场了。磁盘上每个数据页的整体结构如下图所示:
一个数据页的内容是什么?
从上图可以看到,数据页主要包含以下几个部分:
- 文件头
- 页头
- 最大和最小记录 ”s♷可用空间 目录页
- 文件尾部
3。用户记录
对于新申请的数据页,用户记录为空。在插入数据时,innodb会分配一部分空白空间
用于用户记录。
用户记录是innodb的重中之重。通常存储在数据库中的数据是在内部存储的。那么,它包含什么?
实际上,innodb 支持四种数据行格式:
- 紧凑行格式
- 冗余行格式
- 动态行格式 ♻‷紧凑行格式 ♻‷格式例如:
Recor d user主要包含三部分:
- 附加信息记录,其中包括变长字段、空值列表和记录头信息。
- 隐藏列,包含行id、事务id和断点。
- 真实数据字段包含真实的用户数据,并且可以有很多字段。
我们一起来看看内容吧。
3.1 附加信息
附加信息不是实际的用户数据,它用于帮助数据存储。
3.1.1 变长列列表
有些数据如果直接存储的话会出现问题。例如:如果列是varchar或text类型,则其长度不固定,可以根据存储数据的长度而变化。相应地改变。
如果没有在一处记录正确的数据长度,innodb将不知道要分配多少空间。如果按照固定长度分配空间,但实际数据占用的空间并不多,那不是浪费吗?
因此,应在变长列中记录特定变长列所占用的字节数,以方便所需的空间分配。
3.1.2 空值列表
数据库中某些列的值允许为空。如果每个字段的空值都存储在用户记录中,显然是浪费存储空间。
有没有办法只标记它而不存储实际的空值?
答案:将空字段保存到空值列表中。
列表中使用二进制值1表示该列允许为空,使用0表示不允许为空。它只占用1位来指示某个字符是否为空,确实可以节省大量的存储空间。
3.1.3 记录头信息
记录头信息用于描述一些特殊属性。
主要包括:
- deleted_flag:删除标志,用于表示一条记录已被删除。
- min_rec_flag:最小目录标志,即非叶子节点上的最小目录标志。
- n_owned:拥有的记录数,记录该组中索引记录的数量。
- heap_no:在堆中的位置,表示记录当前在堆中的位置。
- record_type:记录类型,其中:0表示普通记录,1表示非叶子节点,2表示Infrimum记录,3表示Supremum记录。
- next_record:下一个记录位置。
3.2 隐藏字段
数据库在存储用户记录时,会自动创建一些隐藏字段。如下图所示:
现在innodb自动创建了三个隐藏列:
- db_row_id,即行id,即记录的唯一标识符。
- db_trx_id,事务id,是事务的唯一标识符。
- db_roll_ptr,回滚点,用于事务回滚。
如果表中有主键,则使用主键作为行ID,无需额外创建。如果表中没有主键,如果有非空唯一键,则将其作为行ID,无需创建添加。
如果表中没有主键或唯一键,数据库会自动生成行id。
也就是说,在innodb中,隐藏列中的
事务id
和重放点
应该根据隐藏条件创建。 。3.3 真实数据字段
真实数据字段存储用户的真实数据,可以包含多个字段的数据。这个很简单,不多说。
3.4 用户记录如何关联?
通过上面的内容,大家应该对如何保存用户记录有了一定的了解。
但有一个问题,一条用户记录如何连接到另一条用户记录? innodb如何知道特定记录的下一条记录是谁?
答:用前面提到的,记录附加信息》记录头信息》下一个记录位置。
许多用户记录通过
下一个记录位置
形成单向链表。这样就可以从前往后查到所有的记录了。4。最大和最小记录
从上面可以看到,在数据页面上,如果有多条用户记录,它们通过
连接到下一个记录位置
。但是有一个问题:如果我们能找到最大记录和最小记录怎么办?
这需要在保存用户记录时保留最大和最小记录。
最大记录存储在 Supremum 记录中。
在 Infimum 记录中保存最少的记录。
存储用户记录时,数据库会自动创建两条附加记录:Supremum 和 Infimum。关系如下图所示:
从图中可以看出,用户数据从最小的记录开始,传递到下一个记录位置,从小到大逐步查找,最后找到最大的记录。
5。页目录
为了提高扫描所有用户数据页的效率,InnoDB创建了一个类似于书籍目录的页目录。
页目录过程:
- 将所有正常记录(包括最大和最小记录,不包括标记为已删除的记录)分为几组。
- 每组的最后一条记录头信息(即该组中最大的记录)列出了该组中的记录数。
- 分别从每组最后一条记录中提取偏移地址,并按顺序存储在靠近页尾的地方。这个地方称为页面目录。页目录中的这个地址偏移量称为槽,因此页目录是由槽组成的。
可以看到页面的目录由很多槽位组成。如下图所示:
这样就可以通过二分查找的方式快速找到需要查找的记录。
注意:
最小记录所在组只能有1条记录,最大记录所在组的记录数只能在1到8之间,其余组的记录数只能是 4 到 8 项。
6。文件头和尾部
6.1 文件头
通过行记录
中下一条记录的位置
和
页目录速度太快,无法记录但是有一个前提,即也就是说,用户记录必须存在于同一数据页上。
用户记录较多,在第一个数据页找不到自己想要的数据,需要到另一个页面查找怎么办?
现在,您需要使用
头文件
。头文件包含了页面的一些通用信息,包含固定的38字节。它由以下内容组成:
innodb 通过页码、上一页码、下一页码来连接不同的数据页。如下图所示:
不同数据页之间,上一页页码和下一页页码构成双向链表。这样就可以从前到后逐页查找所有数据。
另外,页面类型也是一个非常重要的栏目,它包含的类型很多。比较著名的有:数据页、索引页(目录入口页)、溢出页、撤消日志页等。从数据页。如果数据有更新,需要刷新到磁盘。
如果程序刷新到磁盘时出现异常,例如:进程死掉,那么只能更新部分数据。我们如何确定最后一次更新数据何时完成?
这需要在文件末尾添加
这列出了页面中的
校验和
。检查页是在数据刷新到磁盘之前计算的。如果稍后更新数据,则会计算新值。该校验和也将记录在文件头中。由于文件头在前面,所以会先刷新到磁盘。
接下来刷新用户记录到磁盘时,比如再次更新该段,程序就不正常了。此时,文件末尾的校验和仍然是旧值。数据库会验证文件末尾的校验和不等于文件头部的新值,表明数据页中的数据不完整。
7。页头
通过上述内容,可以方便地访问数据页,但是还有一个更重要的问题,就是记录的状态信息。
出于性能原因,数据页状态信息被收集并存储在页头
中。
页面标题由14部分组成,共56字节。它由以下内容组成:
摘要
多个数据页通过
页码
形成双向链表。在每个数据页上的数据行之间,由下一个记录位置
创建一个单链表。整体架构图如下:来源:Data and People
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。