| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 大数据 -> (六)MySQL是如何基于冷热数据分离的方案,来优化LRU算法? -> 正文阅读 |
|
[大数据](六)MySQL是如何基于冷热数据分离的方案,来优化LRU算法? |
(五)MySQL的Buffer Pool的free链表、flush链表、LRU链表 (六)MySQL是如何基于冷热数据分离的方案,来优化LRU算法? 文章目录1、LRU链表淘汰缓存页的问题前面章节已经讲过Buffer Pool的缓存页全部用光了,会把LRU链表尾部的缓存页数据刷入磁盘,腾出空闲页。新加载的数据会放在LRU链表头部,刚访问的数据也会移动到LRU链表的头部。 但是这样的一个LRU机制在实际运行过程中,会有问题吗? 1.1 MySQL的预读机制带来的问题首先会带来隐患的就是MySQL的预读机制,这个所谓预读机制,就是当MySQL从磁盘上加载一个数据页的时候,可能会连带着把这个数据页相邻的其他数据页,也加载到缓存里去。 举个例子,假设现在有两个空闲缓存页,然后在加载一个数据页的时候,连带着把它相邻的一个数据页也加载到缓存里去了,正好每个数据页放入一个空闲缓存页! 但是接下来,实际上只有一个缓存页是被访问了,另外一个通过预读机制加载的缓存页,其实并没有任何访问,但此时这两个缓存页可都在LRU链表的头部,如下图所示: 从上图可以看到,前两个缓存页都是刚加载进来的,但是此时第二个缓存页是通过预读机制捎带着加载进来的,它也被放到了链表的前面,但是实际没人访问它。除了第二个缓存页之外,第一个缓存页,以及尾巴上两个缓存页,都是一直有人访问的那种缓存页。 在这个时候,假如没有空闲缓存页了,那么此时要加载新的数据页了,就要从LRU链表的尾部把所谓的“最近最少使用的一个缓存页”给拿出来,刷入磁盘,然后腾出来一个空闲缓存页了。而如果你把上图中LRU尾部的那个缓存页刷入磁盘然后清空,你觉得合理吗? 这是绝对不合理的,最合理的反而是把上图中LRU链表的第二个通过预读机制加载进来的缓存页给刷入磁盘和清空,毕竟它几乎是没什么人会访问的! 1.2 哪些情况下会触发MySQL的预读机制?现在我们已经理解了预读机制一下子把相邻的数据页加载进缓存,放入LRU链表前面的隐患了,预读机制加载进来的缓存页可能根本不会有人访问,结果它却放在了LRU链表的前面,此时可能会把LRU尾部的那些被频繁访问的缓存页刷入磁盘中! 所以我们来看看,到底哪些情况下会触发MySQL的预读机制呢?
所以默认情况下,主要是第一个规则可能会触发预读机制,一下子把很多相邻区里的数据页加载到缓存里去,这些缓存页如果一下子都放在LRU链表的前面,而且它们其实并没什么人会访问的话,那就会如上图,导致本来就在缓存里的一些频繁被访问的缓存页在LRU链表的尾部。 这样的话,一旦要把一些缓存页淘汰掉,刷入磁盘,腾出来空闲缓存页,就会如上所述,把LRU链表尾部一些频繁被访问的缓存页给刷入磁盘和清空掉了!这是完全不合理的,并不应该这样! 那么可能有人问了,MySQL为什么要设计一个预读机制,为什么要把相邻的数据页一起写入Buffer Pool中去呢? 那么此时他就干脆提前把后续的一大堆数据页(比如数据页57~数据页72)都读取到Buffer Pool里去,那么后续你再读取数据页60的时候,是不是就可以直接从Buffer Pool里拿到数据了? 当然理想是上述那样,而事实是你预读的一大堆数据页要是占据了LRU链表的前面部分,可能这些预读的数据页压根儿后续没人会使用,那这个预读机制就是在捣乱了。 1.3 另外一种可能导致频繁被访问的缓存页被淘汰的场景接着我们讲另外一种可能导致频繁被访问的缓存页被淘汰的场景,那就是全表扫描,意思就是类似如下的SQL语句:
此时查询没加任何一个where条件,会导致直接一下子把这个表里所有的数据页,都从磁盘加载到Buffer Pool里去。这个时候可能会一下子就把这个表的所有数据页都一一装入各个缓存页里去! 此时可能LRU链表中排在前面的一大串缓存页,都是全表扫描加载进来的缓存页!那么如果这次全表扫描过后,后续几乎没用到这个表里的数据呢?此时LRU链表的尾部,可能全部都是之前一直被频繁访问的那些缓存页! 然后当你要淘汰掉一些缓存页腾出空间的时候,就会把LRU链表尾部一直被频繁访问的缓存页给淘汰掉了,而留下了之前全表扫描加载进来的大量的不经常访问的缓存页,这样也是绝对不合理的! 2、基于冷热数据分离优化LRU算法所以为了解决上一讲我们说的简单的LRU链表的问题,真正MySQL在设计LRU链表的时候,采取的实际上是冷热数据分离的思想。 之前一系列的问题,说白了,不都是因为所有缓存页都混在一个LRU链表里,才导致的么? 所以真正的LRU链表,会被拆分为两个部分,一部分是热数据,一部分是冷数据,这个冷热数据的比例是由innodb_old_blocks_pct参数控制的,默认值是37,也就是说冷数据占比37%。 这个时候,LRU链表实际上看起来是下面这样子的。 2.1 数据页第一次被加载到缓存放在哪个位置?首先数据页第一次被加载到缓存的时候,这个时候缓存页会被放在LRU链表的哪个位置呢? 实际上这个时候,缓存页会被放在冷数据区域的链表头部,我们看下面的图,也就是第一次把一个数据页加载到缓存页之后,这个缓存页实际上是被放在下图箭头的位置,也就是冷数据区域的链表头部位置。 2.2 冷数据区域的缓存页什么时候会被放入到热数据区域?接着我们来思考一个问题,第一次被加载了数据的缓存页,都会不停的移动到冷数据区域的链表头部,如上图所示。 那么要知道,冷数据区域的缓存页肯定是会被使用的,那么冷数据区域的缓存页什么时候会放到热数据区域呢? 实际上肯定很多人会想,只要对冷数据区域的缓存页进行了一次访问,就立马把这个缓存页放到热数据区域的头部行不行呢?如下图所示。 其实这也是不合理的,如果你刚加载了一个数据页到那个缓存页,它是在冷数据区域的链表头部,然后立马(在1ms以内)就访问了一下这个缓存页,之后就再也不访问他了呢?难道这种情况你也要把那个缓存页放到热数据区域的头部吗? 所以MySQL设定了一个规则,设计了一个innodb_old_blocks_time参数,默认值1000,也就是1000毫秒。也就是说,必须是一个数据页被加载到缓存页之后,在1s之后,如果访问了这个缓存页,它才会被挪动到热数据区域的链表头部去。 因为假设你加载了一个数据页到缓存去,然后过了1s之后你还访问了这个缓存页,说明你后续很可能会经常要访问它,这个时间限制就是1s,因此只有1s后你访问了这个缓存页,它才会给你把缓存页放到热数据区域的链表头部去。 3、基于冷热数据分离优化后的LRU链表,是如何解决之前的问题的?现在我们已经看完了LRU链表的冷热数据分离的方案,那么我们接着看这个冷热数据分离之后的LRU链表,他是如何解决之前遇到的一大堆问题的? 首先我们思考一下,在这样的一个LRU链表方案下,预读机制以及全表扫描加载进来的一大堆缓存页,他们会放在哪里? 明显是放在LRU链表的冷数据区域的前面! 假设这个时候热数据区域已经有很多被频繁访问的缓存页了,你会发现热数据区域还是存放被频繁访问的缓存页的,只要热数据区域有缓存页被访问,它还是会被移动到热数据区域的链表头部去。 所以此时你看下图,你会发现,预读机制和全表扫描加载进来的一大堆缓存页,此时都在冷数据区域里,跟热数据区域里的频繁访问的缓存页,是独立隔离开的,相互之间没关系的! 3.1 预读机制和全表扫描加载进来的缓存页,能进热数据区域吗?接着我们看第二个问题,预读机制和全表扫描机制加载进来的缓存页,什么时候能进热数据区域呢? 如果你仅仅是一个全表扫描的查询,此时你肯定是在1s内就把一大堆缓存页加载进来,然后就访问了这些缓存页一下,通常这些操作1s内就结束了。 所以基于目前的一个机制,可以确定的是,这种情况下,那些缓存页是不会从冷数据区域转移到热数据区域的! 除非在冷数据区域里的缓存页,在1s之后还被人访问了,那么此时它们就会判定为未来可能会被频繁访问的缓存页,然后移动到热数据区域的链表头部去! 3.2 如果此时缓存页不够了,需要淘汰一些缓存,会怎么样?那就很简单了,直接就是可以找到LRU链表中的冷数据区域的尾部的缓存页,他们肯定是之前被加载进来的,而且加载进来1s过后都没人访问过,说明这个缓存页压根儿就没人愿意去访问他!他就是冷数据! 所以此时就直接淘汰冷数据区域的尾部的缓存页,刷入磁盘就可以了,我们看下图。 3.3 之前的一大堆问题解决了吗?在这样的一套缓存页分冷热数据的加载方案,以及冷数据转化为热数据的时间限制方案,还有就是淘汰缓存页的时候优先淘汰冷数据区域的方案,基于这套方案,大家会发现,之前发现的问题,完美的被解决了。 因为那种预读机制以及全表扫描机制加载进来的数据页,大部分都会在1s之内访问一下,之后可能就再也不访问了,所以这种缓存页基本上都会留在冷数据区域里。然后频繁访问的缓存页还是会留在热数据区域里。 当你要淘汰缓存的时候,优先就是会选择冷数据区域的尾部的缓存页,这就是非常合理的了,它不会让刚加载进来的缓存页占据LRU链表的头部,频繁访问的缓存页在LRU链表的尾部,淘汰的时候淘汰尾部的频繁访问的缓存页了! 这就是LRU链表冷热数据分离的一套机制。 3.4 总结通过这几篇文章的学习,我们已经彻底搞定了LRU链表的设计机制,刚加载数据的缓存页都是放冷数据区域的头部的,1s过后被访问了才会放热数据区域的头部,热数据区域的缓存页被访问了,就会自动放到头部去。 这样的话,实际上冷数据区域放的都是加载进来的缓存页,最多在1s内被访问过,之后就再也没访问过的冷数据缓存页! 而加载进来之后在1s过后还经常被访问的缓存页,都放在了热数据区域里,它们进行了冷热数据的隔离! 这样的话,在淘汰缓存的时候,一定是优先淘汰冷数据区域几乎不怎么被访问的缓存页的! |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年3日历 | -2025/3/4 7:44:23- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |