扒一扒InnoDB数据在硬盘上是如何存放的

前言

hello,小伙伴们下午好。俗话说的好,一时拖更一时爽,一直拖更一直爽。可是今天良心发现了,决定要更一下。高能预警,为了还债,特意写了篇长文。数据库


索引组织表

在InnoDB存储引擎中,表都是按照主键顺序组织存放的,这种存储方式的表被称为索引组织表。bash

在InnoDB中,每张表都有各自的主键(Primary Key),若是在建立表的时候显式的定义主键,则InnoDB存储引擎会按以下方式选择或建立主键。post

  • 首先判断表中是否有非空的索引,若是有则第一个定义的非空索引做为主键
  • 若是不符合上述条件,InnoDB存储引擎自动建立一个6个字节大小的指针
这样的描述太干瘪啦,咱们来动手操做下。

1.选择第一个定义的非空索引

首先,咱们建立表student,并填充两条测试数据,语句以下:性能

create table student(
a int ,
b int not null,
c int not null,
UNIQUE key (a),
UNIQUE key (c),
UNIQUE key (b)
);
insert into student select 1,2,3;
insert into student select 4,5,6;复制代码

运行结果以下,咱们能够看出_rowid的值等于列c的值,那就说明当前存储的结构是将c做为主键的。另外a是能够为空的,虽然他定义惟一键的是第一个,但仍然不会做为主键。b虽然是先定义列,可是定义惟一键是在c以后,因此也不会被做为惟一键。测试


2.自动建立6个字节大小的指针

首先,咱们建立表score,并填充两条测试数据,语句以下:
ui

create table score(
a int ,
b int ,
c int,
UNIQUE key (a),
UNIQUE key (c),
UNIQUE key (b)
);
insert into score select 1,2,3;
insert into score select 4,5,6;复制代码

运行结果以下,直接报错了,不认为_rowid是他的列名,由于没有知足条件的列能成为他的主键,因此其就自动建立指针来解决问题啦。spa


InnoDB的逻辑存储结构(总体)

表空间

表空间能够看作是InnoDB存储引擎逻辑结构的最高层,因此的数据都存放在表空间里面。.net

在默认状况下,InnoDB存储引擎有一个共享表空间ibdata1,即全部数据都存放在这个表空间里面。固然也能够在my.ini参数文件中启用innodb_file_per_table,则每张表的数据就能够单独放在一个表空间中。设计

注意:即便启用innodb_file_per_table参数,一些回滚信息,系统事务信息等仍是在共享的表空间中,其随着时间的变化大小仍是会不断增大的。单独的表空间只会存放一些数据及索引信息。3d

在InnoDB存储引擎中,对段的管理都是由引擎自身所完成的,DBA不能也不必对其进行控制。

区是由连续页组成的空间,在任何状况下每一个区的大小都是1MB,页的大小为16kb,因此一个区一共有64个连续的页。

下面详细描述。

下面详细描述。

InnoDB行记录格式(重点)

InnoDB存储引擎和大多数数据库同样,记录是以行的形式存储的,这就意味着页中保存着表中的一行行数据。那么问题就来了,他这一行数据是包括哪些部分,除了具体的数据,还有其余的一些额外信息吗?

首先,咱们先来看一下若是没有指定行格式,其默认的行格式是什么?

新建表test,拥有的字段a,b ,c 。

create table test(
a varchar(10),
b varchar (10) not null,
c char(10)
);
insert into test values( '001','Andy','compter');
insert into test values( '002','Bob',NULL);复制代码

以下图所示,默认的行格式是Compact ,该行格式是在MySQL5.0中引入的,其设计目标是高校的存储数据。简单来讲,一个页存放的行数据越多,其性能越高。针对这个描述,咱先放在一边,以后看到其余的行格式,咱对比着看,为啥compact性能高?


下图为行格式Compact的大概结构,先瞅一眼,主要分为两个部分,额外信息和真实数据。额外信息包括变成字段长度,NULL值列表,记录头信息,真实数据即为该行记录有多少列,每列数据有哪些。其中记录头信息包括记录删除位,记录类型,下一个指针的位置。

注意,记录头信息还有不少为其余信息,可是重要的就这几个。是否是不知道他们是干撒的,一脸懵逼中,没事,这仍是混个脸熟,下一部分来慢慢盘他。


变长字段长度

MySQL支持一些变长的数据类型,如varchar,text,blob,变长长度存储多少字节的数据是不固定的,因此咱们在存储真实数据的时候,须要将这些数据占用的字节数也要存储起来。

