内存回收

要实现key过期,有三种思路。立即过期(主动淘汰),惰性过期(被动淘汰),定期过期。

  • 立即过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
  • 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。例如所有的查询都会调用expirelfNeeded方法判断是否过期。第二种情况,每次写入key时,发现内存不够,调用activeExpireCycle方法释放一部分内存。
  • 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

Redis中同时使用了惰性过期和定期过期两种过期策略,并不是实时地清除过期的key

淘汰策略

Redis的内存淘汰策略,是指当内存使用达到最大内存极限时,需要使用淘汰算法来决定清理掉哪些数据,以保证新数据的存入。

  • volatile-lru:根据LRU算法删除设置了超时属性(expire)的键,直到腾出足够内存为止。如果没有可删除的键对象,回退到noeviction策略
  • allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够内存为止。
  • volatile-lfu:在带有过期时间的键中选择最不常用的。
  • allkeys-lfu:在所有的键中选择最不常用的,不管数据有没有设置超时属性。
  • volatile-random:在带有过期时间的键中随机选择。
  • allkeys-random:随机删除所有键,直到腾出足够内存为止。
  • volatile-ttl:根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。
  • noeviction:默认策略,不会删除任何数据,拒绝所有写入操作并返回客户端错误信息(error)0OM command not allowed when used memory,此时Redis只响应读操作。

如果没有设置tt或者没有符合前提条件的key被淘汰,那么volatile-lru.
volatile-random、volatile-ttl相当于noeviction(不做内存回收)。

从后缀的算法名来看:

  • LRU,Least Recently Used:最近最少使用。判断最近被使用的时间,目前最远的数据优先被淘汰。
  • LFU,Least Frequently Used,最不常用,按照使用频率删除,4.0版本新增。
  • random,随机删除。

从前缀针对的对象来分:

  • volatile是针对设置了ttl的key,allkeys是针对所有key。

LRU淘汰原理

LRU是一个很常见的算法,比如InnoDB的Buffer Pool也用到了LRU。

传统的LRU:通过链表+HashMap实现,设置链表长度,如果新增或者被访问,就移动到头节点。超过链表长度,末尾的节点被删除。

问题:如果基于传统LRU算法实现Redis LRU会有什么问题?
答:需要额外的数据结构存储,消耗内存。还有,假设A在10秒内被访问了5次,而B在10秒内被访问了3次。因为B最后一次被访问的时间比A要晚,在同等的情况下,A反而先被回收。

Redis LRU对传统的LRU算法进行了改良,通过随机采样来调整算法的精度。如果淘汰策略是LRU,则根据配置的采样值maxmemory_samples(默认是5个),随机从数据库中选择m个key,淘汰其中热度最低的key对应的缓存数据。所以采样参数m配置的数值越大,就越能精确的查找到待淘汰的缓存数据,但是也消耗更多的CPU计算,执行效率降低。

那如何找出热度最低的数据?

Redis中所有对象结构都有一个Iru字段,且使用了unsigned的低24位,这个字段用来记录对象的热度。对象被创建时会记录Iru值。在被访问的时候也会更新Iru的值。但并不是获取系统当前的时间戳,而是设置为全局变量server.lruclock的值。

typedef struct redisObject{unsigned type:4; /*对象的类型,包括:OBJ STRING、OBJ LIST、OBJHASH、OBJ SET、OBJZSET*/unsigned encoding:4; /*具体的数据结构*/unsigned Iru:LRU_BITS; /*24位,对象最后一次被命令程序访问的时间,与内存回收有关*//*LRU time(relative to global Iru clock)or *LFU data(least significant 8 bits frequency *and most significant 16 bits access time)*/int refcount; /*引用计数。当refcount为0的时候,表示该对象已经不被任何对象引用,则可以进行垃圾回收了*/void *ptr; /*指向对象实际的数据结构*/
}robj;

server.lruclock的值怎么来的?

