(学习笔记-内存管理)如何避免预读失效和缓存污染的问题?

news2024/11/16 8:26:47

传统的LRU算法存在这两个问题:

  • 预读失效 导致的缓存命中率下降
  • 缓存污染 导致的缓存命中率下降

Redis的缓存淘汰算法是通过实现LFU算法来避免 [缓存污染] 而导致缓存命中率下降的问题(redis 没有预读机制)

Mysql 和 Linux操作系统是通过改进LRU算法来避免 [预读失效和缓存污染] 而导致缓存命中率下降的问题。


Linux和MySQL的缓存

Linux操作系统的缓存

在应用程序读取文件的数据的时候,Linux操作系统是会对读取的文件数据进行缓存的,会缓存在文件系统中的 Page Cache(页缓存):

 Page Cache 属于内存空间里的数据,由于内存访问比磁盘访问快很多,在下一次访问相同的数据就不需要通过I/O了,命中缓存就直接返回数据即可。

因此,Page Cache 起到了加速访问数据的作用。

MySQL的缓存

MySQL的数据是存储在磁盘里的,为了提升数据库的读写性能,Innodb存储引擎设计了一个缓冲池( Buffer Pool),Buffer Pool 属于内存空间里的数据。

 有了缓冲池后:

  • 当读取数据时,如果数据存在于Buffer Pool中,客户端就会直接读取Buffer Pool中的数据,否则再去磁盘中读取。
  • 当修改数据时,首先是修改Buffer Pool中数据所在的页,然后将其页设置为脏页,最后由后台线程将脏页写入到磁盘。

传统LRU是如何管理内存数据的?

Linux的Page Cache 和 MySQL的Buffer Pool的大小是有限的,并不能无限地缓存数据,对于一些频繁访问的数据我们希望可以一直留在内存中,而一些很少访问的数据希望可以在某些时机可以淘汰掉,从而保证内存不会因为满了而导致无法再缓存新的数据,同时还能保证常用数据留在内存中。

最容易想到的就是LRU(Least recently used)算法。

LRU算法一般是用 [链表] 作为数据结构来实现的,链表头部的数据是最近使用的,而链表末尾的数据是最久没被使用的。当空间不够了,就淘汰最久没被使用的节点,也就是链表末尾的数据,从而腾出内存空间。

因为 Linux 的 Page Cache 和 MySQL 的 Buffer Pool 缓存的基本数据单位都是页(Page)单位,所以后续以「页」名称代替「数据」

传统的LRU算法的实现思路:

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

比如下图,假设LRU链表长度为 5 ,LRU链表从左到右有编号为1,2,3,4,5的页。

 如果访问了3号页,因为3号页已经在内存了,所以把3号页移动到链表头部即可,表示最近被访问了。

 接下来如果访问了 8 号页,因为8号页不在内存中,而且LRU链表的长度为5,所以必须要淘汰数据,以腾出内存空间缓存 8 号页,于是就会淘汰末尾的 5 号页,然后再将 8 号页插入到头部:

 传统的LRU算法并没有被Linux和Mysql使用,因为传统的LRU算法无法避免两个问题:

  • 预读失效导致缓存命中率下降
  • 缓存污染导致缓存命中率下降

预读失效怎么办?

什么是预读机制?

Linux操作系统为基于Page Cache的读缓存机制提供预读机制

  • 应用程序只想读取磁盘文件A的offset 为 0-3KB范围内的数据,由于磁盘的基本读写单位为block(4KB),于是操作系统至少会读 0-4KB内容,这恰好可以在一个page中装下
  • 但是操作系统出于空间局部性原理(靠近当前被访问数据的数据,在未来很大概率会被访问到),会选择将磁盘块offset[4KB ,8KB]、[8KB, 12KB]、[12KB, 16KB]都加载到内存中,于是额外在内存中申请了 3 个page;

 上图中,应用程序利用read系统调用读取4KB数据,实际上内核使用预读机制完成了16KB数据的读取,也就是通过一次磁盘顺序读将多个Page数据装入Page Cache。

这样下次读取 4KB 数据后面的数据的时候,就不用从磁盘读取了,直接在Page Cache即可命中数据。因此,预读机制的好处是减少了磁盘I/O次数,提高系统磁盘I/O吞吐量

MYSQL Innodb存储引擎的Buffer Pool也有类似的预读机制,MySQL从磁盘加载页时,会提前把它邻近的页一并加载进来,目的是为了减少磁盘 IO。

