InnoDB逻辑存储结构

InnoDB逻辑存储结构

在这里插入图片描述

表空间(tablespace)

​ 表空间指的是数据表在硬盘上的存储空间,也即我们所有的数据和数据表最终都是落在磁盘上的ibd文件中。在默认情况下所有表的数据都存在共享表空间中,也就是在默认情况下,InnoDB就一个ibdata1文件。然而每个表的数据也可以放在独占表空间中,也推荐大家使用独占表空间,因为在增删数据库的时候,ibdata1文件不会自动收缩。而且如果使用独占表空间,每个innodb表数据目录下都会有一个.ibd文件,用来存放对应表的数据和索引,这样的话在数据库崩溃时救援、恢复比较方便。

​ 修改表空间管理方式:

  • 只需修改innodb_file_per_table这个参数即可, show variables like innodb_file_per_table 可查看当前表空间管理方式,on表示为独立表空间;off表示为共享表空间
  • 修改innodb_file_per_table的参数值即可,但是修改无法影响之前已经使用过的共享表空间和独立表空间;
  • innodb_file_per_table=1 为使用独占表空间
  • innodb_file_per_table=0 为使用共享表空间

段(segment)

​ 在表空间下我们能看到还能细分出段,大致有如下,而且在InnoDB中,段由存储引擎自动管理

  • Leaf node segment :叶子节点段,这个段是专门存放B+树叶子节点的段,而我们都知道在B+树中叶子节点保存的都是数据,所以该段也称数据段。
  • Non-Leaf node segment :非叶子节点段,这个段中存放着B+树的非叶子节点,也即索引,所以也称索引段。
  • Rollback segment : 回滚段,用来临时的保存当数据库数据发生改变时的先前值。

区(Extent)

​ 区是由连续的页组成的空间,一个区中默认存放着64个page,每个page默认是16KB,所以默认情况下,一个区的大小为1MB。一般来说每次申请磁盘空间时不会1兆1兆的申请,这样效率太低了,所以一般一次性从磁盘申请45个区,也就是说,每一次ibd文件增长,一次就要增长45MB,同时也避免了区在磁盘中存在碎片化。

页(page)

​ 页是InnoDB中磁盘读写的最小逻辑单位,默认是16KB,而一个数据页就是一个B+树的叶子节点(B+ Tree Node),那么为什么页的默认大小是16KB呢,先前我的文章对索引的理解中也有提过比如磁盘,在申请的时候都会有一个最小的逻辑读写单位,而页的大小充分考虑了机械硬盘和SSD的最小单元(512B和4KB)。这样的话,页的大小是机械硬盘和SSD最小单元的倍数,在磁盘读写中才是高效、没有浪费的。

  • 为什么页不能太大,比如16MB?

    因为过大的页对于磁盘的读写来说压力非常大,在InnoDB中,页是磁盘读写的最小单位,而如果每次都是16MB的磁盘读写,对于磁盘来说是非常吃力的,而且每一次的浪费都非常大,因为有可能我们读这么大的页读到内存中然而只需要读取一条数据,那么这16MB的数据在内存中就成为垃圾了;再者,一个16MB的页里面也即有16MB左右的链表,试想一下16MB的链表的数据得有多大,而且查找起来的效率远不比B+树。

​ 每个页在区中是连续排布的,那如果是连续排布的情况下要怎么组成B+树呢,拿下图举例

在这里插入图片描述

实际上一个区中排布的方式并不止图上所显示的数量,物理上排布方式默认应是8x8,而每页都有他下层节点的指针,通过这个指针就能找到下层节点的位置。所以可能在物理结构上看他们是连续排布的,但逻辑上可能相邻的两个页中是没有什么关系的,最终都是以指针找到下层节点。

​ 值得一提的是,InnoDB中的页是InnoDB自身的逻辑概念,与硬件的页无关,InnoDB并不关心自己在硬件是怎么存储的。

行(Row)

InnoDB中的变长列

长度不固定的数据类型:

  • varchar、varbinary、blob、text

占用空间大于768Byte的变长列:

  • Char(在InnoDB中,当Char的长度大于768B的时候会自动变成变长列)

