Redis缓存过期和淘汰策略

题记:
文章内容输出来源:拉勾教育Java高薪训练营。
本篇文章是 Redis 学习课程中的一部分笔记。

Redis缓存过期和淘汰策略

Redis性能高:
官方数据
读:110000次/s
写:81000次/s
长期使用,key会不断增加,Redis作为缓存使用,物理内存也会满。内存与硬盘交换(swap) 虚拟内存 ,频繁IO 性能急剧下降

一、maxmemory

1、不设置的场景

Redis的key是固定的,不会增加
Redis作为DB使用,保证数据的完整性,不能淘汰 , 可以做集群,横向扩展
缓存淘汰策略:禁止驱逐 (默认)

2、设置的场景

Redis是作为缓存使用,不断增加Key
maxmemory : 默认为0 不限制
问题:超过物理内存后性能急剧下架,甚至崩溃。内存与硬盘交换(swap) 虚拟内存 ,频繁IO 性能急剧下降
设置多少?与业务有关
1个Redis实例,保证系统运行 1 G ,剩下的就都可以设置Redis
如:物理内存的3/4
slaver : 留出一定的内存
在redis.conf中配置

maxmemory 1024mb

命令: 获得maxmemory数

CONFIG GET maxmemory

设置maxmemory后,当趋近maxmemory时,通过缓存淘汰策略,从内存中删除对象

二、expire

1、expire数据结构

在Redis中可以使用expire命令设置一个键的存活时间(ttl: time to live),过了这段时间,该键就会自动被删除。

2、expire的使用

expire命令的使用方法如下:
expire key ttl(单位秒)
Redis缓存过期和淘汰策略_第1张图片
3、expire原理
Redis缓存过期和淘汰策略_第2张图片
上面的代码是Redis 中关于数据库的结构体定义,这个结构体定义中除了 id 以外都是指向字典的指针,其中我们只看 dict 和 expires。
dict 用来维护一个 Redis 数据库中包含的所有 Key-Value 键值对,expires则用于维护一个 Redis 数据库中设置了失效时间的键(即key与失效时间的映射)。
当我们使用 expire命令设置一个key的失效时间时,Redis 首先到 dict 这个字典表中查找要设置的key是否存在,如果存在就将这个key和失效时间添加到 expires 这个字典表。
当我们使用 setex命令向系统插入数据时,Redis 首先将 Key 和 Value 添加到 dict 这个字典表中,然后将 Key 和失效时间添加到 expires 这个字典表中。
简单地总结来说就是,设置了失效时间的key和具体的失效时间全部都维护在 expires 这个字典表中。

三、删除策略

Redis的数据删除有定时删除、惰性删除和主动删除三种方式。
Redis目前采用惰性删除+主动删除的方式。

1、定时删除

在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。
需要创建定时器,而且消耗CPU,一般不推荐使用。

2、惰性删除

在key被访问时如果发现它已经失效,那么就删除它。
调用expireIfNeeded函数,该函数的意义是:读取数据之前先检查一下它有没有失效,如果失效了就删除它。

int expireIfNeeded(redisDb *db, robj *key) {
      
	//获取主键的失效时间 
	long long when = getExpire(db,key); 
	//假如失效时间为负数,说明该主键未设置失效时间(失效时间默认为-1),直接返回0 
	if (when < 0) return 0; 
	//假如Redis服务器正在从RDB文件中加载数据,暂时不进行失效主键的删除,直接返回0 
	if (server.loading) return 0; 
	... 
	//如果以上条件都不满足,就将主键的失效时间与当前时间进行对比,如果发现指定的主键 
	//还未失效就直接返回0 
	if (mstime() <= when) return 0; 
	//如果发现主键确实已经失效了,那么首先更新关于失效主键的统计个数,然后将该主键失 
	//效的信息进行广播,最后将该主键从数据库中删除 
	server.stat_expiredkeys++; 
	propagateExpire(db,key);
	return dbDelete(db,key); 
}

3、主动删除

在redis.conf文件中可以配置主动删除策略,默认是no-enviction(不删除)

maxmemory-policy allkeys-lru

四、LRU

LRU (Least recently used) 最近最少使用,算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
最常见的实现是使用一个链表保存缓存数据,详细算法实现如下:

  • 新数据插入到链表头部;
  • 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
  • 当链表满的时候,将链表尾部的数据丢弃。
  • 在Java中可以使用LinkHashMap(哈希链表)去实现LRU