预读失效会带来什么问题?

预读失效提前加载进来的页,并没有被访问,相当于这个预读工作白做了。

如果使用传统的LRU算法,就会把 [预读页] 放到LRU链表头部,而当内存空间不够的时候,还需要把末尾的页淘汰掉。

如果这些 [预读页]一直不被访问到,就会出现一个奇怪的问题:不会被访问的预读页占用了LRU链表前排的位置,而末尾淘汰的页可能是热点数据,这样就大大降低了缓存命中率

如何避免预读失效造成的影响?

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

Linux操作系统和MySQL Innodb通过改进传统的LRU链表来避免预读失效带来的影响,具体的改进分别如下:

  • Linux操作系统实现了两个LRU链表:活跃LRU链表非活跃LRU链表
  • MySQL的Innodb存储引擎是在一个 LRU 链表上划分 2 个区域:young区域old区域

这两个改进方式,设计思想类似,都是将数据划分为冷数据和热数据,然后分别进行LRU算法

不再像传统的LRU算法一样,所有数据都只用一个LRU算法管理。

Linux如何避免预读失效带来的影响?

Linux系统实现两个LRU链表:活跃LRU链表 和 非活跃LRU链表

  • active list :存放的是最近被访问过(活跃)的内存页
  • inactive list:存放的是很少被访问(非活跃)的内存页

有了这两个LRU链表后,预读页就只需要加入到 inactive list区域的头部,当页被真正访问时,才将页插入到active list 的头部。如果预读的页一直没有被访问,就会从inactive list移除,这样就不会影响active list中的热点数据。

MySQL是如何避免预读失效带来的影响?

MySQL 的 Innodb 存储引擎是在一个 LRU 链表上划分来 2 个区域,young 区域 和 old 区域

young 区域在 LRU 链表的前半部分,old 区域则是在后半部分,这两个区域都有各自的头和尾节点,如下图:

 young区域与old区域在LRU链表中的占比关系并不是一比一的,而是63:37(默认比例)的关系。

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


缓存污染怎么办?

虽然Linux和 MySQL通过改进传统的LRU数据结构,避免了预读失效的影响。

但是如果还是使用 [只要数据被访问一次,就将数据加入到活跃的LRU链表头部(或young区域)] 这种方式的话,那么还存在缓存污染的问题

当我们在批量读取数据的时候,由于数据被访问了一次,这些大量数据就会被加入到 [活跃LRU链表] 里,然后之前缓存在活跃LRU链表里的热点数据就全部被淘汰了,如果这些大量的数据在很长一段时间内都不会被访问的话,那么整个活跃LRU链表(Young区域)就被污染了

缓存污染会带来什么问题?

缓存污染带来的影响是很致命的,等这些热数据又被再一次访问的时候,由于缓存没有命中,就会产生大量的磁盘I/O,系统性能会急剧下降。

我们以MySQL为例,Linux发生缓存污染的现象也是类似。

当某个SQL语句扫描了大量的数据时,在Buffer Pool空间比较有限的情况下,可能会将Buffer Pool里的所有页都替换出去,导致大量热数据都被淘汰了,等这些热数据又被再一次访问的时候,由于缓存未命中,就会产生大量的磁盘I/O,MySQL的性能会急剧下降。

注意,缓存污染并不只是查询语句查询出了大量数据才出现的问题,即使查询出的结果很小,也会造成混存污染

比如,在一个数据量非常大的表,执行以下语句:

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

可能这个查询出来的结果只有几条,但是由于这条语句会发生索引失效,所以这个查询过程是全表扫描,接着会发生如下过程:

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

由于这条 SQL 语句访问的页非常多,每访问一个页,都会将其加入 young 区域头部,那么原本 young 区域的热点数据都会被替换掉,导致缓存命中率下降。那些在批量扫描时,而被加入到 young 区域的页,如果在很长一段时间都不会再被访问的话,那么就污染了 young 区域。

举个例子,假设需要批量扫描:21,22,23,24,25 这五个页,这些页都会被逐一访问(读取页里的记录)

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

 原本在young区域的 6 和 7 号页都被淘汰了,而批量扫描的页基本占满了young区域,如果这些页在很长一段时间内都不会被访问,那么就对young区域造成了污染。

如果 6和 7 号页是热点数据,那么在淘汰后,后续有SQL再次读取 6 和 7 号页时,由于未被命中。就要从磁盘中重新读取,降低了MySQL的性能,这就是缓存污染带来的影响。

