(一)大白话MySQL执行SQL的流程

(二)大白话InnoDB存储引擎的架构设计

(三)大白话MySQL Binlog是什么?

(四)MySQL的Buffer Pool内存结构

(五)MySQL的Buffer Pool的free链表、flush链表、LRU链表

(六)MySQL是如何基于冷热数据分离的方案,来优化LRU算法?

(七)MySQL是如何将LRU链表的使用性能优化到极致的?

文章目录

  • 1、LRU链表淘汰缓存页的问题
    • 1.1 MySQL的预读机制带来的问题
    • 1.2 哪些情况下会触发MySQL的预读机制?
    • 1.3 另外一种可能导致频繁被访问的缓存页被淘汰的场景
  • 2、基于冷热数据分离优化LRU算法
    • 2.1 数据页第一次被加载到缓存放在哪个位置?
    • 2.2 冷数据区域的缓存页什么时候会被放入到热数据区域?
  • 3、基于冷热数据分离优化后的LRU链表,是如何解决之前的问题的?
    • 3.1 预读机制和全表扫描加载进来的缓存页,能进热数据区域吗?
    • 3.2 如果此时缓存页不够了,需要淘汰一些缓存,会怎么样?
    • 3.3 之前的一大堆问题解决了吗?
    • 3.4 总结

1、LRU链表淘汰缓存页的问题

前面章节已经讲过Buffer Pool的缓存页全部用光了,会把LRU链表尾部的缓存页数据刷入磁盘,腾出空闲页。新加载的数据会放在LRU链表头部,刚访问的数据也会移动到LRU链表的头部。

但是这样的一个LRU机制在实际运行过程中,会有问题吗?

1.1 MySQL的预读机制带来的问题

首先会带来隐患的就是MySQL的预读机制,这个所谓预读机制,就是当MySQL从磁盘上加载一个数据页的时候,可能会连带着把这个数据页相邻的其他数据页,也加载到缓存里去

举个例子,假设现在有两个空闲缓存页,然后在加载一个数据页的时候,连带着把它相邻的一个数据页也加载到缓存里去了,正好每个数据页放入一个空闲缓存页!

但是接下来,实际上只有一个缓存页是被访问了,另外一个通过预读机制加载的缓存页,其实并没有任何访问,但此时这两个缓存页可都在LRU链表的头部,如下图所示:

从上图可以看到,前两个缓存页都是刚加载进来的,但是此时第二个缓存页是通过预读机制捎带着加载进来的,它也被放到了链表的前面,但是实际没人访问它。除了第二个缓存页之外,第一个缓存页,以及尾巴上两个缓存页,都是一直有人访问的那种缓存页。

在这个时候,假如没有空闲缓存页了,那么此时要加载新的数据页了,就要从LRU链表的尾部把所谓的“最近最少使用的一个缓存页”给拿出来,刷入磁盘,然后腾出来一个空闲缓存页了。而如果你把上图中LRU尾部的那个缓存页刷入磁盘然后清空,你觉得合理吗?

这是绝对不合理的,最合理的反而是把上图中LRU链表的第二个通过预读机制加载进来的缓存页给刷入磁盘和清空,毕竟它几乎是没什么人会访问的!

1.2 哪些情况下会触发MySQL的预读机制?

现在我们已经理解了预读机制一下子把相邻的数据页加载进缓存,放入LRU链表前面的隐患了,预读机制加载进来的缓存页可能根本不会有人访问,结果它却放在了LRU链表的前面,此时可能会把LRU尾部的那些被频繁访问的缓存页刷入磁盘中!

