mysql 学习 - InnoDB的页

InnoDB是一个将表中的数据存储到磁盘上的存储引擎,所以即使关机后重启我们的数据还是存在的。而真正处理数据的过程是发生在内存中的,所以需要把磁盘中的数据加载到内存中,如果是处理写入或修改请求的话,还需要把内存中的内容刷新到磁盘上.


不论是读取数据还是写入数据, InnoDB 引擎是以页为单位操作的. 将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为 16 KB。也就是在一般情况下,一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中

页内容组成:

一个页中至少存放两行记录.


每个页除了存放我们的记录以外,也需要存储一些额外的信息,乱七八糟的额外信息加起来需要 132 个字节的空间, 其他的空间都可以被用来存储记录。


每个记录需要的额外信息是 27 字节 (2 条记录则是 2*27)
包括:
2个字节用于存储真实数据的长度
1个字节用于存储列是否是NULL值
5个字节大小的头信息
6个字节的row_id列
6个字节的transaction_id列
7个字节的roll_pointer列


剩下的空间除以 2, 是单条记录在不是溢出列时的最大长度 , (16384 - 132 - 2*27)/2 = 8099

数据页(索引页)

user records 与 Infimum + Supremum

关于 Infimum + Supremum:


不管我们向页中插入了多少自己的记录,InnoDB 都规定他们定义的两条伪记录分别为最小记录最大记录.


在页的 7 个组成部分中,我们自己存储的记录会按照我们指定的行格式存储到 User Records 部分。但是在一开始生成页的时候,其实并没有 User Records 这个部分,每当我们插入一条记录,都会从 Free Space 部分,也就是尚未使用的存储空间中申请一个记录大小的空间划分到 User Records 部分,当 Free Space 部分的空间全部被 User Records 部分替代掉之后,也就意味着这个页使用完了,如果还有新的记录插入的话,就需要去申请新的页了,这个过程的图示如下:

详解

首先创建一个表:

mysql> CREATE TABLE page_demo(
    ->     c1 INT,
    ->     c2 INT,
    ->     c3 VARCHAR(10000),
    ->     PRIMARY KEY (c1)
    -> ) CHARSET=ascii ROW_FORMAT=Compact;
Query OK, 0 rows affected (0.03 sec)

表的行结构如下:

头信息中的信息:

关于 delete_mask 的补充:


这个属性标记着当前记录是否被删除,占用1个二进制位,值为0的时候代表记录并没有被删除,为1的时候代表记录被删除掉了


(这些被删除的记录之所以不立即从磁盘上移除,是因为移除它们之后把其他的记录在磁盘上重新排列需要性能消耗,所以只是打一个删除标记而已,所有被删除掉的记录都会组成一个所谓的垃圾链表,在这个链表中的记录占用的空间称之为所谓的可重用空间,之后如果有新记录插入到表中的话,可能把这些被删除的记录占用的存储空间覆盖掉。)


将这个delete_mask位设置为 1 和将被删除的记录加入到垃圾链表中其实是两个阶段


插入数据:

mysql> INSERT INTO page_demo VALUES(1, 100, ‘aaaa‘), (2, 200, ‘bbbb‘), (3, 300, ‘cccc‘), (4, 400, ‘dddd‘);
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

关于 next_record 的补充:


文字补充:
从当前记录的真实数据到下一条记录的真实数据的地址偏移量.


下面我们看图说话:
从图中可以看出来,我们的记录按照主键从小到大的顺序形成了一个单链表。最大记录的next_record的值为0,这也就是说最大记录是没有下一条记录了,它是这个单链表中的最后一个节点。如果从中删除掉一条记录,这个链表也是会跟着变化的,比如我们把第2条记录删掉:
DELETE FROM page_demo WHERE c1 = 2;
此时原来的记录变成了

记录的变化为:
第2条记录并没有从存储空间中移除,而是把该条记录的delete_mask值设置为1.
第2条记录的next_record值变为了0,意味着该记录没有下一条记录了。
第1条记录的next_record指向了第3条记录。
最大记录的n_owned值从5变成了4

