当前位置:网站首页>Redis Cache Expiration and Retirement Policy
Redis Cache Expiration and Retirement Policy
2022-08-09 18:05:00 【Jeremy__】
缓存过期和淘汰策略
Redis性能高: 官方数据 读:110000次/s 写:81000次/s 长期使用,key会不断增加,Redis作为缓存使用,物理内存也会满 内存与硬盘交换(swap) 虚拟内存 ,频繁IO 性能急剧下降
maxmemory
不设置的场景 Redis的key是固定的,不会增加 Redis作为DB使用,保证数据的完整性,不能淘汰 , 可以做集群,横向扩展 缓存淘汰策略:禁止驱逐 (默认)
设置的场景 Redis是作为缓存使用,不断增加Key maxmemory : 默认为0 不限制 问题:达到物理内存后性能急剧下架,甚至崩溃 内存与硬盘交换(swap) 虚拟内存 ,频繁IO 性能急剧下降 设置多少? 与业务有关 1个Redis实例,保证系统运行 1 G ,剩下的就都可以设置Redis 物理内存的3/4
slaver : 留出一定的内存
在redis.conf中
maxmemory 1024mb
复制代码
命令: 获得maxmemory数
CONFIG GET maxmemory
复制代码
设置maxmemory后,当趋近maxmemory时,通过缓存淘汰策略,从内存中删除对象 不设置maxmemory 无最大内存限制 maxmemory-policy noeviction (禁止驱逐) 不淘汰 设置maxmemory maxmemory-policy 要配置
expire数据结构
在Redis中可以使用expire命令设置一个键的存活时间(ttl: time to live),过了这段时间,该键就会自动 被删除. expire的使用 expire命令的使用方法如下: expire key ttl(单位秒)
127.0.0.1:6379> expire name 2 #2秒失效
(integer) 1
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> set name zhangfei
OK
127.0.0.1:6379> ttl name #永久有效
(integer) -1
127.0.0.1:6379> expire name 30 #30秒失效
(integer) 1
127.0.0.1:6379> ttl name #还有24秒失效
(integer) 24
127.0.0.1:6379> ttl name #失效
(integer) -2
复制代码
expire原理
typedef struct redisDb {
dict *dict; -- key Value
dict *expires; -- key ttl
dict *blocking_keys;
dict *ready_keys;
dict *watched_keys;
int id;
} redisDb;
复制代码
上面的代码是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目前采用惰性删除+主动删除的方式.
定时删除 在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作. 需要创建定时器,而且消耗CPU,一般不推荐使用.
惰性删除 在key被访问时如果发现它已经失效,那么就删除它. 调用expireIfNeeded函数,该函数的意义是:读取数据之前先检查一下它有没有失效,如果失效了就删除它.
int expireIfNeeded(redisDb *db, robj *key) {
//获取主键的失效时间 get当前时间-创建时间>ttl
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);
}
复制代码
主动删除
在redis.conf文件中可以配置主动删除策略,默认是no-enviction(不删除)
maxmemory-policy allkeys-lru
复制代码
LRU LRU (Least recently used) 最近最少使用,算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”.
最常见的实现是使用一个链表保存缓存数据,详细算法实现如下:
- 新数据插入到链表头部;
- 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
- 当链表满的时候,将链表尾部的数据丢弃.
- 在Java中可以使用LinkHashMap(哈希链表)去实现LRU
让我们以用户信息的需求为例,来演示一下LRU算法的基本思路:
1.假设我们使用哈希链表来缓存用户信息,目前缓存了4个用户,这4个用户是按照时间顺序依次从链表右端插入的.
2.此时,业务方访问用户5,由于哈希链表中没有用户5的数据,我们从数据库中读取出来,插入到缓存当中.这时候,链表中最右端是最新访问到的用户5,最左端是最近最少访问的用户1.
3.接下来,业务方访问用户2,哈希链表中存在用户2的数据,我们怎么做呢?我们把用户2从它的前驱 节点和后继节点之间移除,重新插入到链表最右端.这时候,链表中最右端变成了最新访问到的用户2,最左端仍然是最近最少访问的用户1.
4.接下来,业务方请求修改用户4的信息.同样道理,我们把用户4从原来的位置移动到链表最右侧,并把用户信息的值更新.这时候,链表中最右端是最新访问到的用户4,最左端仍然是最近最少访问的用户1.
5.业务访问用户6,用户6在缓存里没有,需要插入到哈希链表.假设这时候缓存容量已经达到上限,必须先删除最近最少访问的数据,那么位于哈希链表最左端的用户1就会被删除掉,然后再把用户6插入到最右端.
Redis的LRU 数据淘汰机制 在服务器配置中保存了 lru 计数器 server.lrulock,会定时(redis 定时程序 serverCorn())更新,server.lrulock 的值是根据 server.unixtime 计算出来的.
这里首先介绍一下RedisObject的结构 RedisObject结构: Value是一个对象 包含字符串对象,列表对象,哈希对象,集合对象和有序集合对象
typedef struct redisObject {
unsigned type:4;//类型 五种对象类型
unsigned encoding:4;//编码
void *ptr;//指向底层实现数据结构的指针
//...
int refcount;//引用计数
//...
unsigned lru:LRU_BITS; //LRU_BITS为24bit 记录最后一次被命令程序访问的时间
//...
}robj;
复制代码
24位LRU lru 记录的是对象最后一次被命令程序访问的时间,( 4.0 版本占 24 位,2.6 版本占 22 位). 高16位存储一个分钟数级别的时间戳,低8位存储访问计数(lfu : 最近访问次数) lru----> 高16位: 最后被访问的时间lfu----->低8位:最近访问次数
因此,从 以上struct redisObject 中可以发现,每一个 redis 对象都会设置相应的 lru.可以想象的是,每一次访问数据的时候,会更新redisObject.lru.
LRU 数据淘汰机制是这样的:在数据集中随机挑选几个键值对,取出其中 lru 最大的键值对淘汰. 不可能遍历key,只需要用当前时间-最近访问 得到的值越大 说明该Key的访问间隔时间越长
volatile-lru 从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
allkeys-lru 从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
LFU LFU (Least frequently used) 最不经常使用,如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小.
volatile-lfu:与LRU概念类似 allkeys-lfu:与LRU概念类似
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 缓存穿透
边栏推荐
猜你喜欢
随机推荐
【完美解决v-if导致echart不显示问题】
Codeforces Round #808 (Div. 2)||沉淀
我的第一篇博客
C语言小游戏—扫雷
选择器的使用
网络——介质访问控制
为什么四个字节的float表示的范围比八个字节的long要广
The second chapter: create an interactive map (2.1 2.3)
初识C语言(1)
【科普】关于平板电脑的那些事
Nacos注册中心 Feign远程调用 Gateway服务网关
The web project accesses static resources inside the jar
3. Using Earth Engine Data
Super hot summer air conditioner
The first day of the real in CSDN
STM32课设-智能物联网家居系统(UCOSIII+STEMWIN)
Nacos Jaspyt配置加密设置
CompletableFuture异步线程优化代码
网络——IPv6 vs IPv4
一个程序员的水平能差到什么程度?