InnoDB的存储结构

索引组织表

在InnoDB存储引擎中,表都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(index organized table)。
在InnoDB存储引擎表中,每张表都有个主键(Primary Key),如果在创建表时没有显式地定义主键,则InnoDB存储引擎会按如下方式选择或创建主键。

  1. 首先判断表中是否有非空的唯一索引,如果有,则该列即为主键
  2. 如果不符合上述条件,InnoDB存储引擎自动创建一个6字节大小的指针

当表中有多个非空唯一索引时,InnoDB存储引擎将选择建表时第一个定义的非空唯一索引为主键。

InnoDB逻辑存储结构

在InnoDB存储引擎中,所有的数据都被逻辑地存放在一个空间中,称之为表空间(tablespace)。表空间又由段(segment)、区(extent)、页(page)组成。页在一些文档中也成为块(block),InnoDB存储引擎的逻辑存储结构如下图。

表空间

表空间可以看做是InnoDB存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。默认情况下InnoDB存储引擎有一个共享表空间ibdata1,所有的数据都存放在这个表空间中。如果用户开启了参数 innodb_fiule_per_table ,则每张表内的数据可以单独放在一个表空间中。
即使启用了 innodb_fiule_per_table ,每张表的表空间内从存放的也只是数据、索引和插入缓冲Bitmap页,其他类型的数据比如回滚(undo)、插入缓冲索引页、系统事务信息、二次写缓冲(Double write buffer)等还是存放在原有共享表空间。

上面的图已经显示了表空间是由各个段组成的,常见的段有数据段、索引段、回滚段等。因为前面已经介绍过了InnoDB存储引擎表是索引组织(index organized)的,因此数据即索引,索引即数据。那么数据段即B+树的叶子节点(图中的Leaf node segment),索引段即B+树中的非叶子节点(图中的Non-leaf node segment)。回滚段较为特殊。在InnoDB存储引擎中,对段的管理都是由引擎自身所完成。

区是由连续的页组成的空间,在任何情况下,每个区的大小都为1MB。为了保证区中页的连续性,InnoDB存储引擎一次从磁盘中申请4~5个区。在默认情况下,InnoDB存储引擎页的大小为16KB,即一个区中一共有64个连续的页。
InnoDB 1.0.x 版本开始引入压缩页,即每个页的大小可以通过 KEY_BLOCK_SIZE 设置为2K、4K、8K,因此每个区对应页的数量就应该是512、256、128。
InnoDB 1.2.x 版本新增了参数 innodb_page_size,通过该参数可以将默认页的大小设置为4K、8K,但是页中的数据库不是压缩。这是区中页的数量同样页是256、128。总之,不论页的大小怎么变化,区的大小总为1M。
但是有这样一个问题:在开启独立表空间之后,创建的表默认大小是96K,区中是64个连续的页,创建的表空间应该是1M才对呀?这是因为在每个段的开始时,先用32个页大小的碎片页(fragment page)来保存数据,在使用完这些页之后才是64个连续的页的申请。这样做是对于一些小表或者undo这类的段,可以在开始时申请较少的空间,节省磁盘容量的开销。

同大多数数据库一样,InnoDB有页(Page)的概念(也可以称之为块),页是InnoDB磁盘管理的最小单位。
默认大小为16K,可以通过 innodb_page_size 将页的大小设置为4K、8K、16K,则所有表中页的大小都为设置值,不可以对其再次修改。除非通过mysqldump导入和导出操作来产生新的库。

在InnoDB存储引擎中,常见的页类型有:

  • 数据页(B-tree Node)
  • undo页(undo Log Page)
  • 系统页(System Page)
  • 事务数据页(Transaction system Page)
  • 插入缓冲位图页(Insert Buffer Bitmap)
  • 插入缓冲空闲列表页(Inser Buffer Free List)
  • 未压缩的二进制大对象页(Uncompressed BLOB Page)
  • 压缩的二进制大对象页(compressed BLOB Page)

InnoDB存储引擎是面向行的(row-oriented),也就是说数据是按行进行存放的。每个页存放的行记录也是有硬性定义的,最多运行存放(16K/2-200)行的记录,即7992行记录。

InnoDB物理存储结构

行记录格式

InnoDB存储引擎是面向行的(row-oriented),即数据是按照行存放在Page中。InnoDB支持4中行记录格式,REDUNDANT、 COMPACT、COMPRESS、DYNAMIC。可以通过show table status。
MySQL 5.7中的默认行记录格式为Dynamic。

Compact

Compact是在MySQL 5.0中引入的一种高效的行记录格式,其目标是高效的存储数据,较REDUNDANT能够减少20%左右的空间占用,但代价是会增加某些操作的CPU占用率。

整体结构

变长字段长度列表