Page Directory(页目录)

怎么从 page 中查出一条记录? 比如执行 SELECT * FROM page_demo WHERE c1 = 3;这条语句.

innodb 对于查询的优化是使用了 页目录 这一设计. 也就是说每页里面有个跟目录相关的结构, 叫做槽(slot).

分槽以后的示意图如上图所示. 对于最小记录所在的分组只能有 1 条记录,最大记录所在的分组拥有的记录条数只能在 1~8 条之间,剩下的分组中记录的条数范围只能在是 4~8 条之间。
1. 初始情况下一个数据页里只有最小记录和最大记录两条记录,它们分属于两个分组。
2. 之后每插入一条记录,都会从页目录中找到主键值比本记录的主键值大并且差值最小的槽,然后把该槽对应的记录的n_owned值加1,表示本组内又添加了一条记录,直到该组中的记录数等于8个。
3. 在一个组中的记录数等于8个后再插入一条记录时,会将组中的记录拆分成两个组,一个组中4条记录,另一个5条记录。这个过程会在页目录中新增一个槽来记录这个新增分组中最大的那条记录的偏移量。

现在一口气添加数据:

mysql> INSERT INTO page_demo VALUES(5, 500, ‘eeee‘), (6, 600, ‘ffff‘), (7, 700, ‘gggg‘), (8, 800, ‘hhhh‘), (9, 900, ‘iiii‘), (10, 1000, ‘jjjj‘), (11, 1100, ‘kkkk‘), (12, 1200, ‘llll‘), (13, 1300, ‘mmmm‘), (14, 1400, ‘nnnn‘), (15, 1500, ‘oooo‘), (16, 1600, ‘pppp‘);
Query OK, 12 rows affected (0.00 sec)
Records: 12  Duplicates: 0  Warnings: 0

然后刚才的页记录就变成了这样了:

此时如果对页记录中的数据进行查询, 可以首先使用二分法, 找到数据所处于的槽, 然后顺序遍历槽中的元素找到对应的数据.

page header (页面头部)

为了能得到一个数据页中存储的记录的状态信息,比如本页中已经存储了多少条记录,第一条记录的地址是什么,页目录中存储了多少个槽等等,特意在页中定义了一个叫Page Header的部分,它是页结构的第二部分,这个部分占用固定的 56 个字节,专门存储各种状态信息

File Header(文件头部)

File Header针对各种类型的页都通用,也就是说不同类型的页都会以File Header作为第一个组成部分,它描述了一些针对各种页都通用的一些信息,比方说这个页的编号是多少,它的上一个页、下一个页是谁, 这个部分占用固定的 38 个字节

FIL_PAGE_TYPE

这个代表当前页的类型,我们前边说过,InnoDB为了不同的目的而把页分为不同的类型,我们上边介绍的其实都是存储记录的数据页,其实还有很多别的类型的页,具体如下表:

FIL_PAGE_PREVFIL_PAGE_NEXT

InnoDB都是以页为单位存放数据的,有时候我们存放某种类型的数据占用的空间非常大(比方说一张表中可以有成千上万条记录),InnoDB可能不可以一次性为这么多数据分配一个非常大的存储空间,如果分散到多个不连续的页中存储的话需要把这些页关联起来,FIL_PAGE_PREV和FIL_PAGE_NEXT就分别代表本页的上一个和下一个页的页号。这样通过建立一个双向链表把许许多多的页就都串联起来了,而无需这些页在物理上真正连着。需要注意的是,并不是所有类型的页都有上一个和下一个页的属性,不过我们本集中唠叨的数据页(也就是类型为FIL_PAGE_INDEX的页)是有这两个属性的,所以所有的数据页其实是一个双链表,就像这样:

File Trailer

