当前位置: 首页 > news >正文

【MySQL】InnoDB内存结构

目录

InnoDB内存结构

主要组成

缓冲池

缓冲池的作用

缓冲池的结构

缓冲池中页与页之间连接方式分析

缓冲池如何组织数据

控制块初始化

页面初始化

缓冲池中页的管理

缓冲区淘汰策略

查看缓冲池信息

总结

变更缓冲区-Chang Buffer

变更缓冲区的作用

主要配置选项

查看当前变更缓冲区信息

自适应哈希

自适应哈希作用

自适应哈希的配置

查看自适应哈希信息

日志缓冲区

日志缓冲区的作用

日志缓冲区写入磁盘与内存比较

InnoDB内存结构总结

​编辑

InnoDB内存结构

主要组成

内存结构主要构成

  • Buffer Pool缓冲区
  • Change Buffer变更缓冲区
  • adaptive_hash_index自适应哈希索引
  • Log Buffer 日志缓冲区

缓冲池

缓冲池的作用

缓冲池谓语InnoDB内存区域,主要用于缓存表和索引的常用数据。通过将频繁访问的数据(例如数据页、索引页)保存到内存中,缓冲池减少了磁盘的I/O操作,从而提高数据库性能。

缓冲池的结构

InnoDB的缓冲池至少包含一个Instance实例,其中一个Instance是真正的缓冲池事例对象,内存操作都是在Instance实例中进行

每个Instance实例中都有一个Chunk(服务器运行状态下动态调整块的大小),然后没Chunk块有包含若干个从磁盘上加载到内存的page数据页

缓冲池中页与页之间连接方式分析

链接机制

  • 数据页在内存中的链接并未直接使用物理地址,而是通过链表中的指针和偏移量来表示
  • 每个数据页头信息中包含一个next_record字段,该字段记录了下一行数据的偏移量,根据偏移量可以找到下一条数据

数据页的逻辑连接

  • 数据页通过B+树索引逻辑连接,叶子节点为实际数据页,非叶子节点存储指向子节点的指针

双向链表

  • 所有控制块都是通过双向链表连接,形成缓冲池LRU链表
  • 头结点:最近访问的数据页
  • 尾结点:最少访问的数据页,优先淘汰

链表节点的功能

  • 数据是否为脏页
  • 数据页的访问次数
  • 数据页的最后访问时间 

缓冲池如何组织数据

  • 加载数据页
    • 从磁盘中加载数据到缓冲池
    • 为每一个加载的数据页分配一个控制块
    • 控制块记录数据页的指针,并插入到LRU链表的头部
  • 访问数据页:

    • 通过控制块找到目标数据页
    • 更新控制块的访问信息,将其移动到 LRU 链表头部
  • 淘汰数据页

    • 当缓冲池空间不足时,从 LRU 链表尾部开始淘汰数据页
    • 如果是脏页,先写回磁盘再释放

内存中的数据页和磁盘上的数据页的关系

磁盘中的数据页加载到内存后,缓冲区有一个内存页与之对应;内存中是使用控制块对其进行管理,控制块中是有一个指针指向了内存中真实的数据页

设置缓冲区大小

通过系统变量Innodb_buffer_pool_size进行设置,默认是128MB

缓冲池中instance数量

SHOW VARIABLES LIKE 'innodb_buffer_pool_instances';

设置(全局配置或配置文件中配置)

//全局配置
SET GLOBAL innodb_buffer_pool_instances = <数量>;//配置文件
[mysqld]
innodb_buffer_pool_instances = <数量>

根据缓冲区大小进行设置,如果缓冲区小于1GB,那么自动设置为1;如果缓冲区大于1GB那么默认值是8。

最大的实例数量是64

chunk的作用

chunk是缓冲池中最小的分配管理单位,每个chunk都有若干个页组成,页是最小的数据存储单位。一个chunk一般是1MB,一个数据页是16KB,那么一个chunk中有64页

chunk的核心作用:支持缓存池大小动态调整;提升缓存池的内存管理效率;减少大规模内存分配和锁竞争

控制块初始化

控制块在缓冲池初始化时与页面一起分配,每个页面分配一个对应的控制块,控制块的数量等于缓冲池中页面的数量

控制块是使用双向链表组织,开始缓冲池分配控制块数组,初始化每个控制块的结构体,将默认值填充到各个字段,然后建立双向链表,初始化链表的头尾指针