Redis中有个定时处理的函数serverCron,默认每100毫秒调用函数updateCachedTime更新一次全局变量的server.ruclock的值,它记录的是当前unix时间戳。

void updateCachedTime(int update daylight info) {server.ustime = ustime(); server.mstime = server.ustime /1000;server.unixtime = server.mstime /1000;if (update-daylight_info){struct tm tm; time_t ut = server.unixtime; localtime_r(&ut,&tm); server.daylight_active = tm.tm_isdst;}
}

为什么不获取精确的时间而是放在全局变量中?不会有延迟的问题吗?

因为这样函数查询key更新数据的Iru热度值时,就不用每次调用系统函数time,可以提高执行效率。

当对象里面已经有了LRU字段的值,就可以评估对象的热度了。

unsigned long long estimateObjectidleTime(robj *o){unsigned long long lruclock= LRU_CLOCK();if(lruclock >=o->lru){return(lruclock - o->lru)*LRU_CLOCK_RESOLUTION;}else{return(lruclock +(LRU_CLOCK_MAX - o->lru))*LRU_CLOCK_RESOLUTION;}
}

函数estimateObjectldleTime评估指定对象的lru热度,方法就是对象的lru值和全局的server.lruclock的差值越大(越久没有得到更新),该对象热度越低。server.lruclock只有24位,按秒为单位来表示才能存储194天。当超过24bit能表示的最大时间的时候,它会从头开始计算。在这种情况下,可能会出现对象的lru大于server.lruclock的情况,如果这种情况出现那么就两个相加而不是相减来求最久的key。

LFU

typedef struct redisObject{unsigned type:4; /*对象的类型,包括:OBJ STRING、OBJ LIST、OBJHASH、OBJ SET、OBJZSET*/unsigned encoding:4; /*具体的数据结构*/unsigned Iru:LRU_BITS; /*24位,对象最后一次被命令程序访问的时间,与内存回收有关*//*LRU time(relative to global Iru clock)or *LFU data(least significant 8 bits frequency *and most significant 16 bits access time)*/int refcount; /*引用计数。当refcount为0的时候,表示该对象已经不被任何对象引用,则可以进行垃圾回收了*/void *ptr; /*指向对象实际的数据结构*/
}robj;

当这24bits用作LFU时,其被分为两部分:

  • 高16位用来记录访问时间(单位为分钟,Idt,last decrement time)
  • 低8位用来记录访问频率,简称counter(logc,logistic counter)
    counter是用基于概率的对数计数器实现的,8位可以表示百万次的访问频率。

对象被读写的时候,Ifu的值会被更新。

void updateLFU(robj *val) {unsigned long counter = LFUDecrAndReturn(val); counter = LFULogIncr(counter); val->lru= (LFUGetTimelnMinutes()<<8) | counter;
}

这里并不是访问一次,计数就加1增长的速率由一个参数决定,Ifu-log-factor越大,counter增长的越慢。

如果一段时间热点高,就一直保持这个热度,肯定也是不行的,体现不了整体频率。所以,没有被访问的时候,计数器还要递减。计数器怎么递减呢?减少的值由衰减因子lfu-decay-time(分钟)来控制,如果值是1的话,N分钟没有访问,计数器就要减少N,Ifu-decay-time越大,衰减越慢。

