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