0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

关于InnoDB的内存结构及原理详解

jf_f8pIz0xS 来源:SH的全栈笔记 作者:SH的全栈笔记 2021-04-16 16:15 次阅读

之前写过一篇文章「简单了解InnoDB原理」,现在回过头看,其实里面只是把缓冲池(Buffer Pool),重做日志缓冲(Redo Log Buffer)、插入缓冲(Insert Buffer)和自适应哈希索引Adaptive Hash Index)等概念简单的介绍了一下。

除此之外还聊了一下MySQL和InnoDB的日志,和两次写,总的来说算是一个入门级别的介绍,这篇文章就来详细介绍一下InnoDB的内存结构。

InnoDB内存结构

其大致结构如下图。

InnoDB内存的两个主要区域分别为Buffer Pool和Log Buffer,此处的Log Buffer目前是用于缓存Redo Log。而Buffer Pool则是MySQL或者说InnoDB中,十分重要、核心的一部分,位于主存。这也是为什么其访问数据的效率高,你可以暂时把它理解成Redis那样的内存数据库,因为我们更新和新增当然它不是,只是这样会更加方便我们理解。

Buffer Pool

通常来说,宿主机80%的内存都应该分配给Buffer Pool,因为Buffer Pool越大,其能缓存的数据就更多,更多的操作都会发生在内存,从而达到提升效率的目的。

由于其存储的数据类型和数据量非常多,Buffer Pool存储的时候一定会按照某些结构去存储,并且做了某些处理。否则获取的时候除了遍历所有数据之外,没有其他的捷径,这样的低效率操作肯定是无法支撑MySQL的高性能的。

因此,Buffer Pool被分成了很多页,这在之前的文章中也有讲过,这里不再赘述。每页可以存放很多数据,刚刚也提到了,InnoDB一定是对数据做了某些操作。

InnoDB使用了链表来组织页和页中存储的数据,页与页之间形成了双向链表,这样可以方便的从当前页跳到下一页,同时使用LRU(Least Recently Used)算法去淘汰那些不经常使用的数据。

同时,每页中的数据也通过单向链表进行链接。因为这些数据是分散到Buffer Pool中的,单向链表将这些分散的内存给连接了起来。

Log Buffer

Log Buffer用来存储那些即将被刷入到磁盘文件中的日志,例如Redo Log,该区域也是InnoDB内存的重要组成部分。Log Buffer的默认值为16M,如果我们需要进行调整的话,可以通过配置参数innodb_log_buffer_size来进行调整。

当Log Buffer如果较大,就可以存储更多的Redo Log,这样一来在事务提交之前我们就不需要将Redo Log刷入磁盘,只需要丢到Log Buffer中去即可。因此较大的Log Buffer就可以更好的支持较大的事务运行;同理,如果有事务会大量的更新、插入或者删除行,那么适当的增大Log Buffer的大小,也可以有效的减少部分磁盘I/O操作。

至于Log Buffer中的数据刷入到磁盘的频率,则可以通过参数innodb_flush_log_at_trx_commit来决定。

Buffer Pool的LRU算法

了解完了InnoDB的内存结构之后,我们来仔细看看Buffer Pool的LRU算法是如何实现将最近没有使用过的数据给过期的。

原生LRU

首先明确一点,此处的LRU算法和我们传统的LRU算法有一定的区别。为什么呢?因为实际生产环境中会存在全表扫描的情况,如果数据量较大,可能会将Buffer Pool中存下来的热点数据给全部替换出去,而这样就会导致该段时间MySQL性能断崖式下跌。

对于这种情况,MySQL有一个专用名词叫缓冲池污染。所以MySQL对LRU算法做了优化。

优化后的LRU

优化之后的链表被分成了两个部分,分别是 New Sublist 和 Old Sublist,其分别占用了 Buffer Pool 的3/4和1/4。

链表的前3/4,也就是 New Sublist 存放的是访问较为频繁的页,而后1/4也就是 Old Sublist 则是反问的不那么频繁的页。Old Sublist中的数据,会在后续Buffer Pool剩余空间不足、或者有新的页加入时被移除掉。