struct ControlBlock {void* page_ptr;           // 指向页面的指针bool is_dirty;            // 脏页标志int access_count;         // 访问计数uint64_t timestamp;       // 最后访问时间戳ControlBlock* prev;       // 指向前一个控制块ControlBlock* next;       // 指向后一个控制块
};

页面初始化

  • 分配缓冲池实例的内存区域。
  • 将内存区域按照 16KB 切分成若干页面。
  • 初始化每个页面的头部元信息(如页面类型、下一页偏移)。
  • 将页面与控制块建立一一对应关系。

内存先进行初始化,然后建立控制块与内存中缓存数据页之间的关系,从左边开始第一个控制块指向第一个缓冲数据页的内存地址

控制块与页面的关系

每个控制块通过指针指向缓冲池中的一个页面;控制块和页面的数量相等,每个控制块管理一个页面

首先分配页面内存后,记录页面的地址;然后将页面地址存储到对应的控制块中;最后将控制块加入到双向链表中

配置缓冲池提升性能

  •  配置缓冲池大小
  •  配置 多个缓冲池实例
  •  防止缓冲区扫描
  •  配置缓冲区预读取
  •  配置缓冲区的刷新策略
  •  保存和恢复缓冲池的状态

缓冲池中页的管理

分析

页的三种状态

  • Free(空闲页)
    • 含义:没有使用过的页,可以用来存储新的数据;这些页不包含任何有效的数据,完全空闲
    • 作用:当需要加载新的数据页到缓冲池中的时候,InnoDB会优先从Free List中获取空闲页
  • Clean(干净页)
    • 含义:已经使用但是没有被修改的页,与磁盘中的页内容一致,所以是不需要写回磁盘
    • 作用:clean页通通常都是存储在LRU(最近最少使用)列表中,用于提供快速访问
  • Dirty(脏页)
    • 含义:已经被修改但是还没有写回磁盘的页,这些页的内容与磁盘中的数据不同,需要在某些条件下写回磁盘从而确保数据的持久性
    • 作用:Dirty页一般被放在Flush List中,等待后台线程触发特定条件后将其刷新到磁盘中

三个链表管理页

  • Free List
    • 管理所有的空闲页
    • 管理:当需要加载新的数据时候,从Free List中分配页;如果Free List中没有空闲页,会通过替换策略从LRU List中淘汰页
    • 动态变化:LRU List 中淘汰的页被释放后,会重新加入到Free List
  • LRU List
    • 管理Clean 和 Dirty 页
    • 管理思路:页的访问会更新其在LRU列表中的位置,频繁访问的页会移动到列表前部,最少使用的页会逐渐移动到尾部;缓冲区需要更多空间的时候,LRU列表尾部的页会被释放或者写回磁盘
    • 优化策略:使用中点插入策略,将新加载的也插入的LRU列表的中部,避免新页短时间占用较高优先级(下文详细分析)
  • Flush List
    • 专门管理Dirty页
    • 管理:当一个页被修改的时候,会被标记为Dirty然后加入到Flush List中;后台线程或者触发条件(例如缓冲池不足)就会定期扫描Flush List并将脏页写回到磁盘上
    • 动态变化:当Dirty页被刷新到磁盘后,会从Flush List中移除

分析页管理中三种操作

  • 读操作(加载数据页)
    • 首先检查需要的页是否在缓冲池中,如果命中缓存,那么从缓冲池中读取;如果没有命中缓存,则从磁盘加载页到缓冲池;
    • 从Free List分配空闲页,或者从LRU List替换最少使用的页
  • 写操作(修改数据页)
    • 修改缓冲池中数据页后,该页被标记为Dirty,然后将脏页加入到Flush List中,等待后续刷新磁盘
  • 替换页
    • 缓冲区不足的时候,从LRU List 尾部选择使用最少的页;如果是脏页那么先写回磁盘,再释放,释放后的页加入到Free List重新分配

总结

每个缓冲池通过三个列表管理三种内存页。Free未使用的页、Clean干净页、Dirty脏页

拓展1:内存如何快速找到目标页

InnoDB使用page Hash方式,当磁盘数据加载到内存的时候,用数据页的表空间ID和页号作为Key,页在内存的地址为Value保存起来,每次查询的时候通过Key快速定位到目标页,如果内存中没有目标页,则从磁盘中获取 