所以我们来看看,到底哪些情况下会触发MySQL的预读机制呢?

  • MySQL有一个参数是innodb_read_ahead_threshold,默认值是56,意思就是如果顺序的访问了一个区里的多个数据页,访问的数据页的数量超过了这个阈值,此时就会触发预读机制,把下一个相邻区中的所有数据页都加载到缓存里去

  • MySQL还有另外一个场景会触发预读机制,如果Buffer Pool里缓存了一个区里的13个连续的数据页,而且这些数据页都是比较频繁会被访问的,此时就会直接触发预读机制,把这个区里的其他的数据页都加载到缓存里去。这个机制是通过参数innodb_random_read_ahead来控制的,默认值是OFF,也就是这个默认规则是关闭的。

所以默认情况下,主要是第一个规则可能会触发预读机制,一下子把很多相邻区里的数据页加载到缓存里去,这些缓存页如果一下子都放在LRU链表的前面,而且它们其实并没什么人会访问的话,那就会如上图,导致本来就在缓存里的一些频繁被访问的缓存页在LRU链表的尾部。

这样的话,一旦要把一些缓存页淘汰掉,刷入磁盘,腾出来空闲缓存页,就会如上所述,把LRU链表尾部一些频繁被访问的缓存页给刷入磁盘和清空掉了!这是完全不合理的,并不应该这样!

那么可能有人问了,MySQL为什么要设计一个预读机制,为什么要把相邻的数据页一起写入Buffer Pool中去呢?
其实道理很简单,为了减少磁盘IO,提升性能。MySQL设计了预读机制,也就是说如果在一个区内,你顺序读取了好多数据页了,比如数据页01~数据页56都被你依次顺序读取了,MySQL会判断,你可能接着会继续顺序读取后面的数据页。

那么此时他就干脆提前把后续的一大堆数据页(比如数据页57~数据页72)都读取到Buffer Pool里去,那么后续你再读取数据页60的时候,是不是就可以直接从Buffer Pool里拿到数据了?

当然理想是上述那样,而事实是你预读的一大堆数据页要是占据了LRU链表的前面部分,可能这些预读的数据页压根儿后续没人会使用,那这个预读机制就是在捣乱了。

1.3 另外一种可能导致频繁被访问的缓存页被淘汰的场景

接着我们讲另外一种可能导致频繁被访问的缓存页被淘汰的场景,那就是全表扫描,意思就是类似如下的SQL语句:

SELECT * FROM USERS;

此时查询没加任何一个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后你访问了这个缓存页,它才会给你把缓存页放到热数据区域的链表头部去。

所以我们看上面的图,文字说明做了一点改动,是数据加载到缓存页之后过了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过后还经常被访问的缓存页,都放在了热数据区域里,它们进行了冷热数据的隔离!

这样的话,在淘汰缓存的时候,一定是优先淘汰冷数据区域几乎不怎么被访问的缓存页的!
其实好多缓存都是基于冷热数据隔离思想完成的,也希望大家好好吸收这种冷热数据隔离的思想,尽可能让热数据和冷数据分开,避免冷数据影响热数据的访问!

