Redis的内存淘汰策略分析

概念

  • LRU 是按访问时间排序,发生淘汰的时候,把访问时间最久的淘汰掉。
  • LFU 是按频次排序,一个数据被访问过,把它的频次 + 1,发生淘汰的时候,把频次低的淘汰掉。

几种LRU策略

以下集中LRU测率网上有很多,我自己结合项目加以整理。也可以选择跳过。

1. 普通LRU

在这里插入图片描述

  1. 一般使用双向链表+map实现,新数据加入链表表头
  2. 每当缓存命中时,将数据移动到表头
  3. 链表长度超过设定值,将尾部数据淘汰
    缺点:当热点数据较多时,随后来了一次偶发性的操作,操作的数据较多,容易将热点数据淘汰出去。

2. LRU-K

考虑到传统LRU的缺点,改进措施是记录数据的被访问次数。维护两个LRU队列,一个数据访问次数队列,一个缓存队列。当访问达到预设值K时,加入到缓存队列中。对于偶然性的访问非热点数据时,命中次数不够,不会加入到缓存队列中,则不会挤出热点数据。
在这里插入图片描述

  1. 命中数据后,加入访问次数队列中,被访问次数+1,同普通LRU的逻辑。
  2. 淘汰数据。
  3. 当访问次数超过预设值,从此队列中移除,加入到缓存队列中,按照访问时间排序。
  4. 缓存队列中的数据再次被命中,按照访问时间顺序排序。
  5. 淘汰数据。
    缺点:需要谨慎考虑K值的设定,设定过大会导致数据很难被淘汰。整体内存消耗也偏高。同时也要按照访问时间重排序。

3. 2Queue

优化重排序问题。
在这里插入图片描述

  1. 数据被访问后,加入到FIFO队列中。
  2. FIFO按照访问时间进行淘汰。
  3. 当数据再次被访问时,则移到LRU队列头部。
  4. 数据再次被访问,移动到头部。
  5. LRU队列淘汰。

4. Multi Queue

同2Queue,增加了多个FIFO队列,按照预设条件,从左到右逐级提升等级。随着数据被淘汰,从右向左逐级降级。
在这里插入图片描述

Redis的LRU/LFU策略

内存淘汰策略配置

  • maxmemory: 指定限制内存大小。默认=0,表示无限制。
  • maxmemory_policy: 指定的淘汰策略,目前有以下几种:
    • noeviction: 默认值,不处理。
    • allkeys-lru:对所有的key都采取LRU淘汰策略。
    • volatile-lru:仅对设置了过期时间的key采取LRU淘汰。
    • allkeys-random: 随机回收key。
    • Volatile-random: 随机回收设置了过期时间的key。
    • volatile-ttl:仅淘汰设置了过期时间的key,并淘汰生存时间更小的key。
    • Volatile-lfu: 对设置了过期时间的key采取LFU策略。
    • Allkeys-lfu: 对全部key采取LFU策略
  • maxmemory_samples: 随机采样精度。官方表示配10更接近真实的LRU策略。

2. Redis的LRU策略

  • 给每个key记录一个lru time。
  • 每次访问key的时候,更新key的lru time。
  • 按照策略配置。在一定范围内,找访问时间最早的key,将其淘汰。
  • 具体看下面的源码分析。

3. Redis的LRU策略的缺陷

//从左到右是时间轴,每个波浪线代表一个时间单位
//竖线是当前时间点~~~~~A~~~~~A~~~~~A~~~~A~~~~~A~~~~~A~~|
~~B~~B~~B~~B~~B~~B~~B~~B~~B~~B~~B~~B~|
~~~~~~~~~~C~~~~~~~~~C~~~~~~~~~C~~~~~~|
~~~~~D~~~~~~~~~~D~~~~~~~~~D~~~~~~~~~D|//可以看到,如果4个key中非要淘汰一个,肉眼看出来一定是淘汰D,因为它访问的次数最少。但是由于
//当前时间点,D再次被访问,它的LRU时间又被更新了,导致D不会被淘汰,范围淘汰了C。
//这种情况就不合理,因此redis4.0版本后引入了LFU策略。