如何避免缓存污染造成的影响?

前面的LRU算法只要数据被访问一次,就将数据加入活跃LRU链表(young 区域),这种LRU算法进入活跃LRU链表的门槛太低了。正是因为门槛太低,才导致在发生缓存污染的时候,很容易就将原本在活跃LRU链表里的热点数据淘汰了。

所以,只要我们提高进入到活跃LRU链表(young 区域)的门槛,就能有效地保证活跃LRU链表(young 区域)里的热点数据不会被轻易替换掉

Linux操作系统和MySQL Innodb的存储引擎分别是这样提高门槛

  • Linux操作系统:在内存页被访问第二次的时候,才将页从inactive list 升级到active list里
  • MySQL Innodb:在内存页被访问第二次的时候,并不会马上将该页从old区域升级到young区域,因为还要进行停留在old区域的时间判断
    • 如果第二次访问时间与第一次访问的时间在1秒内,那么该页就不会被从old区域升级到young区域
    • 如果第二次的访问时间与第一次访问时间超过 1 秒,那么该页就从 old 区域升级到 young 区域。

提高了进入活跃LRU链表(或 young区域)的门槛后,就很好地避免了缓存污染带来的影响。

在批量读取数据的时候,如果这些大量数据只会被访问一次,那么它们就不会进入到活跃LRU链表(或 young 区域),也就不会把热点数据淘汰,只会待在非活跃LRU链表(old 区域中),后续很快也被淘汰。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/818020.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MPLS 虚拟专用网--跨域OptionA方案

域间概述 随着MPLS VPN解决方案的广泛应用,服务的终端用户的规格和范围也在增长,在一个企业内部的站点数目越来越大,某个地理位置与另外一个服务提供商相连的需求变得非常的普遍,例如国内运营商的不同城域网之间,或相互协作的运营商的骨干网之间都存在着跨越不同自治域的…

在 Amazon DocumentDB 里处理 Decimal128类型数据的解决方案

一道简单的数学题 在开始今天的内容之前,我们先计算一道简单的数学题。0.1 X 0.2 ?我相信很多人都笑了,0.02,这是一个孩童都可以回答得出的答案。我们用这道数学题问一下计算机,看看结果又是怎样。 亚马逊云科技开发…

关于我对刚开始学Java的小白想分享的内容:

编程是很有魅力的,让很多人为之痴迷 如果你是初学者,俗称小白,不妨看看下述内容: 文章目录 1. Java 简介1.1 特性介绍1.简单性2. 面向对象3. 分布式4. 健壮性5. 安全性6. 体系结构中立7. 可移植性8. 解释型9. 高性能10. 多线程11…

【javascript】关于path-package

背景 一个老的vue项目,预览pdf文件的时候,电子签章不显示 解决方案 由于是老项目,升级版本存在风险,然后又找到一些解决方案,都是修改源码,修改源码就引出了今天的主题 path-package,我们需要…

maven项目、springboot项目复制文件进来后没反应、不编译解决方法

问题如下 把文件复制进springboot项目后,没反应,不编译。 解决 在maven工具框中选择compile工具,运行即可。

cicd实验

系列文章目录 文章目录 系列文章目录一、1.2. 二、安装并使用1.安装gitlab2.//Jenkins安装3. 总结 一、 1. 2. 二、安装并使用 需要三台服务器一台安装gitlab 192.168.169.10 第二台负责 安装jenkins 192.168.169.20 第三台是负责业务 192.168.169.30 1.安装gitlab yum in…

JVM总结笔记

JVM JVM是什么?JVM 的主要组成部分JVM工作流程JVM内存模型直接内存与堆内存的区别:堆栈的区别Java会存在内存泄漏吗?简述Java垃圾回收机制垃圾收集算法轻GC(Minor GC)和重GC(Full GC)新生代gc流程JVM优化与JVM调优 JVM是什么? JVM是Java Virtual Mach…

【自用记录】常见的第三方接口加密签名方式(ASCll码字典序、URL键值对、 SHA-256加密、MD5加密)

