InnoDB引擎底层原理
2023-07-03 12:28:46 9 举报
InnoDB引擎底层原理
作者其他创作
大纲/内容
<b>InnoDB记录存储结构和索引页结构</b>
InnoDB是一个将表中的数据存储到<b><font color="#b71c1c">磁盘</font></b>上的存储引擎,<br>
<b>InnoDB如何获取记录</b>
将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为16 KB。也就是在一般情况下,一次最少从磁盘中读取<b><font color="#b71c1c">16KB</font></b>的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。
<b>行格式</b>
我们可以在创建或修改表的语句中指定行格式:<b>CREATE TABLE表名(列的信息) ROW_FORMAT</b>=行格式名称
表中的某些列可能存储NULL值,Compact行格式把这些值为NULL的列统一管理起来,存储到 NULL 值列表每个允许存储 NULL 的列对应一个二进制位,二进制位的值为<b><font color="#b71c1c">1</font></b>时,代表该列的值为NULL。二进制位的值为0时,代表该列的值不为NULL。
<b>记录头</b>
预留位 1 1 没有使用
预留位 2 1 没有使用
delete_mask 1 标记该记录是否被删除
min_rec_mask 1 B+树的每层非叶子节点中的最小记录都会添加该标记
n_owned 4 表示当前记录拥有的记录数
heap_no 13 表示当前记录在页的位置信息
record_type 表示当前记录的类型
0 表示普通记录
1 表示 B+树非叶子节点记录
2 表示最小记录
3 表示最大记录
<b><font color="#b71c1c">MySQL默认添加的隐藏列</font></b>
<b>DB_ROW_ID</b>(row_id):非必须,6 字节,表示行 ID,唯一标识一条记录
<b>DB_TRX_ID</b>:必须,6 字节,表示事务 ID
<b>DB_ROLL_PTR</b>:必须,<b>7</b> 字节,<b>表示回滚指针</b>
<b>InnoDB 表对主键的生成策略</b>
优先使用用户自定义主键作为主键,如果用户没有定义主键,则选取一个<b><font color="#b71c1c"> Unique 键</font></b>作为主键,如果表中连 Unique 键都没有<br>定义的话,则 InnoDB 会为表默认添加一个名为 <b><font color="#b71c1c">row_id 的隐藏列作为主键</font></b>。DB_TRX_ID(也可以称为 trx_id) 和 DB_ROLL_PTR(也可以称为 roll_ptr) 这两个列是必有的,但是 row_id 是可选的(在没有自定义主键以及 Unique 键的情况下才会添加该列)。
<b>索引页格式</b>
索引页是InnoDB 管理存储空间的<b>基本单位</b>,一个页的大小一般是 <font color="#b71c1c" style=""><b>16KB</b></font>。
一个 InnoDB 数据页的存储空间大致被划分成了 7 个部分:
File Header 文件头部 <b>38 字节</b> 页的一些通用信息
Page Header 页面头部<b> 56 </b>字节 数据页专有的一些信息
Infimum + Supremum 最小记录和最大记录<b> 26 字节</b> 两个虚拟的行记录
User Records 用户记录 大小不确定 实际存储的行记录内容
我们<b>自己存储的记录</b>会按照我们指定的行格式存储到 User Records 部分
Free Space 空闲空间 大小不确定 页中尚未使用的空间
Page Directory 页面目录 大小不确定 页中的某些记录的相对位置
Page Directory 主要是解决<b>记录链表</b>的查找问题
File Trailer 文件尾部 8 字节 校验页是否完整
<b>InnoDB 的体系结构</b>
宏观的角度看看 InnoDB 的<b>内存结构</b>和<b>磁盘存储结构</b>。MySQL官网原图
独立表空间结构
对于 <b>16KB</b> 的页来说,<b><font color="#b71c1c">连续的</font></b>64 个页就是一个区,也就是说一个区默认占用 <b>1MB</b> 空间大小
不论是系统表空间还是独立表空间,都可以看成是由若干个区组成的,每<b> 256个区</b>又被划分成一个组
一个索引会生成 2 个段,一个叶子节点段,一个非叶子节点段。段其实不对应表空间中某一个连续的物理区域,而是一个<b>逻辑上</b>的概念
<b><font color="#b71c1c">引入区的主要目的是什么</font></b>
我们每向表中插入一条记录,本质上就是向该表的聚簇索引以及所有二级索引代表的 B+树的节点中插入数据。而 B+树的每一层中的页都会形成一个<b><font color="#b71c1c">双向链表</font></b>,如果是<b>以页为单位</b>来分配存储空间的话,双向链表<b>相邻的两个页</b>之间的物理位置可能离得非常远。
<b>系统表空间</b>
<b><font color="#b71c1c">Innodb三大特性</font></b>
双写缓冲区/双写机制
Buffer Pool
自适应 Hash 索引
<b>doublewrite buffer 的作用</b>
提高 innodb 把缓存的数据写到硬盘这个过程的<b><font color="#d32f2f">安全性</font></b>
innodb 的事务日志不需要包含所有数据的前后映像,而是二进制变化量,这可以<b>节省大量的 IO</b>
<b>InnoDB 的 Buffer Pool</b>
缓存的重要性
减少磁盘IO的开销
<b>Buffer Pool</b>
InnoDB 为了缓存磁盘中的页,在 MySQL 服务器启动的时候就向操作系统申请了一片连续的内存默认128m
查看buffer大小 <b><font color="#b71c1c">show variables like 'innodb_buffer_pool_size';</font></b>
配置buffer的值 <b><font color="#b71c1c">innodb_buffer_pool_size = 268435456</font></b>
268435456 的单位是<b><font color="#b71c1c">字节</font></b>,也就是指定 Buffer Pool 的大小为 256M。需要注意的是,Buffer Pool 也不能太小,<b>最小值为 5M</b>(<b><font color="#b71c1c">当小于该值时会自动设置成5M</font></b>)。
Buffer Pool 内部组成
free 链表的管理
<b>缓存页的哈希处理</b>
根据表空间号 + 页号来定位一个页的,也就相当于表空间号 +页号是一个 key,<b>缓存页</b>就是对应的 value
用表空间号 + 页号作为 key,缓存页作为 value 创建一个哈希表,在需要访问某个页的数据时,先从哈希表中根据表空间号 + 页号看看有没有对应的缓存页,如果有,直接使用该缓存页就好,如果没有,那就从 <b>free 链表</b>中选一个<b>空闲</b>的缓存页,然后把<b>磁盘中</b>对应的页加载到该缓存页的位置。、
<b>LRU 链表的管理</b>
<b>刷新脏页到磁盘</b>
从 LRU 链表的冷数据中刷新一部分页面到磁盘
从 flush 链表中刷新一部分页面到磁盘
<b>多个 Buffer Pool 实例</b>
通过设置 innodb_buffer_pool_instances 的值来修改 Buffer Pool 实例的个数
每个 Buffer Pool 实例实际占多少内存空间
使用这个公式算出来的:<b><font color="#b71c1c">innodb_buffer_pool_size/innodb_buffer_pool_instances</font></b>
InnoDB 规定:innodb_buffer_pool_instances 能设置的最大值是<b><font color="#b71c1c"> 64</font></b>,而且当<b> innodb_buffer_pool_size(默认 128M)的值小于 1G 的时候设置多个实例是无效的</b>,InnoDB 会默认把 innodb_buffer_pool_instances 的值修改为 1。
最佳的 innodb_buffer_pool_instances 的数量是,innodb_buffer_pool_size 除以 innodb_buffer_pool_instances,可以让每个 BufferPool 实例达到 <b><font color="#b71c1c">1 个 G</font></b>
<b>查看 Buffer Pool 的状态信息</b>
<b><font color="#b71c1c">SHOW ENGINE INNODB STATUS\G</font></b>
<b>Total memory allocated</b>:代表 Buffer Pool 向操作系统申请的<b>连续内存空间大小</b>,包括全部控制块、缓存页、以及碎片的大小。
<b>Dictionary memory allocated</b>:为数据字典信息分配的内存空间大小,注意这个内存空间和 Buffer Pool 没啥关系,不包括在 Total memory allocated 中
<b>Buffer pool size</b>:代表该 Buffer Pool 可以容纳<b><font color="#b71c1c">多少缓存页</font></b>,注意,单位是页!
<b>Free buffers</b>:代表当前 Buffer Pool 还有多少空闲缓存页,也就是 free 链表中还有多少个节点。
<b>Database pages</b>:代表 LRU 链表中的页的数量,包含 young 和 old 两个区域的数量。
<b>Old database pages</b>:代表 LRU 链表 old 区域的节点数量
<b>Modified db pages</b>:代表脏页数量,也就是 flush 链表中节点的数量
<b>Pending read</b>s:正在等待从磁盘上加载到 Buffer Pool 中的页面数量
<b>Pending writes LRU</b>:即将从 LRU 链表中刷新到磁盘中的页面数量。
<b>Pending writes flush list</b>:即将从 flush 链表中刷新到磁盘中的页面数量。
<b>Pending writes single page</b>:即将以单个页面的形式刷新到磁盘中的页面数量
<b>Pages made young</b>:代表 LRU 链表中曾经从 old 区域移动到 young 区域头部的节点数量。
<b>Page made not young</b>:在将 innodb_old_blocks_time 设置的值大于 0 时,首次访问或者后续访问某个处在old 区域的节点时由于不符合时间间隔的限制而不能将其移动到 young 区域头部时,Page made not young 的值会加 1。
<b>youngs/s</b>:代表每秒从 old 区域被移动到 young 区域头部的节点数量
<b>non-youngs/s</b>:代表每秒由于不满足时间限制而不能从 old 区域移动到 young区域头部的节点数量。
<b>Pages read、created、written</b>:代表读取,创建,写入了多少页。后边跟着读取、创建、写入的速率。
<b>Buffer pool hit rate</b>:表示在过去某段时间,平均访问 1000 次页面,有多少次该页面已经被缓存到 Buffer Pool 了。
<b>young-making rate</b>:表示在过去某段时间,平均访问 1000 次页面,有多少次访问使页面移动到 young 区域的头部了。
<b>not (young-making rate)</b>:表示在过去某段时间,平均访问 1000 次页面,有多少次访问没有使页面移动到 young 区域的头部。
<b>LRU len</b>:代表 LRU 链表中节点的数量。
<b>unzip_LRU</b>:代表 unzip_LRU 链表中节点的数量。
<b>I/O sum</b>:最近 50s 读取磁盘页的总数。
<b>I/O cur</b>:现在正在读取的磁盘页数量。
<b>I/O unzip sum</b>:最近 50s 解压的页面数量。
<b>I/O unzip cur</b>:正在解压的页面数量。
0 条评论
下一页