缓冲区淘汰策略

分析

LRU机制分析

  • 页访问
    • 如果该页已在缓冲池中,InnoDB 会将其移动到 LRU 列表的头部,标记为最近使用。
    • 如果该页不在缓冲池中,InnoDB 会从磁盘加载该页到缓冲池,并插入到 LRU 列表中。
  • 页插入
    • 新加载的页并不会直接插入到 LRU 列表的头部,而是通过中点插入策略
  • 页淘汰
    • 如果待淘汰页是 Clean 页,则直接从 LRU 列表移除。
    • 如果是 Dirty 页,则需先将其写回磁盘,然后再移除。

总结

淘汰策略采用LRU

拓展1:页的插入为什么在中部而不是新列表的头部

如果直接将新页插入到列表的头部,容易受到顺序扫描或者批量数据加载的影响,从而会导致缓冲池被新加载的数据被污染,频繁访问的页反而会被淘汰,所以使用中点插入解决该问题

InnoDB在读取页的时候会有预读,也就是根据当前访问记录自动推断后面可能访问哪些页,然后一起加载到内存,从而提高查询效率,从中间点插入可以让其尽快淘汰

查看缓冲池信息

SHOW ENGINE INNODB STATUS\G;

  • Buffer pool size:缓冲池的总大小(以页为单位)。
  • Free buffers:空闲页的数量。
  • Database pages:已加载到缓冲池的数据库页数量。
  • Modified db pages:脏页(已被修改但尚未写回磁盘的页)数量。
  • Pages read:自服务器启动以来从磁盘读取的页数。
  • Pages written:自服务器启动以来写入磁盘的页数。
  • Pages created:自服务器启动以来新创建的页数。
  • Read requests:逻辑读请求的总数。
  • Write requests:写请求的总数。

总结

  • 缓冲池的主要作用是用于缓存各种数据,最主要的就是缓存从磁盘中加载数据页,从而提高读取效率
  • 缓冲池为方便数据组织定义了不同的数据结构:Buffer Pool中包含至少一个Instance,Instance包含至少一个Chunk,Chunk管理若干个个从磁盘中加载到内存的Page页
  • 缓冲池使用三种链表管理三种内存页,分别是Free List 、LRU List和FLush List
  • 淘汰策略是LRU算法

变更缓冲区-Chang Buffer

变更缓冲区的作用

分析

变更缓冲区

  • 存储内容:用于缓存次级索引页的膝盖内容(例如INSERT\UPDATA\DELETE);如果修改涉及的次索引页还没有被加载到缓冲池中,那么变更缓冲池就会临时保存这些修改,而不是立刻就加载索引页到缓冲池中并写入磁盘

写入磁盘触发条件

  • 当涉及的索引页不在缓冲池中的时候,变更缓冲区会暂时存储修改操
  • 当后面其他操作需要访问这些索引页的时候,InnoDB会将索引页从磁盘加载到缓冲池中,并将变更缓冲区的修改合并到加载页中       

总结

  • 减少磁盘I/O:次级索引修改频繁的时候,变更缓冲区避免了每次修改都触发磁盘读取操作;批量合并修改后再写入磁盘,有效减少随机磁盘访问

  • 提高读写性能,通过合并操作减少了磁盘的随机写入次数,优化了磁盘写入性能

  • 对于不经常访问的索引页,变更缓冲区延迟了其加载时间,降低了缓冲池的压力

拓展:主键索引和二级索引

  • 主键索引就像一本书的目录,唯一且直接指向内存
  • 二级索引就像术后的关键词索引表,可以针对于多列进行加速查询,但是需要回表找到数据行
    • 二级索引不是唯一的,针对的是非主键列,允许多个数据行有相同值
  • 二级索引的存在让查询更加灵活高效

主要配置选项

分析

缓冲类型

控制变更缓冲区的行为,指定变更缓冲区缓存哪些类型

  • all:缓存所有次级索引的修改操作(默认值)
  • insert:进缓存次级层插入操作
  • deletes:次级的删除操作
  • changes:缓存插入和删除操作
  • purges:仅缓存清除操作
  • none:禁用变更缓冲区
// 动态调整
SET GLOBAL innodb_change_buffering = 'inserts';// 修改配置文件[mysqld]
innodb_change_buffering = inserts