(六)大白话MySQL是如何基于冷热数据分离的方案,来优化LRU算法?相关推荐

  1. MySQL基于冷热数据分离优化的LRU刷盘策略

    MySQL基于冷热数据分离优化的LRU刷盘策略 前言 对于计算机刷盘这个概念相信大家都非常熟悉了,刷盘策略,其实在操作系统层面来说的话就是页面置换算法. 不知道各位朋友们还记得页面置换算法有哪些吗? ...

  2. Alluxio基于冷热数据分离的元数据管理策略

    文章目录 前言 Alluxio内部元数据管理架构 Alluxio的支持异步写出功能的自定义Cache实现 引用 前言 上篇文章末尾,笔者聊到了一种叫做分层元数据管理模式.它主张的思想是将元数据进行分级 ...

  3. 云上如何做冷热数据分离

    目录(?)[-] 前言 如何区分冷热数据 冷数据的特点 冷数据的处理 场景一极低频度的查询 冷数据备份 使用冷数据 场景二需要经常查询 方案一冷热RDS 方案二冷数据转存RDS PostgreSQL版 ...

  4. mysql 冷热数据分离_elasticsearch冷热数据读写分离

    Elasticsearch5.5冷热数据读写分离 前言 冷数据索引:查询频率低,基本无写入,一般为当天或最近2天以前的数据索引 热数据索引:查询频率高,写入压力大,一般为当天数据索引 当前系统日志每日 ...

  5. 关于redis的冷热数据分离

    一.概述 当前KV数据库从存储介质可以分为两种模式,一种是以内存为主持久化为辅,如memcache(无持久化).redis等:一种是以持久化为主内存为辅,如ssdb(基于leveldb/rocksdb ...

  6. hybriddb mysql移植_HybridDB for MySQL 实现在线与离线数据分离的实践

    本文将重点介绍HybridDB for MySQL 实现在线与离线数据分离的实践,特别推荐! 核心业务简介 任务中心汇聚了集团的所有工作流任务,并提供统一的入口给用户处理集团的工作任务. 面临主要问题 ...

  7. 文件服务器冷热数据划分,游戏服务器冷热数据分离方案

    冷热数据分离 当前场景: gamserver启动时,会将所有数据加载到内存中,提高读取数据的性能.但是有很多数据很可能是不常用甚至再也用不到的数据,将这些数据加载到内存中需要占用更多的内存,极大的浪费 ...

  8. Shopee ClickHouse 冷热数据分离存储架构与实践

    本文首发于微信公众号"Shopee技术团队". 摘要 Shopee ClickHouse 是一款基于开源数据库 ClickHouse 做二次开发.架构演进的高可用分布式分析型数据库 ...

  9. 【论文研读】-基于对偶种群的约束多目标优化进化算法-补充材料

    基于对偶种群的约束多目标优化进化算法-补充材料 Supplementary File of "A Dual-Population based Evolutionary Algorithm f ...

最新文章

  1. 特征匹配--GMS: Grid-based Motion Statistics for Fast, Ultra-robust Feature Correspondence
  2. 动态规划入门 洛谷P1108 低价购买
  3. 1930年的上海是什么样
  4. (建议收藏)相对靠谱的国内大学排行榜
  5. python中列表数据汇总和平均值_python的列表List求均值和中位数实例
  6. python实现雪花飘落效果_jQuery实现雪花飘落效果
  7. 10g索引的作用实验1
  8. echarts柱状图变色
  9. Linux 迅雷 chrome插件,Chrome(Chromium)迅雷下载支持扩展1.1测试版【更新】
  10. 被纳入MSCI ACWI全球指数,达达集团的财报透露出什么信号?
  11. 网络安全简历如何写?
  12. XML 大于号 小于号 处理
  13. 浅谈中国程序员的四个层次,你在第几层?
  14. 微信小程序获取今日天气预报api 免费接口
  15. ubuntu16.04下ORB_SLAM2的配置
  16. VS2017 下QT工程不能生成moc文件的解决方法
  17. DDIA读书笔记 | 第七章:事务
  18. 【深度之眼cs231n第七期】笔记(四)
  19. 关于深圳户口从集体户口迁入朋友家庭户口的相关手续总结
  20. JS回调函数——简单易懂有实例

热门文章

  1. C++:定义嵌套的克伦肖柯蒂斯正交规则(附完整源码)
  2. HCIP笔记(15)
  3. 关于某天发现笔记本电脑开机启动超级慢的解决办法之一:(本质问题)
  4. 汉兰达汽车发动机怠速抖动故障诊断方案设计
  5. 1479_英飞凌AURIX TC275 iLLD例子中的CPU同步实现分析
  6. spring boot校园二手销售网站 毕业设计-附源码161417
  7. 基于C++的校园十大青年投票系统
  8. Java模板和地图模板网址收藏
  9. 微电子新手入门之Cadence常用仿真——NMOS管的跨导
  10. 上链行动|李银科致辞 2019国际区块链产融峰会启动