了解了链表的整体构造和组成之后,我们就以新页被加入到链表为起点,把整体流程走一遍。首先,一个新页被放入到Buffer Pool之后,会被插入到链表中 New Sublist 和 Old Sublist 相交的位置,该位置叫MidPoint。

该链表存储的数据来源有两部分,分别是:

MySQL的预读线程预先加载的数据

用户的操作,例如Query查询

默认情况下,由用户操作影响而进入到Buffer Pool中的数据,会被立即放到链表的最前端,也就是 New Sublist 的 Head 部分。但如果是MySQL启动时预加载的数据,则会放入MidPoint中,如果这部分数据被用户访问过之后,才会放到链表的最前端。

这样一来,虽然这些页数据在链表中了,但是由于没有被访问过,就会被移动到后1/4的 Old Sublist中去,直到被清理掉。

优化Buffer Pool的配置

在实际的生产环境中,我们可以通过变更某些设置,来提升Buffer Pool运行的性能。

例如,我们可以分配尽量多的内存给Buffer Pool,如此就可以缓存更多的数据在内存中

当前有足够的内存时,就可以搞多个Buffer Pool实例,减少并发操作所带来的数据竞争

当我们可以预测到即将到来的大量请求时,我们可以手动的执行这部分数据的预读请求

我们还可以控制Buffer Pool刷数据到磁盘的频率,以根据当前MySQL的负载动态调整

那我们怎么知道当前运行的 MySQL 中 Buffer Pool 的状态呢?我们可以通过命令show engine innodb status来查看。这个命令是看 InnoDB 整体的状态的, Buffer Pool 相关的监控指标包含在了其中,在Buffer Pool And Memory模块中。

样例如下。

---------------------- BUFFER POOL AND MEMORY ---------------------- Total large memory allocated 137428992 Dictionary memory allocated 972752 Buffer pool size 8191 Free buffers 4596 Database pages 3585 Old database pages 1303 Modified db pages 0 Pending reads 0 Pending writes: LRU 0, flush list 0, single page 0 Pages made young 1171, not young 0 0.00 youngs/s, 0.00 non-youngs/s Pages read 655, created 7139, written 173255 0.00 reads/s, 0.00 creates/s, 0.00 writes/s No buffer pool page gets since the last printout Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s LRU len: 3585, unzip_LRU len: 0 I/O sum[0]:cur[0], unzip sum[0]:cur[0]

解释一些关键的指标所代表的含义:

Total memory allocated:分配给 Buffer Pool 的总内存

Dictionary memory allocated:分配给 InnoDB 数据字典的总内存

Buffer pool size:分配给 Buffer Pool 中页的内存大小

Free buffers:分配给 Buffer Pool 中 Free List 的内存大小

Database pages:分配给 LRU 链表的内存大小

Old database pages:分配给 LRU 子链表的内存大小

Modified db pages:当前Buffer Pook中被更新的页的数量

Pending reads:当前等待读入 Buffer Pool 的页的数量

Pending writes LRU:当前在 LRU 链表中等待被刷入磁盘的脏页数量

都是些很常规的配置项,你可能会比较好奇什么是 Free List。

Free List 中存放的都是未被使用的页。因为MySQL启动的时候,InnoDB 会预先申请一部分页。如果当前页还未被使用,就会被保存在 Free List 中。

知道了 Free List,那么你也应该知道 Flush List,里面保存的是所有的脏页,都是被更改后需要刷入到磁盘的。

自适应哈希索引

自适应哈希索引(Adaptive Hash Index)是配合Buffer Pool工作的一个功能。自适应哈希索引使得MySQL的性能更加接近于内存服务器。

如果要启用自适应哈希索引,可以通过更改配置innodb_adaptive_hash_index来开启。如果不想启用,也可以在启动的时候,通过命令行参数--skip-innodb-adaptive-hash-index来关闭。