redis基础篇——内存回收相关推荐

  1. Redis基础篇(万丈高楼平地起):核心底层数据结构

    微信原文链接,排版更舒适. 大家好,我是小龙.近期有很多小伙伴私信我Redis怎么做持久化?集群方案怎么做?分布式锁怎么实现?可是我发现,每次简答完一个问题他还有其他类似问题,或则各个知识点不能串通形 ...

  2. Redis基础篇 高速缓存技术与Redis的庐山真面目 AUTHOR:LBY

    最新更新 8.24 创建 8.25 更新第二章 客户端的使用以及配置参数 8.25 Redis的数据 事务 百年沉浮困低谷,莫以今朝度兴衰, 人生终有高飞日,傲振才华过沧海. 什么是Redis Red ...

  3. Redis学习笔记 - 内存回收、对象共享、对象的空转时长

    参考:<<Redis设计与实现>> 注:这本书是基于Redis3.0版本写的,和后面的版本有点差异 一.内存回收 C语言不具备自动内存回收功能,所以Redis在自己的对象系统中 ...

  4. Redis基础篇-(入门、数据类型、通用命令、Jedis)

    windows系统环境 目录 Redis入门 问题现象: 罪魁祸首---关系型数据库 解决思路 Nosql 常见的Nosql数据库: 解决方案(电商场景): Redis简介 Redis的应用 Redi ...

  5. 【檀越剑指大厂--redis】redis基础篇

  6. 内存淘汰算法_「承」Redis 原理篇——Redis 的内存回收机制

    前言 关于 Redis 的"起承转合",我前面已经用五个篇章的长度作了一个 Redis 基础篇--"起"篇的详细阐述,相信大家无论之前有没有接触过 Redis, ...

  7. 视频教程-Redis进阶教程—基础篇-NoSQL

    Redis进阶教程-基础篇 雅座Java架构师,架构开发公司百万级订单支付平台 叶向阳 ¥49.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免费看 APP订阅课程,领取优惠 ...

  8. Redis进阶教程—基础篇-叶向阳-专题视频课程

    Redis进阶教程-基础篇-217人已学习 课程介绍         该系列教程涵盖了redis的方方面面,大亮点是实战经验分享总结.系列视频包含Redis基础篇.Redis提升篇.从零编写Redis ...

  9. 【承】Redis 原理篇——关于 Redis 中的事务

    前言 关于 Redis 的"起承转合",我前面已经用五个篇章的长度作了一个 Redis 基础篇--"起"篇的详细阐述,相信大家无论之前有没有接触过 Redis, ...

最新文章

  1. SPU表管理之保存SPU表数据
  2. Day9-Postfix
  3. ES6 一些常用使用
  4. Redis 内存压缩实战,学习了!
  5. Silverlight与数据库的三种互操作[源代码]
  6. concurrency_Java Concurrency Essentials教程
  7. sqlserver note
  8. ananconda3安装(python3.8)
  9. php++背景自适应屏幕宽度,背景图片+自适应屏幕
  10. 管理系统中的计算机应用数据库系统,自考管理系统中的计算机应用重点: 数据库系统(1)...
  11. java面试第十七天
  12. Mac隐藏技巧:右键点按,在访达中玩出点新花样
  13. cad无法修复图形文件_CAD应用技巧:DWG图形的“瘦身”
  14. c数据库读写分离和负载均衡策略
  15. ubuntu 20.04安装谷歌拼音输入法
  16. 医学知识图谱构建关键技术及研究进展
  17. bat文件转exe工具分享
  18. win10专业版修改家庭计算机,win10家庭版升级专业版的最完美的方法_win10专业版技巧...
  19. 想让游戏代入感更强,要靠他。。
  20. HTML中关于使用 innerHTML 动态创建DOM节点时,相关事件(如onclick等)失效的解决方案

热门文章

  1. Bilateral Filter、Cross/Joint Bilateral Filter
  2. 校园跳蚤市场信息管理c语言,C语言 习题课.ppt
  3. 硅谷安全大腕弓峰敏和卜峥加盟滴滴
  4. 【网络流24题】搭配飞行员(最大流+二分图匹配)
  5. 扰码器(三)并行扰码器综述及设计思路
  6. 网络电视测试软件,2018三款智能电视屏幕检测软件,当贝市场良心推荐
  7. 记录——python编程从入门到实践十二章练习题
  8. 安装Linux出现致命错误,安装liunx出现致命错误,为什么,如何解决?
  9. 一文搞定bp神经网络,bp神经网络的实现
  10. 网页css实现文字竖向排版的几种方法