4. Redis的LFU策略

struct redisObject {unsigned type:4;unsigned encoding:4;//对于lru而言,这里记录了lru time//对于lfu而言,高24位记录LRU time,低8位记录计数器的值(最大可表示255)unsigned lru:LRU_BITS; int refcount;void *ptr;
};
  • 给每个key记录一个计数count。
  • 由于只有8位长度,最多只能表示255,因此采用了一个因子控制count的增长速度。
  • 新的key加入进来,会设置为预设值(LFU_INIT_VAL),以免为0直接被淘汰。
  • 每当这个key被访问时,按照增长逻辑,增长count值。
  • 每当这个key被放入到淘汰候选池内,则会降低count值。

5. 源码分析

当执行命令,命中数据时,更新数据:

//查找缓存数据时,最终都会调用此函数
//如: lookupKeyRead(), lookupKeyWrite() 
robj *lookupKey(redisDb *db, robj *key, int flags) {dictEntry *de = dictFind(db->dict,key->ptr);...robj *val = dictGetVal(de);if (val) { if (不能在执行子任务的时候 && !(flags & LOOKUP_NOTOUCH)){if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {//如果是LFU策略,这里就增长LFU计数updateLFU(val);} else {//如果是LRU策略,这里就更新lru timeval->lru = LRU_CLOCK();}}} else {...}return val;
}

然后在处理指令时,如果发现缓存达到了预设值,会触发内存淘汰策略:

int processCommand(client *c) 
{
...if (server.maxmemory && !isInsideYieldingLongCommand()) {//达到了预设值了,这里开始处理内存淘汰逻辑int out_of_memory = (performEvictions() == EVICT_FAIL);...}...
}
//伪代码
int performEvictions(void)
{if (如果是LRU或者LFU策略或者volatile-ttl策略){while (memFree < memNeedFree) {for (i = 0; i < server.dbnum; i++) {db = server.db+i;dict = (如果淘汰策略是针对allkeys) ? db->dict : db->expires;if (只要dict里有数据) {evictionPoolPopulate(i, dict, db->dict, 淘汰候选池);}}}}else if (如果是两种随机策略){for (i = 0; i < server.dbnum; i++) {//用一个静态变量next_db,这样每次都不会只命中第一个dbj = (++next_db) % server.dbnum;db = server.db + j;dict = (如果淘汰策略是针对allkeys) ? db->dict : db->expires;bestkey = 随机找一个keybreak;}}for (k = 淘汰候选池大小-1; k >= 0; k--) {bestkey = 从候选池里逆序找真实存在的key    }if (bestkey) {最后,在这里回收这个key;memFree += 新释放的内存;}//while执行太久了,break掉if (流逝的时间 > eviction_time_limit_us) {break;}
}

开始处理淘汰策略,并将合适的key放入淘汰候选池内,这个池是已从左到右从小到大排好序的:

void evictionPoolPopulate(int dbid, dict *sampledict, //如果策略是allkey,则是db->dict,//如果是volatile则为db->expiresdict *keydict, //db->dictstruct evictionPoolEntry *pool) //这个是候选池
{//这里开始采样//server.maxmemory_samples是一个预设值,官方建议设置为10count = dictGetSomeKeys(sampledict, samples, server.maxmemory_samples);for (j = 0; j < count; j++) {...if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) {//因为每次key在被loopupKey的时候,都会更新它自己的lru时间//这个函数:lru当前时间 - 当前这个key的lru时间idle = estimateObjectIdleTime(o);} else if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {//取lfu的计数器的计数,这里是255 - 数值,因为最小访问次数的要被淘汰//注意这里顺带给它减少了LFU计数idle = 255-LFUDecrAndReturn(o);} else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {//常量 - validle = ULLONG_MAX - (long)dictGetVal(de);} else {}...}
}

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

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

相关文章

浙江大学漏洞报送证书

获取来源&#xff1a;edusrc&#xff08;教育漏洞报告平台&#xff09; url&#xff1a;主页 | 教育漏洞报告平台 兑换价格&#xff1a;20金币 获取条件&#xff1a;提交浙江大学任意中危或以上级别漏洞

selenium xpath定位

selenium-xpath定位 <span style"background-color:#2d2d2d"><span style"color:#cccccc"><code class"language-javascript">element_xpath <span style"color:#67cdcc"></span> driver<span styl…

asp.net core把所有接口和实现类批量注入到容器

要将所有接口和实现类批量注入到容器&#xff0c;可以使用反射和循环来实现自动批量注册。下面是一种示例方法&#xff1a; 创建一个扩展方法&#xff0c;用于批量注册接口和实现类。 public static class ServiceCollectionExtensions {public static IServiceCollection Re…

在gitlab中指定自定义 CI/CD 配置文件

文章目录 1. 介绍2. 配置操作3. 配置场景3.1 CI/CD 配置文件在当前项目step1&#xff1a;在当前项目中创建目录&#xff0c;编写流水线文件存放在该目录中step2&#xff1a;在当前项目中配置step3&#xff1a;运行流水线测试 3.2 CI/CD 配置文件位于外部站点上step1&#xff1a…

掌动智能性能压力测试优势有哪些

企业通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。本文将介绍性能压力测试的价值及主要优势! 一、性能压力测试的价值 1、评估系统能力&#xff1a;有助于参数的基准测试&#xff0c;可以度量系统的响应时间;还有助于检查系统是否可…

OpenCV-Python小应用(九):通过灰度直方图检测图像异常点

OpenCV-Python小应用&#xff08;九&#xff09;&#xff1a;通过灰度直方图检测图像异常点 前言前提条件相关介绍实验环境通过灰度直方图检测图像异常点代码实现输出结果 参考 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&#xff…

基于springboot实现高校党务平台管理系统【项目源码】

基于springboot实现高校党务平台管理系统演示 Java技术 Java是由Sun公司推出的一门跨平台的面向对象的程序设计语言。因为Java 技术具有卓越的通用性、高效性、健壮的安全性和平台移植性的特点&#xff0c;而且Java是开源的&#xff0c;拥有全世界最大的开发者专业社群&#x…

【flink-sql实战】flink 主键声明与upsert功能实战

文章目录 一. flink 主键声明语法二. 物理表创建联合主键表三. flink sql使用 一. flink 主键声明语法 主键用作 Flink 优化的一种提示信息。主键限制表明一张表或视图的某个&#xff08;些&#xff09;列是唯一的并且不包含 Null 值。 主键声明的列都是非 nullable 的。因此主…

hdlbits系列verilog解答(100输入逻辑门)-39

文章目录 一、问题描述二、verilog源码三、仿真结果一、问题描述 构建一个具有 100 个输入in[99:0]的组合电路。 有 3 个输出: out_and: output of a 100-input AND gate. out_or: output of a 100-input OR gate. out_xor: output of a 100-input XOR gate. 二、verilog源…

ubuntu16.04 交叉编译 mbedtls

在为客户交叉编译项目时需要依赖 mbedtls&#xff0c; 客户的机器是 arm64 的 ubuntu 16.04&#xff0c; 交叉编译过程中遇到几个问题。 首先&#xff0c; mbedtls 需要依赖 python, 在 cmake 的过程中&#xff0c; 如果不是使用系统默认的 cmake 可能会导致&#xff0c;mbedt…

189. 轮转数组

给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数 实现方法1:每次移动一位&#xff0c;直到k个位置。 void roate1(vector<int>& nums, int k) {if(k < 0){return;}int len nums.size();int stride k % len;for…

蓝桥杯算法双周赛心得——被替换的身份证(分类讨论)

大家好&#xff0c;我是晴天学长&#xff0c;分类讨论一定要细节啊&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .被替换的身份证 2) .算法思路 假设一方获胜 1.接受数据 2.假设潜梦醒 无非就是&am…

计算机组成原理之处理器(单周期)

引言 处理器的实现方式决定了时钟周期长度和CPI。实现方式有单周期与流水线&#xff0c;本篇谈谈单周期处理器。 目前CPU的频率一般是3GHZ/4GHZ&#xff0c;但是频率是有极限值的&#xff0c;受cycletime影响 基本的RISC-V实现 存储指令&#xff1a;ld,sd算术逻辑指令 &…

AI 绘画 | Stable Diffusion 进阶 Embeddings(词嵌入)、LoRa(低秩适应模型)、Hypernetwork(超网络)

前言 Stable Diffusion web ui&#xff0c;除了依靠文生图&#xff08;即靠提示词生成图片&#xff09;&#xff0c;图生图&#xff08;即靠图片提示词生成图片&#xff09;外&#xff0c;这两种方式还不能满足我们所有的绘图需求&#xff0c;于是就有了 Embeddings&#xff0…

idea代码快捷键Mac版

1、查询任何东西 双击 Shift2、文件内查找 Command F 3、文件内替换 Command R4、全局查找&#xff08;根据路径&#xff09; Command Shift F5、在当前文件跳转到某一行的指定处 Command L6、退回 / 前进到上一个操作的地方 Command Option 方向键左Command Opt…

京东数据分析:2023年Q3户外鞋服市场分析报告(冲锋衣行业销售数据分析)

从露营、骑行、徒步、桨板、垂钓、飞盘、滑雪到如今的city walk&#xff0c;近两年户外运动已经成为了年轻人新的生活方式。户外运动的爆发也刺激了人们对于鞋服在穿搭、场景化、专业性功能等方向的需求&#xff0c;户外鞋服市场迎来增长。 而全国性的降温则带给目前的户外鞋服…

json数据格式的理解(前+后)

什么是JSON&#xff1a; JSON&#xff08;JavaScript Object Notation&#xff09;是一种广泛使用的数据交换格式&#xff0c;它在前端和后端开发中都扮演着重要的角色。 JSON 的结构&#xff1a; JSON 数据由大括号 {} 包围&#xff0c;表示对象。 对象中的数据以键值对形式…

React 组件和 prop:入门教程

组件和 props React 的核心概念是组件。组件是可重用的代码块&#xff0c;用于描述 UI 中的一部分内容。组件可以通过 props 接收输入&#xff0c;通过 state 管理本地数据&#xff0c;并通过事件处理程序响应用户的操作。 例如&#xff0c;我们可以创建一个名为 Button 的组件…

vue.cli 中怎样使用自定义的组件

目录 创建自定义组件 注册并使用自定义组件 全局注册自定义组件 使用 Props 传递数据 总结 前言 在Vue CLI中使用自定义组件是构建交互式和模块化Web应用的重要一环。Vue CLI为开发者提供了使用自定义组件的灵活性和简便性。通过Vue CLI&#xff0c;你可以创建、注册和使…

【SA8295P 源码分析 (三)】120 - QNX i2c_tool 工具编译、使用方法介绍(I2C / CCI) 及 源码分析

【SA8295P 源码分析】120 - QNX i2c_tool 工具编译、使用方法介绍(I2C / CCI) 及 源码分析 一、i2c_tool 获取、编译 及 使用方法1.1 i2c_tool 源码开发1.2 i2c_tool 源码编译1.3 i2c_tool 使用方法介绍二、i2c_tool 源码分析2.1 头文件 及 数据结构体定义2.2 i2c_write_reg(…