案例1: 案例2: 以上第三方接口都用类似的加密签名方式,两者有类似的部分: 方案1的: $kdata = array(parkId=>$parkId,ts => $ts,serviceCode=>getParkingPaymentList,reqId => $reqId,plateNo => $car_code,//车牌 可为空pageIndex => 1,//第几页page…

15. Spring AOP 的实现原理 代理模式

目录 1. 代理模式 2. 静态代理 3. 动态代理 3.1 JDK 动态代理 3.2 CGLIB 动态代理 4. JDK 动态代理和 CGLIB 动态代理对比 5. Spring代理选择 6. Spring AOP 实现原理 6.1 织入 7. JDK 动态代理实现 8. CGLIB 动态代理实现 9. 总结 1. 代理模式 代理模式&#xf…

<Git/Gerrit>版本控制Git以及代码评审Gerrit常见的开发操作

下载安装,环境变量配置直接百度; 1.代码拉取: 操作步骤:在正确配置完git的条件下:在本地文件夹下:右键–Git Bash -Here: 出现如下弹窗: 在黑窗口输入:代码拉取路径(一般都是把命令和路径在外面写好,直接粘贴(在窗口右键,Paste,回车)) 代码…

linux系统磁盘性能监视工具iostat

目录 一、iostat介绍 二、命令格式 三、命令参数 四、参考命令:iostat -c -x -k -d 1 (一)输出CPU 属性值 (二)CPU参数分析 (三)磁盘每一列的含义 (四)磁盘参数分…

AI生成式视频技术来临:Runway Gen-2文本生成视频

Runway Gen-2的官方网站提供了一种文本生成视频的工具。以下是对该工具的介绍: 文本生成视频:Runway Gen-2是一个创新的在线工具,可以将文本转化为视频。用户只需输入文本描述或句子,Runway Gen-2就能自动生成相应的视频内容。这…

uni-ajax网络请求库使用

uni-ajax网络请求库使用 uni-ajax是什么 uni-ajax是基于 Promise 的轻量级 uni-app 网络请求库,具有开箱即用、轻量高效、灵活开发 特点。 下面是安装和使用教程 安装该请求库到项目中 npm install uni-ajax编辑工具类request.js // ajax.js// 引入 uni-ajax 模块 import ajax…

【最短路算法】SPFA

引入 在计算机科学的世界里,算法就像是星空中的繁星,各自闪烁着智慧的光芒。它们沉默而坚定,像是一群不语的哲人,默默地解答着世界的问题。 算法的步骤,如同优美的诗行,让复杂的问题在流转的字符中得以释…

PHP8的常量-PHP8知识详解

常量和变量是构成PHP程序的基础,在PHP8中常量的这一节中,主要讲到了定义常量和预定义常量两大知识点。 一、定义常量 定义常量也叫声明常量。在PHP8中,常量就是一个标识符(名字),一旦定义(声明&…

ansible-playbook编写 lnmp 剧本

ansible-playbook编写 lnmp 剧本 vim /opt/lnmp/lnmp.yaml执行剧本 ansible-playbook lnmp.yaml

WebDAV之π-Disk派盘 + DEVONthink

DEVONthink是由一家来自德国的老牌软件开发商发布的「知识管理」软件,运行于 Mac/iOS 平台。官方自己定位为全方位(中文环境下略有遗憾)帮助你实现知识管理,可以称之为“模块级”应用了。 DEVONthink还支持各种云服务同步,文件管理您的终极文件管理应用、文件、图片与连接远…

Android Ble蓝牙App(一)扫描

Ble蓝牙App(一)扫描 前言正文一、基本配置二、扫描准备三、扫描页面① 增加UI布局② 点击监听③ 扫描处理④ 广播处理 四、权限处理五、扫描结果① 列表适配器② 扫描结果处理③ 接收结果 六、源码 前言 关于低功耗的蓝牙介绍我已经做过很多了&#xff0…

基于SHARC+®单核的ADSP-21567KBCZ6、ADSP-21566BBCZ4、ADSP-21566KBCZ4高性能DSP处理器产品

ADSP-2156x 处理器的速度高达 1 GHz,属于 SHARC 系列产品。ADSP-2156x 处理器基于 SHARC 单核。ADSP-2156x SHARC 处理器是 SIMD SHARC 系列数字信号处理器 (DSP) 中的一款产品,采用 ADI 的超级哈佛架构。这些 32 位/40 位/64 位浮点处理器已针对高性能音…

Practice3|922. 按奇偶排序数组 II、143. 重排链表

922. 按奇偶排序数组 II 1.题目: 给定一个非负整数数组 nums, nums 中一半整数是 奇数 ,一半整数是 偶数 。 对数组进行排序,以便当 nums[i] 为奇数时,i 也是 奇数 ;当 nums[i] 为偶数时, i…