变更缓冲池大小

范围1-50(表示占缓冲池大小的百分比)

// 动态调整
SET GLOBAL innodb_change_buffer_max_size = 30; -- 设置为 30%//配置文件
[mysqld]
innodb_change_buffer_max_size = 30

总结

主要配置项就是缓冲类型更改缓冲区的最大大小

查看当前变更缓冲区信息

SHOW ENGINE INNODB STATAUS\G//命令同上,找到对应的信息即可

自适应哈希

自适应哈希作用

 分析

工作原理分析

  • InnoDB动态监控缓冲池页的访问情况
    • 如果某些表或者索引页被频繁访问,并且通过创建哈希表可以显著提升查询性能,InnoDB会自动为其生成哈希索引
  • 构建哈希索引
    • 自适应哈希索引是基于B+树索引动态生成;其将频繁访问的页或行的索引转化为哈希表的条目,映射到内存的位置
  • 自适应哈希索引存储在内存中,全部依赖缓冲池空间

启动和关闭命令

SET GLOBAL innodb_adaptive_hash_index = 1;SET GLOBAL innodb_adaptive_hash_index = 0;

总结

自适应哈希可以提升查询速度,通过减少索引层次的遍历,显著提升查询效率;同时基于缓冲池的构建,直接利用已有的数据和索引页

拓展:使用自适应哈希的原因

  • 提升查询性能:B+树已经非常高效,但是其需要多层遍历;自适应哈希索引通过直接映射的方式减少查询路径上的层次
  • 减少系统资源消耗:针对于高频访问的路径,B+树多次比较和遍历会大量消耗CPU资源;自适应哈希通过避免遍历和比较,降低CPU资源的利用
  • 简化高频查询:针对于某些频繁重复查询的模式,自适应哈希索引可以显著减少查询时间

拓展:自适应哈希KV设置

Key来源:自适应哈希Key是查询条件的索引键值;Value则是该Key对应缓冲池中的B+树页的内存地址。自适应哈希索引通过将Key映射到该Value,可以直接跳转到缓冲池中的内存位置,避免了遍历B+树的操作

映射过程分析:当某一个索引键频繁的被访问,自适应哈希索引会记录该Key和Value的关系;查询的时候直接通过哈希值找到对应的Value,无需要再去访问B+树的多级结构

拓展:自适应哈希索引存储位置

自适应哈希是存储在缓冲池中的内存区域中,缓冲池初始化后分配一部分空间用于存储自适应哈希;

为了避免高并发访问自适应哈希引起锁的竞争,InnoDB支持对哈希索引进行分区,该分区机制可以显著减少多线程访问自适应哈希索引时的冲突

自我理解&&

例如在图书馆找一本书的场景为被背景。传统B+树索引则每次读者找书,都逐层开始招数,即使手里有目录,但还是需要一页一页的查找;自适应哈希则是将那些热门书籍放在(缓冲区)的快速取书区中,所以此时找书的时候就不需要去按照目录逐层寻找。

高并发场景下就会出现问题,因为设置快速取书区只有一张桌子,当多个人去找不同的热门书籍的时候,这些人就会排队,所以也就导致了锁竞争

分区解决该问题的思路,图书馆中设置多个桌子,根据热门数据的类型将其放在不同的桌子,那么当读者来找书的时候,找到不同的桌子,然后在自己的分区中上锁,这样就提高了效率

技术角度理解&&

自适应哈希的分区机制,也就是把一个大的快速指引区域(一个单个哈希表)拆分成多个小的指引区域;通过哈希函数计算,将不同查询条件(Key)分配到不同区

这样做的好处就在于当多个线程访问的时候,如果其查询的数据分配到了不同的分区,也是可以同时工作的,不会相互阻塞。

自适应哈希的配置

启用和禁用

// 临时关闭和启用SET GLOBAL innodb_adaptive_hash_index = OFF; -- 禁用
SET GLOBAL innodb_adaptive_hash_index = ON;  -- 启用// 配置文件[mysqld]
innodb_adaptive_hash_index = 0  # 禁用

分区数量配置

通过调整哈希索引分区数量,从而减少高并发访问时的锁竞争;每个分区都有一个独立的哈希表,线程访问的时候只需要锁定对应分区即可,不需要整个哈希表

