天天看点

innodb是如何存数据的

文章目录

  • ​​1.磁盘or内存​​
  • ​​1.1 磁盘​​
  • ​​1.2 内存​​
  • ​​2.数据页​​
  • ​​3.用户记录​​
  • ​​3.1 额外信息​​
  • ​​3.1.1 变长字段​​
  • ​​3.1.2 null值列表​​
  • ​​3.1.3 记录头信息​​
  • ​​3.2 隐藏列​​
  • ​​3.3 真正数据列​​
  • ​​3.4 用户记录是如何相连的​​
  • ​​4.最大和最小记录​​
  • ​​5.页目录​​
  • ​​6.文件头部和尾部​​
  • ​​6.1 文件头部​​
  • ​​6.2 文件尾部​​
  • ​​7.页头部​​
  • ​​8.总结​​

innodb底层是如何存储数据的

innodb是如何存数据的

1.磁盘or内存

1.1 磁盘

数据对系统来说是非常重要的东西,比如:用户的身份证、手机号、银行号、会员过期时间、积分等等。一旦丢失,会对用户造成很大的影响。

把数据存在磁盘上。

但是IO请求是比较耗时的操作,如果频繁的进行IO请求势必会影响数据库的性能。

1.2 内存

内存可以存储一些用户数据,但无法存储所有的用户数据,因为如果数据量太大了,它可能还是存不下。

2.数据页

写操作时,先将数据写到内存的某个批次中,然后再将该批次的数据一次性刷到磁盘上。如下图所示:

innodb是如何存数据的

读操作时,从磁盘上一次读一批数据,然后加载到内存当中,以后就在内存中操作。如下图所示:

innodb是如何存数据的

数据页主要是用来存储表中记录的,它在磁盘中是用双向链表相连的,方便查找,能够非常快速得从一个数据页,定位到另一个数据页。

一个表在磁盘中可能存放在多个数据页当中。

innodb是如何存数据的

数据页默认的大小是16kb。innodb_page_size,来重新设置大小。不过,一般情况下,用它的默认值就够了。

数据页:

innodb是如何存数据的

文件头部

页头部

最大和最小记录

用户记录

空闲空间

页目录

文件尾部

3.用户记录

用户记录是innodb的重中之重,我们平时保存到数据库中的数据,就存储在它里面。

其实在innodb支持的数据行格式有四种:

compact行格式

redundant行格式

dynamic行格式

compressed行格式

compact:

innodb是如何存数据的

一条用户记录主要包含三部分内容:

  • 记录额外信息,它包含了变长字段、null值列表和记录头信息。
  • 隐藏列,它包含了行id、事务id和回滚点。
  • 真正的数据列,包含真正的用户数据,可以有很多列。

3.1 额外信息

额外信息并非真正的用户数据,它是为了辅助存数据用的。

3.1.1 变长字段

有些数据如果直接存会有问题,比如:如果某个字段是varchar或text类型,它的长度不固定,可以根据存入数据的长度不同,而随之变化。

如果不在一个地方记录数据真正的长度,innodb很可能不知道要分配多少空间。假如都按某个固定长度分配空间,但实际数据又没占多少空间,岂不是会浪费?

所以,需要在变长字段中记录某个变长字段占用的字节数,方便按需分配空间

3.1.2 null值列表

数据库中有些字段的值允许为null,如果把每个字段的null值,都保存到用户记录中,显然有些浪费存储空间。

有没有办法只简单的标记一下,不存储实际的null值呢?

答案:将为null的字段保存到null值列表。

在列表中用二进制的值1,表示该字段允许为null,用0表示不允许为null。它只占用了1位,就能表示某个字符是否为null,确实可以节省很多存储空间。

3.1.3 记录头信息
innodb是如何存数据的

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,即回滚点,它用于事务回滚。连接undolog的

如果表中有主键,则用主键做行id,无需额外创建。如果表中没有主键,假如有不为null的unique唯一键,则用它做为行id,同样无需额外创建。

如果表中既没有主键,又没有唯一键,则数据库会自动创建行id。

也就是说在innodb中,隐藏列中事务id和回滚点是一定会被创建的,但行id要根据实际情况决定。

3.3 真正数据列

真正的数据列中存储了用户的真实数据,它可以包含很多列的数据。这个比较简单,没有什么好多说的。

3.4 用户记录是如何相连的

undolog

innodb是如何存数据的

表头为最新的数据,表尾为最旧的数据

4.最大和最小记录

如果才能快速找到最大的记录和最小的记录呢

最大记录保存到Supremum记录中。

最小记录保存在Infimum记录中

innodb是如何存数据的

所以表头为最大的记录,表尾为最小记录

5.页目录

页目录,说白了就是把一页用户记录分为若干组,每一组的最大记录都保存到一个地方,这个地方就是页目录。每一组的最大记录叫做槽。

innodb是如何存数据的

假设一页的数据分为4组,这样在页目录中,就对应了4个槽,每个槽中都保存了该组数据的最大值。

这样就能通过二分查找,比较槽中的记录跟需要找到的记录的大小。如果用户需要查找的记录,小于当前槽中的记录,则向上查找上一个槽。如果用户需要查找的记录,大于当前槽中的记录,则向下查找下一个槽。

6.文件头部和尾部

6.1 文件头部

通过前面介绍的行记录中下一条记录的位置和页目录,innodb能非常快速的定位某一条记录。但有个前提条件,就是用户记录必须在同一个数据页当中。

这时就需要使用文件头部了。

  • 页号
  • 上一页页号
  • 下一页页号
  • 页类型
innodb是如何存数据的

不同的数据页之间,通过上一页页号和下一页页号构成了双向链表。这样就能从前向后,一页页查找所有的数据了。

此外,页类型也是一个非常重要的字段,它包含了多种类型,其中比较出名的有:数据页、索引页(目录项页)、溢出页、undo日志页等。

6.2 文件尾部

数据库的数据是以数据页为单位,加载到内存中,如果数据有更新的话,需要刷新到磁盘上。

如果某一天比较倒霉,程序在刷新到磁盘的过程中,出现了异常,比如:进程被kill掉了,或者服务器被重启了。

这时候数据可能只刷新了一部分,如何判断上次刷盘的数据是完整的呢

文件尾部有校验和,在数据刷新到磁盘之前,都会计算一个校验和,后面如果数据更新的话,会计算一个新值。文件头部也会记录这个校验和,由于头部在前面,会被刷新到磁盘上。

然后,刷新用户记录到磁盘的时候,假设刷新了一部分,出现异常了,这个时候文件尾部的校验和,还是一个旧值,数据库会去校验文件尾部的校验和,不等于文件头部新生成的校验和,那么就代表数据是不完整的。

7.页头部

页头部记录了状态信息。

比如一页数据保存多少记录,页目录有多少个slot。

不过为了性能,这些信息都保存在页头部。

页头部记录的信息:

1.已删除记录所占的字节数

2.最后插入记录的位置

3.最大事务id

4.索引id

5.索引层级

8.总结