自适应哈希索引是根据索引Key的前缀来构建的,InnoDB 有自己的监控索引的机制,当其检测到为当前某个索引页建立哈希索引能够提升效率时,就会创建对应的哈希索引。如果某张表数据量很少,其数据全部都在Buffer Pool中,那么此时自适应哈希索引就会变成我们所熟悉的指针这样一个角色。

当然,创建、维护自适应哈希索引是会带来一定的开销的,但是比起其带来的性能上的提升,这点开销可以直接忽略不计。但是,是否要开启自适应哈希索引还是需要看具体的业务情况的,例如当我们的业务特征是有大量的并发Join查询,此时访问自适应哈希索引被产生竞争。并且如果业务还使用了LIKE或者%等通配符,根本就不会用到哈希索引,那么此时自适应哈希索引反而变成了系统的负担。

所以,为了尽可能的减少并发情况下带来的竞争,InnoDB对自适应哈希索引进行了分区,每个索引都被绑定到了一个特定的分区,而每个分区都由单独的锁进行保护。其实通俗点理解,就是降低了锁的粒度。分区的数量我们可以通过配置innodb_adaptive_hash_index_parts来改变,其可配置的区间范围为[8, 512]。

Change Buffer

聊完了 Buffer Pool 中索引相关,剩下的就是 Change Buffer 了。Change Buffer是一块比较特殊的区域,其作用是用于存储那些当前不在 Buffer Pool 中的但是又被修改过的二级索引。

用流程来描述一下就是,当我们更新了非聚簇索引(二级索引)的数据时,此时应该是直接将其在Buffer Pool中的对应数据更新了即可,但是不凑巧的是,当前二级索引不在 Buffer Pool 中,此时将其从磁盘拉取到 Buffer Pool 中的话,并不是最优的解,因为该二级索引可能之后根本就不会被用到,那么刚刚昂贵的磁盘I/O操作就白费了。

所以,我们需要这么一个地方,来暂存对这些二级索引所做的改动。当被缓存的二级索引页被其他的请求加载到了Buffer Pool 中之后,就会将 Change Buffer 中缓存的数据合并到 Buffer Pool 中去。

当然,Change Buffer也不是没有缺点。当 Change Buffer 中有很多的数据时,全部合并到Buffer Pool可能会花上几个小时的时间,并且在合并的期间,磁盘的I/O操作会比较频繁,从而导致部分的CPU资源被占用。

那你可能会问,难道只有被缓存的页加载到了 Buffer Pool 才会触发合并操作吗?那要是它一直没有被加载进来,Change Buffer 不就被撑爆了?很显然,InnoDB在设计的时候考虑到了这个点。除了对应的页加载,提交事务、服务停机、服务重启都会触发合并。
编辑:lyn

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内存
    +关注

    关注

    8

    文章

    3023

    浏览量

    74028
  • MySQL
    +关注

    关注

    1

    文章

    807

    浏览量

    26551
  • 索引
    +关注

    关注

    0

    文章

    59

    浏览量

    10468