SET GLOBAL innodb_adaptive_hash_index_parts = 16;  -- 设置为 16 个分区// 配置文件[mysqld]
innodb_adaptive_hash_index_parts = 16

缓冲池大小设置

SET GLOBAL innodb_buffer_pool_size = 8G; -- 设置缓冲池为 8GB

查看自适应哈希信息

查看自适应哈希的状态

  • Hash table size:哈希表大小。
  • Used cells:已使用的哈希表条目。
  • Total searchesSuccessful searches:表示查询命中率,越接近 100%,AHI 的效果越好

查询性能统计

命中率计算

日志缓冲区

日志缓冲区的作用

总结

  • 缓存事务日志

    • 在事务执行过程中,InnoDB 会将所有需要写入到 Redo Log 的信息先写入日志缓冲区
    • 这些信息包括事务对表数据的修改,用于在数据库崩溃时进行恢复
  • 提高写入效率

    • 直接将每次事务的日志写入磁盘会产生大量的随机 I/O 操作,影响性能
    • 日志缓冲区通过将多个事务的日志批量写入磁盘,显著减少磁盘写入次数,从而提高性能
  • 事务持久性

    • Redo Log 是 InnoDB 支持事务持久性的核心组件。即使数据库崩溃,也可以通过 Redo Log 恢复已提交的事务
    • 日志缓冲区确保在事务提交时,相关日志已经持久化到磁盘
  • 崩溃恢复
    • 当 MySQL 服务意外中断或崩溃时,日志缓冲区中的数据如果已经被刷新到磁盘的 Redo Log 文件,InnoDB 可以利用这些日志恢复数据

日志缓冲区写入磁盘与内存比较

日志缓冲区工作流程分析

当事务修改表中数据的时候,InnoDB会先修改操作记录未日志条目,然后写入日志缓冲区;日志缓冲区中的日志会定期或者在事务提交的时候刷新到磁盘中;刷新的时间则是由参数innodb_flush_log_at_trx_commit决定;当日志从缓冲区刷新到磁盘后,事务的修改才被认为是持久化

日志通过缓冲区写入的原因分析

根本原因就是尽量减少磁盘I/O,先写入到缓冲区中,然后统一刷新到磁盘中,这样就实现了一次磁盘I/O写入多条日志,从而提高效率

InnoDB内存结构总结

http://www.lryc.cn/news/488560.html

相关文章:

  • 基于大数据爬虫数据挖掘技术+Python的网络用户购物行为分析与可视化平台(源码+论文+PPT+部署文档教程等)
  • 蓝桥杯每日真题 - 第19天
  • CentOS7.9.2009的yum更换vault地窖保险库过期源,epel的archive归档源 笔记241117
  • Spark SQL大数据分析快速上手-完全分布模式安装
  • Java面试题2024-Java基础
  • 局域网协同办公软件,2024安全的协同办公软件推荐
  • osg、osgearth简介及学习环境准备
  • nodejs基于微信小程序的云校园的设计与实现
  • uni-app快速入门(十)--常用内置组件(下)
  • golang基础
  • Selenium + 数据驱动测试:从入门到实战!
  • LLaMA与ChatGLM选用比较
  • GPTZero:高效识别AI生成文本,保障学术诚信与内容原创性
  • C/C++ 优化,strlen 示例
  • 【动手学深度学习Pytorch】1. 线性回归代码
  • 深入理解PyTorch中的卷积层:工作原理、参数解析与实际应用示例
  • DataGear 5.2.0 发布,数据可视化分析平台
  • uniapp: vite配置rollup-plugin-visualizer进行小程序依赖可视化分析减少vender.js大小
  • 深度学习:如何复现神经网络
  • Spring Boot与MyBatis-Plus的高效集成
  • 【Unity ShaderGraph实现流体效果之Function入门】
  • Spark RDD sortBy算子执行时进行数据 “采样”是什么意思?
  • React-useRef与DOM操作
  • Mistral AI 发布 Pixtral Large 模型:多模态时代的开源先锋
  • Windows、Linux多系统共享蓝牙设备
  • C语言 | Leetcode C语言题解之第564题寻找最近的回文数
  • wsl虚拟机中的dockers容器访问不了物理主机
  • Spark RDD 的宽依赖和窄依赖
  • 二进制转十进制
  • 深度学习:神经网络中的非线性激活的使用