共计 3046 个字符,预计需要花费 8 分钟才能阅读完成。
这篇文章主要介绍“MySQL 中的数据库缓冲池怎么管理”,在日常操作中,相信很多人在 MySQL 中的数据库缓冲池怎么管理问题上存在疑惑,丸趣 TV 小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”MySQL 中的数据库缓冲池怎么管理”的疑惑有所帮助!接下来,请跟着丸趣 TV 小编一起来学习吧!
对于使用 InnoDB 存储引擎的表来说,是以页为单位来管理存储空间的,作为内存和磁盘之间换入换出的基本粒度。当我们将某页从磁盘中加载到内存中,会进行磁盘 I /O。而磁盘 I / O 的开销非常影响整体性能,如果我们直接从内存中读取相应的页,那岂不是减少了磁盘 I / O 带来的性能损耗,效率则会提升很多。基于此,缓冲池(Buffer Pool)出现了,那么接下来,我们就来谈谈 InnoDB 中的 Buffer Pool。
缓冲池(Buffer Pool)
有人会想,既然缓冲池这么好,那我们将所有数据都存储到缓冲池中不就好了,不不不,缓冲池是操作系统分配的一片连续的内存。而内存相比于磁盘的容量小得多,并且价格昂贵。那么操作系统会给缓冲池分配多少内存呢?
默认情况下,缓冲池的大小为 128MB;
当然,如果你的机器的内存容量非常大,可以在配置文件中配置启动选项参数 innodb_buffer_pool_size 单位是字节,最小不能小于 5MB。
缓冲池的内部结构
缓冲池将操作系统分配的这一片连续的内存,划分成若干个大小默认为 16KB 的页(缓冲页)【此时还没有真正的磁盘页被缓存到 Buffer Pool 中】,当我们从磁盘中换入一个页到缓冲池中,如何分配位置呢?因此就需要一些控制信息来标识这些缓冲池中的缓冲页,这些控制信息都存放在一个叫控制块的内存区域中,与缓冲页一一对应。控制块的大小也是固定的。因此在这片连续的内存空间中,难免会产生内存碎片。综上,缓冲池的内部结构如下:
缓冲页
控制块:页号、缓冲页在缓冲池中的地址、链表节点信息等。
内存碎片【若内存分配得当,内存碎片可有可无】
缓冲池的管理
上面在控制块中提到了链表节点信息,那么链表节点是用来做什么的呢?是为了更好的管理缓冲池中的页。而链表就是用来链接控制块的,因为控制块与缓冲页是一一对应的。
1)空闲链表
将所有空闲的缓冲页对应的控制块链接起来,形成的链表。
解决的问题:从磁盘中换入一个页到缓冲池中,如何区分缓冲池中的哪个页是空闲的呢?而有了空闲链表之后,换入一个磁盘页到缓冲池中时,就直接从空闲链表中获取一个空闲的缓冲页,并将磁盘页中对应的信息填到缓冲页对应的控制块中,然后将该控制块从空闲链表中删除即可。
2)更新链表
若修改了缓冲池中的缓冲页的数据,导致其与磁盘中数据不一致,该页称为脏页。将所有脏页对应的控制块链接起来形成更新链表,在将来的某个时间根据该链表将对应缓存页的数据刷新到磁盘中。
3)LRU 链表
缓冲池的大小是有限的,如果缓存的页超出了缓冲池的大小,即没有空闲的缓冲页了,当有新的页要添加到缓冲池中时,采取 LRU 的策略将旧的缓冲页从缓冲池中移除,然后将新的页添加进来。由于 LRU 链表涉及的内容较多,我们接下来单独介绍。
LRU 链表所蕴含的“哲理”先提一下预读机制
在 I / O 上的优化机制,预读顾名思义,会异步地把某些页面加载到缓冲池中,预计很快就会需要这些页面,这些请求在一个范围内引入所有页面,就是所谓的 局部性原理,目的是减少磁盘 I /O。
了解预读机制之前,先回顾一下 InnoDB 逻辑存储单元:表空间(tablespace)→段(segment)→区(extent)→页(page)。其中特意提一下区,后面会用到:一个区就是物理位置上连续的 64 个页,即一个区的大小是 1MB.
预读机制可以细分为以下两种:
Linear read-ahead(线性预读):一种基于按顺序访问的缓冲池中的页面来预测可能很快需要哪些页面的技术。通过配置参数 innodb_read_ahead_threshold,若顺序访问的某个区的页面超过这个参数的值,会触发异步读请求来读取下一个区中全部的页面到缓冲池中。
Random read-ahead(随机预读):可以根据缓冲池中已经存在的页面预测何时可能需要页面,而不管这些页面的读取顺序如何。如果在缓冲池中发现同一个区段的 13 个连续页面,InnoDB 会异步发出一个请求来预取该区段的剩余页面。通过配置变量 innodb_random_read_ahead 来控制随机读的。
传统 LRU 对缓冲页是如何管理的呢?
利用 LRU 算法对最近最少使用的缓冲页进行管理,形成对应的链表,方便用于淘汰。
当访问一个页【即最近访问】
该页在缓冲池中,将对应控制块移至 LRU 链表头部
该页不在缓冲池中,淘汰尾部最近最少使用的页,从磁盘中加载进来该页并放在 LRU 链表头部
那么为什么 InnoDB 不使用这么直观的 LRU 算法呢?原因如下:
预读失效
预读到缓冲池中的页都会放到 LRU 链表的头部,但其中很多页可能并不会被读取。
缓冲池污染
很多使用频率较低的页加载到缓冲池中,会把使用频率较高的页从缓冲池中淘汰掉。比如全表扫描
优化后的 LRU 对缓冲页是如何管理的呢?
基于上述缺点,优化后的具体方法将传统 LRU 链表划分为两部分:热数据区域【年轻区】冷数据区域【老年区】
热数据区域【年轻区】:使用频率高的缓冲页
冷数据区域【老年区】:使用频率低的区域
结构简图如下所示:
如图所示,热数据区域与冷数据区域分别占用不同比例,那么我们可以通过 innodb_old_blocks_pct 启动选项来控制冷数据区域所占比例。
改进后的 LRU 如何更好的解决预读失效问题呢?
某个页在初次加载到缓冲池中时,先淘汰掉冷数据区域尾部的控制块(即其对应的页淘汰掉),然后新页对应的控制块会先放到冷数据区域的头部。
若后续该页不被进行访问就会慢慢从冷数据区域中被淘汰掉,总体不会影响热数据区域访问频繁的缓冲页。
改进后的 LRU 如何更好的解决缓冲池污染问题呢?
先说结论,并没有很好的优化这个问题,原因如下【以全表扫描为例】:
某个初次访问的页同样会放到冷数据区域的头部,但后续访问又会将其放到热数据区域的头部,这样同样会把访问频率较高的页给挤掉。
那么到底该如何解决缓冲池污染问题呢?
缓冲池引入了冷数据区域时间窗口机制,即只有后续访问该页与第一访问该页的时间间隔大于规定的窗口值,就会将该页从冷数据区域移到热数据区域的头部。小于规定的窗口值,就不会进行移动操作。
同样,窗口值可通过 innodb_old_blocks_time 参数【单位 ms】来设置,默认 1000ms,而 1s 会筛选掉大部分像全表扫描这样的操作。比如在一次全表扫描过程中,多次访问一个页面的时间间隔不会超过 1s。
缓冲池 VS 查询缓存
缓冲池和查询缓存是一个东西吗?→不是
缓冲池会尽量将经常使用的数据保存起来,在 MySQL 进行页面读操作的时候,首先会判断该页面是否在缓冲池中,如果存在就直接读取,如果不存在,就会通过内存或磁盘将页面存放到缓冲池中再进行读取。
查询缓存是提前把查询结果缓存起来,这样下次不需要执行就可以直接拿到结果。需要说明的是,在 MySQL 中的查询缓存,不是缓存查询计划,而是查询对应的结果。命中条件苛刻,而且只要数据表发生变化,查询缓存就会失效,因此命中率低。
到此,关于“MySQL 中的数据库缓冲池怎么管理”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注丸趣 TV 网站,丸趣 TV 小编会继续努力为大家带来更多实用的文章!