收藏 人收藏

    评论

    相关推荐

    DDR5内存的工作原理详解 DDR5和DDR4的主要区别

    DDR5内存的工作原理详解 1. DDR5内存简介 DDR5(Double Data Rate 5)是第五代双倍数据速率同步动态随机存取存储器(SDRAM)。它是DDR4的后续产品,提供更高
    的头像 发表于 11-22 15:38 816次阅读

    DDR内存的工作原理与结构

    电子设备的内存威廉希尔官方网站 。以下是对DDR内存的工作原理与结构的介绍: 一、工作原理 时钟同步 :DDR内存是同步的,这意味着数据传输与系统时钟同步。时钟信号用于协调
    的头像 发表于 11-20 14:32 401次阅读

    Windows管理内存的三种主要方式

    Windows操作系统提供了多种方式来管理内存,以确保系统资源的有效利用和性能的优化。以下是关于Windows管理内存的三种主要方式的详细阐述,包括堆内存管理、虚拟
    的头像 发表于 10-12 17:09 748次阅读

    逻辑内存和物理内存的区别

    逻辑内存和物理内存是计算机系统中两个重要的概念,它们在计算机的运行和数据处理中起着至关重要的作用。 1. 物理内存(Physical Memory) 物理内存,也称为RAM(Rando
    的头像 发表于 09-27 15:38 656次阅读

    如何使用反射内存交换机

    反射内存交换机是一种用于实现高速数据共享和通信的关键设备,以下是关于如何使用反射内存交换机的详细介绍:一、前期准备 在开始使用反射内存交换机之前,需要进行以下准备工作: 1.了解系统需
    发表于 09-14 09:23 0次下载

    内存缓冲区和内存的关系

    内存缓冲区和内存之间的关系是计算机体系结构中一个至关重要的方面,它们共同协作以提高数据处理的效率和系统的整体性能。
    的头像 发表于 09-10 14:38 553次阅读

    内存管理的硬件结构

    常见的内存分配函数有malloc,mmap等,但大家有没有想过,这些函数在内核中是怎么实现的?换句话说,Linux内核的内存管理是怎么实现的?
    的头像 发表于 09-04 14:28 303次阅读
    <b class='flag-5'>内存</b>管理的硬件<b class='flag-5'>结构</b>

    堆栈和内存的基本知识

    本文主要聊聊关于堆栈的内容。包括堆栈和内存的基本知识。常见和堆栈相关的 bug,如栈溢出,内存泄漏,堆内存分配失败等。后面介绍软件中堆栈统计的重要性,以及如何使用工具工具软件中堆栈使用
    的头像 发表于 08-29 14:10 464次阅读
    堆栈和<b class='flag-5'>内存</b>的基本知识

    华纳云:InnoDB 具有哪四大特性

    InnoDB 是 MySQL 数据库中的一种存储引擎,它具有许多特性,但通常被认为有以下几个主要特点: 行级锁定:InnoDB 支持行级锁定,这意味着它在处理并发事务时,只锁定那些需要修改的行,而
    的头像 发表于 08-14 16:02 302次阅读

    你是否真的了解结构体占用了多少字节?

    结构体成员所占内存空间大小一般情况下,如果想知道结构体成员的内存占用情况需要:1、先用结构体在内存
    的头像 发表于 06-04 08:04 450次阅读
    你是否真的了解<b class='flag-5'>结构</b>体占用了多少字节?

    寄存器和内存的区别

    在计算机体系结构中,寄存器和内存是两个至关重要的组成部分。它们各自承担着不同的角色,共同确保计算机系统的正常运行。本文将对寄存器和内存进行详细的介绍,包括它们的定义、功能以及二者之间的主要区别。
    的头像 发表于 05-12 17:11 2252次阅读

    集成芯片原理图详解

    集成芯片的原理图详解涉及多个方面,包括芯片的结构、功能模块、信号传输以及内部电路连接等。
    的头像 发表于 03-19 16:36 1904次阅读

    数组和链表在内存中的区别 数组和链表的优缺点

    数组和链表在内存中的区别 数组和链表的优缺点  数组和链表是常见的数据结构,用于组织和存储数据。它们在内存中的存储方式以及优缺点方面存在一些显著的差异。本文将详细探讨这些差异以及它们的优缺点。 1.
    的头像 发表于 02-21 11:30 1027次阅读

    经典 C 语言编程,结构体和联合体如何共用?

    结构结构体占用的内存大小,首先和编译器的系统位数有关系,类似于CPU是 64 bits 还是 32 bits 的情形;其次,结构体需要考虑字节对齐的问题。 实际上占用的
    的头像 发表于 01-11 18:24 1368次阅读
    经典 C 语言编程,<b class='flag-5'>结构</b>体和联合体如何共用?

    FreeRTOS内存机制详解

    FreeRTOS是一种实时操作系统,它提供了多种内存分配方式,包括动态内存分配和静态内存分配。
    的头像 发表于 12-31 16:49 2688次阅读
    FreeRTOS<b class='flag-5'>内存</b>机制<b class='flag-5'>详解</b>