刚才咱们新增了两条数据,先拿第一个数据为例,将真正数据占用的字节长度都存放在记录的开头部位,从而造成一个变长字段长度列表,逆序存放。以下图,因此最终第一条记录存放的十六进制为08 04 03,他们之间没有空格,是为了显示的效果才加了空格。那第二条记录很明显是03 03.

注意:若是表中没有变长字段,则该字段不存在。


NULL值列表

咱们知道表中的某些列可能存储NULL值,若是这些NULL值放在记录的真实数据中存储会占用空间,因此Compact将这些值为NULL的列统一管理起来,存储在NULL表中。

注意:跟变长字段同样,若是表中没有NULL值的列,则该字段不存在。

注意:MySQL规定NULL值列表必须是整数个字节的位表示,若是使用的二进制位歌手不是整数个字节,则在字节的高位补0.

第一行数据虽然没有NULL值,可是a,c是可能存储NULL值的列,因此NULL值列表以下,0表示列所对应的值不为NULL,1表示列所对应的值为NULL。


第二行数据a不是NULL,c是NULL,因此对应的NULL值列表以下。


记录头信息

  • 记录删除位(delete_mask):0为未删除,1为删除
  • 记录类型(record_type):0表示普通记录,1表示B+树非叶子节点记录,2表示最小记录,3表示最大记录。
  • 下一个指针的位置(next_record):表示从当前记录的真实数据到下一条记录的真实数据之间的地址偏移量。好比第一条记录的next_record为20,那么意味从第一条记录的真实数据的地址处向后找32个字节即是下一条记录的真实数据。实际上就是链表结构。

真实数据

InnoDB数据页结构(重点)

下图为数据页的总体结构,咱先了解个大概,再慢慢盘他。


文件头(File Header)

页的一些通用信息,包括该页属于哪一个表空间,InnoDB存储引擎页的类型。

页头(Page Header)

记录数据页的状态信息。

最小记录+最大记录(Infimum+supermum)

在InnoDB存储引擎中,每一个数据页都有两条虚拟的行记录,用来限定记录的边界。

Infimum记录是指比该页中任何主键值都要小的值,Supermum记录是指比该页中任何主键值都要大的值。

这两个值在页建立时都会被的建立,而且在任何状况下不会被删除。

这两条记录的构造十分简单,都是有5个字节大小的记录头信息和8个字节的固定部分组成的。

注意:上面提到了最小记录的record_type为2,最大记录的record_type为3。

用户记录(User Records)

重点来了,这边是数据实际存储位置,上面已经针对行格式作了详细的划分,如今咱就长话短说啦。

若是我删除了第二行记录,这条记录并非马上删除了,只是将删除记录位改成1啦。而且将他前面一条数据的指针指向他后面一条数据的地址,从而跳过这一条数据。

至于为何会这样作呢?是为了节约时间和空间的消耗。若是在删除的时候,马上从磁盘上移除,那么其余记录在磁盘上从新排列须要性能消耗,因此在删除的时候,只会将全部被删除的记录组成一个垃圾链表,稍后操做。或者有新纪录插入的时候,覆盖掉刚才的存储空间。




空闲空间(Free Space)

不重要。

页面目录(Page Directory)

咱们如今已经找到记录在页面中按照主键由小到达顺序组成一个单链表,那若是想根据主键查询页中的某条记录怎么办?

最蠢的方法确定是按单链表的顺序从头至尾的查找,由于只有知道前面一条记录的记录的地址,才能根据指针找到下一条记录。可是这个有个明显的缺点,就是太慢了,若是有1000条数据,一个个的查询,若是最后一条记录才知足条件,那就太浪费时间啦。

咱们能够先从顺序表中想一想,若是顺序表中要找一个记录,咱们除了从头开始查以外,还能够采用二分法,能够提高查询速度。

那么在单链表中是否能够采用二分法呢?答案是确定的。即采用目录的形式,将全部的记录划分为多个记录块,而后取每一个记录块的最大的值,将其组成一个目录,在查找的时候,先查目录,能判断在哪一个区间内。这个过程就相似于在书中找到某一个概念,要从目录先找同样。

文章尾部(File Tailer)

不重要。哈哈哈。

结束

码字不易,请多多关注哦。

写在最后

该篇借鉴的博文,书籍包括以下,特此感谢!

《MySQL技术内幕——InnoDB存储引擎》

MySQL是如何运行的

blog.csdn.net/u010922732/…

juejin.im/post/5cb3e3…

相关文章
相关标签/搜索