InnoDB存储引擎会把数据存储到磁盘上,但是磁盘速度太慢,需要以页为单位把数据加载到内存中处理,如果该页中的数据在内存中被修改了,那么在修改后的某个时间需要把数据同步到磁盘中。

为了防止在同步过程中出现问题, InnoDB在每个页的尾部都加了一个 File Trailer部分,这个部分由 8 个字节组成,可以分成 2 个小部分:
1. 前4个字节代表页的校验和
2. 后4个字节代表页面被最后修改时对应的日志序列位置(LSN)

总结

  1. InnoDB 为了不同的目的而设计了不同类型的页,我们把用于存放记录的页叫做数据页。
  2. 一个数据页可以被大致划分为7个部分,分别是:

    File Header,表示页的一些通用信息,占固定的38字节。
    Page Header,表示数据页专有的一些信息,占固定的56个字节。
    Infimum + Supremum,两个虚拟的伪记录,分别表示页中的最小和最大记录,占固定的26个字节。
    User Records:真实存储我们插入的记录的部分,大小不固定。
    Free Space:页中尚未使用的部分,大小不确定。
    Page Directory:页中的某些记录相对位置,也就是各个槽在页面中的地址偏移量,大小不固定,插入的记录越多,这个部分占用的空间越多。
    File Trailer:用于检验页是否完整的部分,占用固定的8个字节。

  3. 每个记录的头信息中都有一个next_record属性,从而使页中的所有记录串联成一个单链表。
  4. InnoDB 会把页中的记录划分为若干个组,每个组的最后一个记录的地址偏移量作为一个槽,存放在 Page Directory 中,所以在一个页中根据主键查找记录是非常快的,分为两步:

    通过二分法确定该记录所在的槽。
    通过记录的 next_record 属性遍历该槽所在的组中的各个记录。

  5. 每个数据页的File Header部分都有上一个和下一个页的编号,所以所有的数据页会组成一个双链表。
  6. 为保证从内存中同步到磁盘的页的完整性,在页的首部和尾部都会存储页中数据的校验和和页面最后修改时对应的LSN值,如果首部和尾部的校验和和LSN值校验不成功的话,就说明同步过程出现了问题。

原文地址:https://www.cnblogs.com/it-dennis/p/12607886.html

时间: 2024-10-29 03:31:02

mysql 学习 - InnoDB的页的相关文章

MySQL中InnoDB脏页刷新机制Checkpoint

我们知道InnoDB采用Write Ahead Log策略来防止宕机数据丢失,即事务提交时,先写重做日志,再修改内存数据页,这样就产生了脏页.既然有重做日志保证数据持久性,查询时也可以直接从缓冲池页中取数据,那为什么还要刷新脏页到磁盘呢?如果重做日志可以无限增大,同时缓冲池足够大,能够缓存所有数据,那么是不需要将缓冲池中的脏页刷新到磁盘.但是,通常会有以下几个问题: 服务器内存有限,缓冲池不够用,无法缓存全部数据 重做日志无限增大成本要求太高 宕机时如果重做全部日志恢复时间过长 事实上,当数据库

mysql 学习 - InnoDB的行

InnoDB行 我们平时是以记录为单位来向表中插入数据的,这些记录在磁盘上的存放方式也被称为行格式或者记录格式.InnoDB存储引擎到现在为止设计了4种不同类型的行格式,分别是Compact.Redundant.Dynamic和Compressed行格式 compact(行格式) 记录的额外信息这部分信息是服务器为了描述这条记录而不得不额外添加的一些信息,这些额外信息分为3类,分别是变长字段长度列表.NULL值列表和记录头信息 关于变长字段 变长的数据类型,比如VARCHAR(M).VARBIN

Mysql学习——InnoDB与Myisam

