【MySQL自身的性能优化】InnoDB 的 Buffer Pool

这篇具有很好参考价值的文章主要介绍了【MySQL自身的性能优化】InnoDB 的 Buffer Pool。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


有近俩个来月没有写博客了,也不知道自己在瞎忙活什么。之前我有写过一篇关于 MySQL 的内存池一篇博客 InnoDB体系架构之内存池(buffer pool),那是看楠哥视频后总结出来的。然后现是看了《MySQL 是怎样运行的》一书,也可能是基础相比之前要好了些,理解的要好点了吧,就再总结一篇,现进入正文。

一、引入缓存的重要性

无论是系统数据还是用于存储用户数据的索引(包括聚簇索引和二级索引),都是以页为基本单位存放在表空间中。所谓的表空间,只不过是 InnoDB 对一个或几个实际文件的抽象(ibd)。说到底数据是存放在磁盘上的,那磁盘的速度是非常慢的,而且在磁盘缓冲区和内存之间进行交互的时候也是需要霸占着CPU的,不管是在性能上还是在资源上都是一种不好的选择。所以 InnoDB 存储引擎在处理客户端的请求时,如果需要访问某个页的数据,就会把完整的页中的数据全部加载到内存中。也就是说,即使你是需要访问某索引页的一条记录,也需要先把整个页的数据加载到内存中(这一方面是和MySQL存储数据是在索引中,另一方面就是为了更好的缓存数据)。将整个页加载到内存中后就可以进行读写访问了,而且在读写访问之后并不着急把该页对应的内存空间释放掉,而是将其缓存起来,这样将来有请求再次访问该页面时,就可以省下磁盘 I/O 的开销了。

二、InnoDB 的 Buffer Pool

为缓存上面提到的磁盘中的页,在 MySQL 服务器启动时就向操作系统申请了一片连续的内存,这块内存的名字就叫 Buffer Pool(缓冲池)
查看 innodb_buffer_pool_size 可以查看到其申请的内存大小,我这分配的是 128mb;
【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库

1. Buffer Pool 内部组成

整个Buffer Pool 由控制块、缓冲页和碎片组成。

  • 控制块:它记录着缓冲页的描述信息,比如该页所属的表空间编号、页号、缓冲页在 Buffer Pool 中的地址等等。设置的 innodb_buffer_pool_size 的大小不包含这个
  • 缓冲页:buffer pool 中存放的【数据页】称之为【缓冲页】,和磁盘上的数据页是一一对应的,都是16KB,缓冲页的数据,是从磁盘上加载到 buffer pool 当中的一个完整页。
  • 碎片:若设置的Buffer Pool 放满了缓冲页,剩余的内存则称之为碎片。

【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库

2. free 链表管理空闲页

在最初启动 MySQL 服务器的时候,需要完成 Buffer Pool 的初始化工作。先向系统中申请 Buffer Pool 的内存空间,然后把它划分成若干对控制块和缓冲页。此时是没有磁盘页被存到 Buffer Pool 中的。
而有个 free 链表就是用来管理这些空闲页的,以控制块作为节点,进行连接组成的一个链表
【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库当有磁盘页需要被缓存到 Buffer Pool 中时,就可以进行下面操作

  1. 可以取出一个空闲的缓冲页;
  2. 然后将其描述信息填入控制块;
  3. 最后将 free 链表中的对应节点删除,表示已经不再空闲,而哪个磁盘页就放到对应的缓冲页的位置上。
  4. 然后将 表空间号 + 页号 作为 key,控制块地址作为 value,存入到一个哈希表中,方便后续读取或写对应的缓冲页。

若读某页的逻辑就大概如下:
【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库

3. flush 链表管理脏页

设计 Buffer Pool 除了可以提高读性能,也可提高写性能。当需要更新数据的时,若可以根据 表空间号 + 页号 可以找到对应的控制块地址,说明 Buffer Pool 存在对应的缓冲页,那么进行写操作可以选择直接对 缓冲页 进行写操作,那么它就和对应磁盘上的数据不一致了,这样的缓冲页也被称为脏页