Compact行记录格式的首部是变长字段长度列表,存储着非NULL变长字段的实际长度。因为MySQL中VARCHAR类型的最大长度不能超过65535,所以每个字段长度最多用2个字节就可以表示。

NULL标志位

变长字段长度列表的后边是NULL标志位,表示了可以为NULL的列中,哪些列实际为NULL,若为NULL则该位标记为1。该部分的长度依赖于该表中可以为NULL列的个数,譬如,该表定义中有9列可以为NULL,那么需要2个字节的比特向量才能表示9个列的NULL情况。
实际为NULL的列(无论是VARCHAR还是CAHR)除了在NULL标志位中占用一个比特位外,不会再占用其它空间。

记录头信息

占用5个字节,记录着改行记录的基本信息。

实际数据

记录头信息的后面开始存储实际每列的数据,实际为NULL的列不占据空间。除了用户定义的列外,还有两个隐藏列:事务ID和回滚指针列。如果没用定义主键,那么还会有一个6字节大小的rowid列。

不同类型的数据如何存储

CHAR

对于固定长度的CHAR类型,缺少的字符会以0x20填充。
由于CHAR(N)中的N指的是字符个数,对于变长字符集(比如UTF-8),其实际字节长度不是定长数据。此时,InnoDB将其视为变长字符类型,在变成字符长度列表中也会记录CHAR类型字段的实际长度。因此,在使用变长字符集的情况下,CHAR和VARCHAR在存储上没有区别。

VARCHAR

VARCHAR(N)中的N指的是字符个数,VARCCHAR的最大字节长度不能超过65532(较65535小,因为有其它开销)。例如,在UTF-8符集下,N的最大值要在最大字节长度上再除以3。

PS:

MySQL中的UTF-8字符集并不是标准的UTF-8,其最多支持3个字节,而标准的UTF-8使用1-4字节来进行字符编码,故MySQL又推出了UTF-8 mb4来进行弥补。

行溢出数据

一般情况下,行数据都是放在页中的,但是,对于一些长度较长的列数据,InnoDB会将存储在数据页外,存储在Uncompress BOLB页中。VARCHAR、BLOB、TEXT都可能发生行溢出,但长度较短时,即使是BOLB也可能完全存储在页内。
是否发生行溢出,取决于页的大小和行的大小,InnoDB会保证一个数据页至少存储两行记录。
当生行溢出时,在Compact行格式下,数据页中会存储前768个字节,然后再存储一个20字节的指针,指向行溢出页。

在Dynamic和Compressed行记录格式下,会采用完全行溢出的方式,在数据页只存在指针,不再存放列的前缀字节。

Dynamic和Compressed行记录格式

新的两种行记录格式对于存放在BLOB中的数据采用了完全的行溢出方式,在数据页中只存放20个字节的指针,实际的数据都存放子页OffPage中。而之前的两种行记录格式都是会存放768个前缀字节。新的行溢出方式如下:


Compressed行记录格式的另一个功能就是:存储在其中的行数据会以zlib的算法进行压缩。因此对于BLOB、TEXT、VARXCHAR这些大长度类型的数据能够非常有效的存储。

数据页结构

页是InnoDB管理数据库的最小磁盘单位。
InnoDB数据页由以下七部分组成

File Header:文件头

File Header用来记录页的一些头信息,共由如下8部分组成,共占用38字节:

InnoDB页的类型

Page Header:页头

该部分用来记录数据页的状态信息,由14个部分组成,共56字节:

Infimum和Supremum Record

  • 在InnoDB中,每个数据页都有两个虚拟的行记录,用来限定记录的边界。
  • Infimum记录是比该页中任何主键值都要小的值。
  • Supremum指比任何可能大的值还要大的值。
  • 这两个值在页创建时被建立,且在任何情况下都不会被删除。

User Record和Free Space

  • User Record,用户记录,即行记录。
  • Free Space,空闲空间。是个链表数据结构。在一条记录被删除后,该空间会被加入到空闲链表中。

Page Directory:页目录

  • Page Directory中存放了记录的相对位置,有时将这些记录指针称为Slots(槽)或Directory Slots(目录槽)。
  • InnoDB中并不是每个记录都拥有一个槽,InnoDB的槽是一个稀疏目录,即一个槽中可能包含多个记录。当记录被插入或删除时,需要对槽进行分裂或平衡的维护操作。
  • 在槽中记录按照索引键信息顺序存放,这样可以利用二叉查找迅速找到记录的指针。
  • 需要注意的是:B+树索引本身并不能找到具体的一条记录,能找到只是该记录所在的页。数据库把页载入到内存,然后通过Page Directory再进行二叉查找。只不过二叉查找的时间复杂度很低,同时在内存中查找很快,因此通常忽略这部分时间。

File Trailer:文件结尾信息

为了检测页是否已完整地写入磁盘(如写入时可能发生磁盘损坏、机器关机等),InnoDB设置了File Trailer来保证页的完整性。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