MysqlInnoDB和Myisam两种类型的存储我们在Mysql创建表的时候可以在选项中进行制定,如下图所示: 下面来说说两种存储的区别吧: 1.两种数据存储的事务机制不同 InnoDB支持事务,Myisam不支持,但是在查询方面Myisam的性能略胜一筹 2.锁的机制不同 InnoDB为行级锁,能更大程度的支持并发操作:Myisam是表级锁 3.数据操作方面 修改.新增.删除数据使用InnoDB性能更高,大数据量的查询使用Myisam性能更高 查询数据库条数时InnoDB不保存表中数据的条数

重新学习MySQL数据库2:『浅入浅出』MySQL 和 InnoDB

重新学习Mysql数据库2:『浅入浅出』MySQL 和 InnoDB 作为一名开发人员,在日常的工作中会难以避免地接触到数据库,无论是基于文件的 sqlite 还是工程上使用非常广泛的 MySQL.PostgreSQL,但是一直以来也没有对数据库有一个非常清晰并且成体系的认知,所以最近两个月的时间看了几本数据库相关的书籍并且阅读了 MySQL 的官方文档,希望对各位了解数据库的.不了解数据库的有所帮助. 本文中对于数据库的介绍以及研究都是在 MySQL 上进行的,如果涉及到了其他数据库的内容或者

MySQL学习之——锁(行锁、表锁、页锁、乐观锁、悲观锁等)

锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具.在计算机中,是协调多个进程或县城并发访问某一资源的一种机制.在数据库当中,除了传统的计算资源(CPU.RAM.I/O等等)的争用之外,数据也是一种供许多用户共享访问的资源.如何保证数据并发访问的一致性.有效性,是所有数据库必须解决的一个问题,锁的冲突也是影响数据库并发访问性能的一个重要因素.从这一角度来说,锁对于数据库而言就显得尤为重要. MySQL锁 相对于其他的数据库而言,MySQL的锁机制比较简单,最显著的特点就是不同的存储引擎支持不

更改Innodb 数据页大小优化MySQL

更改Innodb 数据页大小优化MySQL http://www.mysqlsupport.cn/change_innodb_page_size/ 更改Innodb 数据页大小优化MySQL 2009年12月13日 Posted by wubx 作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究.         我们知道Innodb的数据页是16K,而且是一个硬性

我的MYSQL学习心得(十四)

我的MYSQL学习心得(十四) 我的MYSQL学习心得(一) 我的MYSQL学习心得(二) 我的MYSQL学习心得(三) 我的MYSQL学习心得(四) 我的MYSQL学习心得(五) 我的MYSQL学习心得(六) 我的MYSQL学习心得(七) 我的MYSQL学习心得(八) 我的MYSQL学习心得(九) 我的MYSQL学习心得(十) 我的MYSQL学习心得(十一) 我的MYSQL学习心得(十二) 我的MYSQL学习心得(十三) 这一篇<我的MYSQL学习心得(十四)>将会讲解MYSQL的备份和恢复

我的MYSQL学习心得(十四) 备份和恢复

原文:我的MYSQL学习心得(十四) 备份和恢复 我的MYSQL学习心得(十四) 备份和恢复 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运算符 我的MYSQL学习心得(六) 函数 我的MYSQL学习心得(七) 查询 我的MYSQL学习心得(八) 插入 更新 删除 我的MYSQL学习心得(九) 索引 我的MYSQL学习心得(十) 自定义存储过程

【大白话系统】MySQL 学习总结 之 缓冲池(Buffer Pool) 的设计原理和管理机制

一.缓冲池(Buffer Pool)的地位 在<MySQL 学习总结 之 InnoDB 存储引擎的架构设计>中,我们就讲到,缓冲池是 InnoDB 存储引擎中最重要的组件.因为为了提高 MySQL 的并发性能,使用到的数据都会缓存在缓冲池中,然后所有的增删改查操作都将在缓冲池中执行. 通过这种方式,保证每个更新请求,尽量就是只更新内存,然后往磁盘顺序写日志文件. 更新内存的性能是极高的,然后顺序写磁盘上的日志文件的性能也是比较高的,因为顺序写磁盘文件,他的性能要远高于随机读写磁盘文件. 正因为