这些脏页的控制块最后也会被拼接成一个链表——flush 链表
【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库和 Free 链表是一样的,只是区别是 Free 链表的结点是空闲缓冲页的控制块,而 Flush 链表的结点是脏页的控制块。
有了 Flush 链表之后,后台线程就可以通过遍历这个链表,然后将脏页写入到磁盘

【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库从上图的InnoDB存储引擎的体系架构中看出其后台线程所担任的角色:
负责刷新内存池中的数据,保证缓存池中的内存缓存是最近的数据。此外将已修改的数据文件刷新到磁盘文件中,同时保证在数据库中发生异常的情况下 InnoDB 能恢复到正常运行状态。(如:Master Thread:负责将缓存池中的数据异步刷新到磁盘;IO Thread:异步处理IO请求,提高数据库的性能

4. LRU 链表提高缓存命中

看见 LRU(Least Recently Used) 第一反应就是最近最少使用淘汰策略
这里使用它的目的就是因为内存的针对,Buffer Pool 的内存资源也是有限的,当无法缓存新的数据的时候,希望把一些不用的缓冲页给淘汰掉,空闲出缓冲页供新的数据页使用。
针对上面的需求,Buffer Pool 使用了 LRU 淘汰策略去实现。

简单的 LRU 算法的实现思路是这样:

  • 当访问的页在 Buffer Pool 里,就直接把该页对应的 LRU 链表节点移动到链表的头部;
  • 当访问的页不在 Buffer Pool 里,除了要把页放入到 LRU 链表的头部,还要淘汰 LRU 链表末尾的节点。

阐述了这三种链表,咱就知道 Buffer Pool 是怎样管理数据的了:
【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库解释图:

  • Free Page(空闲页),表示此页未被使用,位于 Free 链表;
  • Clean Page(干净页),表示此页已经被使用,但是页面未发生修改,位于 LRU 链表;
  • Dirty Page(脏页),表示此页【已被使用】且【已经被修改】,其数据和磁盘上的数据已经不一致了。当脏页上的数据写入磁盘后,内存数据和磁盘数据一致,那么该页就变成了干净页。脏页同时存在于 LRU 链表和 Flush 链表。

简单的 LRU 算法并没有被 MySQL 使用,因为简单的 LRU 算法无法避免下面这俩个问题:

  • 预读失效;
  • Buffer Pool 污染;

何为预读?
Innodb 提供了一个看起来比较贴心的服务——预读。所谓预读,就是 InnoDB 认为执行当前的请求时,可能会在后面读取某些页面,于是就预先把这些页面加载到 Buffer Pool 中。默认是线性预读,而不是随机预读。

咱就是说预读本来是个好事,但是如果预读的页用不到呢?插入到 LRU 链表的头部,然后满了的话把尾部的页都淘汰掉,从而大大降低 Buffer Pool 的命中率。

大部分情况下局部性原理还是可靠的。

那咱需要咋地解决预读问题呢?

想要避免这种预读效果带来的影响,最好就是让预读页停留在 Buffer Pool 里的时间尽可能的短,让真正被访问的页才移动到 LRU 链表的头部,从而保证真正被读取的热数据留在 Buffer Pool 里的时间尽可能长

MySQL 改进了 LRU 算法,将 LRU 划分了俩个区域:Old 区域 和 Young 区域

【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库Young区域和Old区域在 LRU 链表的占比是由 innodb_old_blocks_pct 参数设置的。

划分这俩个区域后,预读的页就只需要加入到 Old 区域的头部,当页被真正访问的时候,才将页插入 Young 区域的头部。如果预读的页一致没有被访问,就会从 Old 区域移除,这样就不会影响 Young 区域的热点数据了。

假设有一个长度为 10 的 LRU 链表,其中 young 区域占比 70 %,old 区域占比 30 %。
【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库

现在有个编号为 20 的页被预读了,这个页只会被插入到 old 区域头部,而 old 区域末尾的页(10号)会被淘汰掉。

【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库

如果 20 号页一直不会被访问,它也没有占用到 young 区域的位置,而且还会比 young 区域的数据更早被淘汰出去。

如果 20 号页被预读后,立刻被访问了,那么就会将它插入到 young 区域的头部,young 区域末尾的页(7号),会被挤到 old 区域,作为 old 区域的头部,这个过程并不会有页被淘汰。

【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库

虽然通过划分 old 区域 和 young 区域避免了预读失效带来的影响,但是还有个问题无法解决,那就是 Buffer Pool 污染的问题。

何为 Buffer Pool 污染?
当某个 SQL 语句扫描了大量的数据时,在 Buffer Pool 空间比较有限的情况下,可能会将 Buffer Pool 里的所有页都替换出去,导致大量数据被淘汰了,等这些热数据又被再次访问的时候,由于缓存未能命中,就会产生大量的磁盘 IO,MySQL 性能就会急剧下降,这个过程被称为 Buffer Pool 污染
只要咱说无用查询,这种LRU策略就会引起 Buffer Pool 污染。

比如数据量很大的表 t_user,执行了下面这个查询:

select * from t_user where name like "%xiaolin%";

即时这查询的结果就几条,但是这也会引起索引失效,这样就得进行全表扫描(就是说从聚簇索引的叶子节点记录、节点遍历),接下来会发生下面这些过程:

  • 从磁盘读到的页加入到 LRU 链表的 Old 区域头部;
  • 当从页里读取行记录时,也就是页被访问的时候,就要将该页放到 Young 区域头部;
  • 接下来拿行记录的 name 字段和字符串 xiaolin 进行模糊匹配,如果符合条件,就加入到结果集里;
  • 如此往复,直到扫描完表中的所有记录。

经过这一番折腾,原本 Young 区域的热点数据都会被替换掉。

举个例子,假设需要批量扫描:21,22,23,24,25 这五个页,这些页都会被逐一访问(读取页里的记录)。
【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库

在批量访问这些数据的时候,会被逐一插入到 young 区域头部。

【MySQL自身的性能优化】InnoDB 的 Buffer Pool,MySQL进阶,mysql,性能优化,数据库

可以看到,原本在 young 区域的热点数据 6 和 7 号页都被淘汰了,这就是 Buffer Pool 污染的问题。

那咱需要咋地解决 Buffer Pool 污染问题呢?

很多缓存页只是被访问了一次,但是却只因为被访问了一次而进入到 Young 区域,从而导致热点数据被替换了。现在我们只要提高 Young 区域的门槛,这样就可以有效地保证 Young 区域里的热点数据不会被替换掉。

MySQL 是这样做的,进入到 Young 区域条件增加了一个停留在 Old 区域的时间判断。

具体是这样做的,在对某个处在 Old 区域的缓存页进行第一次访问时,就在它对应的控制块中记录下来这个访问时间:

  • 如果后续的访问时间与第一次访问的时间在某个时间间隔内,那么该缓存页就不会被从 old 区域移动到 Young 区域的头部;
  • 如果后续的访问时间与第一次访问的时间不在某个时间间隔内,那么该缓存页移动到 young 区域的头部;

这个间隔时间是由 innodb_old_blocks_time 控制的,默认是 1000 ms。

就是说,只有同时满足【被访问】与【在 Old 区域停留时间超过 1 秒】俩个条件,才会被插入到 Young 区域头部,这样就解决了 Buffer Pool 污染问题。

另外,MySQL 针对 young 区域其实做了一个优化,为了防止 young 区域节点频繁移动到头部。young 区域前面 1/4 被访问不会移动到链表头部,只有后面的 3/4被访问了才会。

5. 脏页什么时候被刷入磁盘?

若修改数据时 Buffer Pool 有这个数据,那么自然会直接修改 BufferPool 控制块映射的缓存页,这样一来这个缓存页就被称为脏页,其控制块也会被放入到 Flush 链表中进行管理。

但是脏页终究还是要被刷入磁盘的,保证缓存和磁盘数据的一致,一般情况下为了不影响性能,都会在一定时机进行批量刷盘。

可能大伙担心,如果脏页还没有来得及刷入到磁盘内,MySQL 宕机了,不就丢失数据了吗?
这个不用担心,InnoDB 考虑到这一点,更新操作采用的是 Write Ahead Log 策略,就是说先写入日志,再写入磁盘,通过 redo log 日志让 MySQL 拥有了崩溃恢复的能力

下面几种情况会触发脏页的刷新:

  • 当 redo log 日志满了的情况下,会主动触发脏页刷新到磁盘;
  • Buffer Pool 空间不足时,需要将一部分数据页淘汰掉,如果淘汰的是脏页,需要先将脏页同步到磁盘;
  • MySQL 认为空闲时,后台线程会定期将适量的脏页刷入到磁盘;
  • MySQL 正常关闭之前,会把所有的脏页刷入到磁盘;

在开启慢 SQL 监控后,如果你发现 【偶尔】会出现用时稍长的 SQL ,这可能是因为脏页在刷新到磁盘时可能会给数据库带来性能开销,导致数据库操作抖动。

如果间断地出现这种现象,就需要调大 Buffer Pool 空间或 Redo Log 日志的大小。

三、总结

为尽可能地降低磁盘 IO,来提高数据库的读写性能,InnoDB 存储引擎设计了一个 缓冲池(Buffer Pool)

Buffer Pool 以页为单位缓冲数据,可以通过 innodb_buffer_pool_size 参数调整缓冲池的大小,默认是 128 M。

Buffer Pool 的内部组成有三种角色,控制块、缓冲页、碎片

InnoDB 通过三种链表来管理缓冲页:

  • Free List(空闲页链表),管理空闲页;
  • Flush List(脏页链表),管理脏页;
  • LRU List(淘汰链表),管理脏页+干净页,将最近且经常查询的数据缓存在其中,而不常查询的数据就淘汰出去。

InnoDB 对 LRU 做了一些优化,我们熟悉的 LRU 算法通常是将最近查询的数据放到 LRU 链表的头部,而 InnoDB 做了以下俩点优化(当然还有很多优化点,这里阐述的是重要的俩个):

  • 将 LRU 链表分为了 Young 和 Old 区域俩个部分加入缓冲池的页优先被放到 Old 区域,页被访问时,才进入到 Young 区域,目的是为了解决预读失效问题
  • 当 【页被访问】 和 【Old 区域停留时间超过 innodb_old_blocks_time 阈值(默认为1秒)】 时,才会将页插入到 Young 区域,否则还是插入到 Old 区域,目的是为了解决批量数据访问(如全表扫描),大量热数据淘汰的问题。

可以通过调整 innodb_old_blocks_pct 参数,设置 young 区域和 old 区域比例。

在开启了慢 SQL 监控后,如果你发现 【偶尔】 会出现一些用时稍长的 SQL,这是因为脏页在刷新到磁盘时导致数据库性能抖动。如果在很短的时间出现这种现象,就需要调大 Buffer Pool 空间或 redo log 日志的大小。

参考文献:文章来源地址https://www.toymoban.com/news/detail-809054.html

  1. 《MySQL是怎样运行的》第十七章 InnoDB 的 Buffer Pool。
  2. 小林的解开Buffer Pool 的面纱

到了这里,关于【MySQL自身的性能优化】InnoDB 的 Buffer Pool的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MySQL 8.0 Reference Manual(读书笔记70节--InnoDB Buffer Pool Configuration)

    When increasing or decreasing innodb_buffer_pool_size , the operation is performed in chunks. Chunk size is defined by the innodb_buffer_pool_chunk_size configuration option, which has a default of 128M.-----innodb_buffer_pool_size的扩容和缩容,都是以innodb_buffer_pool_chunk_size为单位进行的,其默认为128M 。 Buffer pool size must always

    2024年03月24日
    浏览(51)
  • MySQL 8.0 Reference Manual(读书笔记71节--InnoDB Buffer Pool Configuration (2))

    【目的是未来提高并发,减少竞争】 For systems with buffer pools in the multi-gigabyte range, dividing the buffer pool into separate instances can improve concurrency, by reducing contention as different threads read and write to cached pages. This feature is typically intended for systems with a buffer pool size in the multi-gigabyte range. Mu

    2024年03月24日
    浏览(56)
  • MySQL 8.0 Reference Manual(读书笔记72节--InnoDB Buffer Pool Configuration (3))

    主要内容为【热启动】,就是把之前常用的内存数据,按照划定的比例快速重新加载到内存中。 To reduce the warmup period after restarting the server, InnoDB saves a percentage of the most recently used pages for each buffer pool at server shutdown and restores these pages at server startup. The percentage of recently used pa

    2024年03月24日
    浏览(47)
  • MySQL - Buffer Pool

    Buffer Pool 主要用于缓存数据库表的数据页,以提高数据库的读取性能: 缓存数据页 :Buffer Pool 是 MySQL 中用于缓存数据页的内存区域。数据页通常包含数据库表的数据,如行记录等。当查询或读取数据时,MySQL会首先查看Buffer Pool中是否已经缓存了相应的数据页。如果数据页在

    2024年02月07日
    浏览(41)
  • MySQL中的Buffer Pool

            Buffer Pool是数据库的一个内存组件,里面缓存了磁盘上的真实数据,然后我们的Java系统对数据库执行的增删改操作,其实主要就是对这个内存数据结构中的缓存数据执行的。我们先来看一下下面的图,里面就画了数据库中的Buffer Pool内存组件。         接着我

    2024年02月11日
    浏览(52)
  • 2-3-5-3、InnoDB 的 Buffer Pool

    对于使用 InnoDB 作为存储引擎的表来说,不管是用于存储用户数据的索引(包括聚簇索引和二级索引),还是各种系统数据,都是以页的形式存放在表空间中的,而所谓的表空间只不过是 InnoDB 对文件系统上一个或几个实际文件的抽象,也就是说数据说到底还是存储在磁盘上的

    2023年04月08日
    浏览(35)
  • 如何设置innodb_buffer_pool_size

    innodb_buffer_pool_size 是MySQL InnoDB存储引擎的一个重要参数,它决定了InnoDB存储引擎可以使用的内存缓存池的大小。合理的设置 innodb_buffer_pool_size 可以提高MySQL数据库的性能。以下是设置 innodb_buffer_pool_size 的步骤: 确认MySQL的版本: 在MySQL客户端中输入以下命令: 如果MySQL的版本

    2024年02月06日
    浏览(38)
  • innodb_log_file_size 和 innodb_buffer_pool_size

    innodb_log_file_size 和 innodb_buffer_pool_size 是 MySQL 数据库中 InnoDB 存储引擎的两个重要配置参数。它们对数据库的性能和可靠性有着显著的影响。 innodb_log_file_size 参数用于指定 InnoDB 重做日志文件(redo log files)的大小。重做日志是 InnoDB 用来保证事务的持久性和数据库的恢复能力

    2024年03月19日
    浏览(38)
  • 【MySQL】change buffer,buffer pool,redo log,bin log,undo log的作用

    当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InnoDB 会将这些更新操作缓存在 change buffer 中,这样就不需要从磁盘中读入这个数据页了。 在下次查询需要访问这个数据页的时候,将数据页

    2024年02月16日
    浏览(43)
  • mysql undo log、redo log、binlog、mvcc、Buffer Pool的关系

            MySQL中的undo log、redo log、binlog和MVCC(多版本并发控制)是数据库管理系统中的 重要概念,它们各自有不同的作用和关系。 Buffer Pool(缓冲池): 作为InnoDB存储引擎的核心组件,Buffer Pool用于缓存从磁盘读取的数据页。所有的数据修改首先发生在内存中的缓冲池里

    2024年02月02日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包