变长编码下的Char:

  • 所谓变长到底是指硬盘存储空间的变化还是字符串长度的变化呢?从理论上讲,所谓变长列应该指的是磁盘存储空间的变化,如varchar,他的字符串有可能长,有可能短,所以在硬盘存储空间中也有可能长有可能短,所以称变长列(字段),而在UTF-8这种编码下一个字符的所占的空间是不固定的,所以在UTF-8或者其他变长编码下的Char也为变长列。

行溢出数据

​ 由于InnoDB每个数据页容量有限,导致数据字段也是有限的。而在当数据段过大时,InnoDB会使用行溢出机制,它会将超长的字段放入单独开辟的数据页中。

在这里插入图片描述

​ 我们可以看到,在一个数据行中,当一个数据段过大时,就会将超长的字段存入到Blob页中,这样就避免了在一个数据页中页数据量过少,因为如果数据段过大,这个数据行中只能存放一个数据段得话,这个效率就会退化到二叉树了,所以在InnoDB中做了这么一种操作来使得页数据量恢复,从而能维持B+树的在硬盘读写的优势。但是如果就是要查询那个大数据段,就只能再多加一步从利用该数据段指向Blob页的指针查找到对应的数据。

InnoDB行记录格式

​ InnoDB行记录格式主要分为两个时代:

  • Redundant / Compact (Antelope文件格式)
  • Dynamic / Compressed (Barracuda 文件格式)
Redundant

​ MySQL 5.0之前的InnoDB默认行记录格式,主要物理结构如下:("|"实际不存在,都是直接挨着的)

​ 字段偏移列表 | Header | RowID | TxID | Roll Pointer | Col 1 | Col 2 | … | Col n

  • 字段偏移列表

    用来记录每个字段的相对位置

    按照列的顺序逆序放置,因为实际中每个字段是直接挨着的,如果没有记录,尤其是对于变长列而言,是无法知道其位置在哪的,比如最后一个字段是空,那么这条行记录最后只会记录到Col n-1,那怎么知道他是不是最后一个呢,所以在这个字段偏移列表中就是记录了该字段偏移了多少到上一个字段。

  • Header

    列数量、字段偏移表的单位、下一行记录的指针等信息。

  • RowID

    没有可用主键时,使用RowID作为隐藏主键

  • TxID(Transaction ID)

    事务ID

  • Roll Pointer

    回滚指针

  • 未溢出的数据

    前768Byte数据 | Blob页指针

​ 那么这种行记录格式问题在哪呢,如果我们存储的是变长列,存在字段偏移列表中的字段偏移量能帮我们找到数据段在什么位置,但是如果我们用的是定长列呢,那这个字段偏移量就显得冗余了。所以发展除了Compact,就只是为了解决这个问题。

Compact

​ 物理结构:

​ 变长字段长度表 | Null标志位 | Header | RowID | TxID | Roll Pointer | Col 1 | Col 2 | … | Col n

  • 变长字段长度表

    记录每个变长字段的长度

    按照列的顺序逆序放置

  • Null标志位

    一个bitmap,指示记录中的Null值

    每个bit代表一个字段

  • 后面的与Redundant一致

​ 这么一看好像已经挺好的了,但为什么仍不采用这种方式呢?当我们要去查询一个有溢出数据的数据段时,我们已经确定要去查Blob页了,那么那768Byte的数据存在Col里还有什么意义呢,直接全部放到Blob里不就好了,还省出了一大片空间。由此发展出了我们现在最常用的Dynamic。

Dynamic

​ Dynamic的物理结构和Compact一样,唯一不同的地方在于在Dynamic中,数据小于40Byte才算未溢出,数据全部放到行中,但如果大于了40Byte,就全部放到Blob页中,原来的地方指记一个Blob页指针。

Compressed

​ 为了进一步节约空间,发展出了Compressed,他的物理结构与Dynamic类似,而他的不同在于:

  • 对表的数据行使用zlib算法进行了压缩存储
  • 可以节约40%左右空间,但对CPU的压力比较大
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值