让我们以用户信息的需求为例,来演示一下LRU算法的基本思路:
1、假设我们使用哈希链表来缓存用户信息,目前缓存了4个用户,这4个用户是按照时间顺序依次从链表右端插入的。
Redis缓存过期和淘汰策略_第3张图片
2、此时,业务方访问用户5,由于哈希链表中没有用户5的数据,我们从数据库中读取出来,插入到缓存当中。这时候,链表中最右端是最新访问到的用户5,最左端是最近最少访问的用户1。
Redis缓存过期和淘汰策略_第4张图片
3、接下来,业务方访问用户2,哈希链表中存在用户2的数据,我们怎么做呢?我们把用户2从它的前驱节点和后继节点之间移除,重新插入到链表最右端。这时候,链表中最右端变成了最新访问到的用户2,最左端仍然是最近最少访问的用户1。
Redis缓存过期和淘汰策略_第5张图片
4、接下来,业务方请求修改用户4的信息。同样道理,我们把用户4从原来的位置移动到链表最右侧,并把用户信息的值更新。这时候,链表中最右端是最新访问到的用户4,最左端仍然是最近最少访问的用户1。
Redis缓存过期和淘汰策略_第6张图片
5、业务访问用户6,用户6在缓存里没有,需要插入到哈希链表。假设这时候缓存容量已经达到上限,必须先删除最近最少访问的数据,那么位于哈希链表最左端的用户1就会被删除掉,然后再把用户6插入到最右端。
Redis缓存过期和淘汰策略_第7张图片

Redis的LRU 数据淘汰机制:
在服务器配置中保存了 lru 计数器 server.lrulock,会定时(redis 定时程序 serverCorn())更新,server.lrulock 的值是根据 server.unixtime 计算出来的。
另外,从 struct redisObject 中可以发现,每一个 redis 对象都会设置相应的 lru。可以想象的是,每一次访问数据的时候,会更新 redisObject.lru。
LRU 数据淘汰机制是这样的:在数据集中随机挑选几个键值对,取出其中 lru 最大的键值对淘汰。不可能遍历key 用当前时间-最近访问 越大,说明访问间隔时间越长
volatile-lru
从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
allkeys-lru
从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

五、LFU

LFU (Least frequently used) 最不经常使用,如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小。

六、random

随机
volatile-random
从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-random
从数据集(server.db[i].dict)中任意选择数据淘汰

七、ttl

volatile-ttl
从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
redis 数据集数据结构中保存了键值对过期时间的表,即 redisDb.expires。
TTL 数据淘汰机制:从过期时间的表中随机挑选几个键值对,取出其中 ttl 最小的键值对淘汰。

八、noenviction

禁止驱逐数据,不删除 默认

缓存淘汰策略的选择

  • allkeys-lru : 在不确定时一般采用策略。
  • volatile-lru : 比allkeys-lru性能差 存 : 过期时间
  • allkeys-random : 希望请求符合平均分布(每个元素以相同的概率被访问)
  • 自己控制:volatile-ttl 缓存穿透

案例分享:字典库失效

key-Value 业务表存 code 显示 文字
早期将字典库,设置了maxmemory,并设置缓存淘汰策略为allkeys-lru,结果造成字典库某些字段失效,缓存击穿 , DB压力剧增,差点宕机。
分析:
maxmemory设置较小,采用allkeys-lru,会对没有经常访问的字典库随机淘汰,当再次访问时会缓存击穿,请求会打到DB上。
解决方案:
1、不设置maxmemory
2、使用noenviction策略
Redis是作为DB使用的,要保证数据的完整性,所以不能删除数据。
可以将原始数据源(XML)在系统启动时一次性加载到Redis中。

至此,结束


最后

在这个知识付费的时代,每一位热爱技术分享、奋笔直书的人,都值得我们尊敬!所以,请不要吝啬您手中的鼠标,按下左键,为小编点个赞吧。
更多内容,请关注微信公众号:架构视角

特别鸣谢

感谢老猫老师风趣幽默的讲解,让我对所学知识点记忆深刻!
感谢木槿导师的认真和负责,每一次作业点评都是我前进的动力!
感谢班主任毕老师的负责和耐心,每次不厌其烦的上课通知都是我不忘初心,保持良好学习状态的精神支柱!
感谢拉勾教育平台,给我这次花少量的钱就能报名第一期拉钩训练营,就能学习到很多深层次的技术精华的机会。而且,在学习过程中还认识了很多技术大佬,可以请教他们一些问题,比如张大佬、卢大佬、雨生大佬等等。

你可能感兴趣的:(redis,分布式,redis,分布式)