Redis内存回收

redis内存回收

我们知道缓存使用内存来保存数据,但内存大小毕竟有限,随着要缓存的数据量越来越大,有限的缓存空间不可避免地会被写满。redis是个基于内存的缓存数据库,既然是基于内存的,那肯定就会有存满的时候。如果真的存满了,再有新的数据过来肯定就存不进去了。这时候就需要缓存的淘汰策略去删除数据。

过期数据-DB结构

redis典型的key-value内存存储数据库,因此所有的key和value都保存在dict结构中。在database结构体中,有两个dict:一个是记录key-value,一个是记录key-ttl。

typeof struct redisDb {
	dict *dict;						// 存放所有key-value的地方,也被成为keyspaces
	dict *expires;					// 存放每一个可以对应的ttl存活时间,只包含设置的ttl的key
	dict *blocking_keys;			// keys with clients waiting for data(BLPOP)
	dict *ready_keys;				// blocked keys that received a PUSH
	dict *watched_keys;				// watched keys for multi/exec cas
	int id;							// 数据库ID,默认为:0-15
	long avg_ttl;					// 记录平均ttl时长
	unsigned long expires_cursor;	// expire检查时在dict中抽样的索引位置
	list *defrag_later;				// 等待碎片整理的key的列表
} redisDb;

过期数据-DB结构图:
在这里插入图片描述

过期key过期策略

  1. 惰性删除
    在访问key的时候,检查key的存活时间,若果已经过期才执行删除。
# 检查一个key在写操作
robj *lookupKeyWriteWithFlags(redisDB *db, robj *key, int flags) {
	# 检查key是否过期
	expireIfNeeded(db, key);
	return lookup(db, key, flags);
}
# 检查一个key的读操作
robj *lookupKeyReadWithFlags(redisDB *db, robj *key, int flags) {
	robj *val;
	# 检查key是否过期
	if(expireIfNeeded(db, key) == 1) {
		// 略...
	}
	return lookup(db, key, flags);
}

# 判断key是否需要删除
int expireIfNeeded(redisDB *db, robj *key){
	// 判断是否过期,若果未过期,直接返回0
	if(!keysIfExpired(db, key)) return 0;
	// 略...
	// 删除过期key
	deleteExpiredKeyAndPropagate(db, key);
	return 1;
}
  1. 周期删除
    通过一个定时任务,定期性的抽样部分过期的key,然后执行删除

1、redis会设置一个定时任务serverCron(),按照server.hz的频率来执行过期的key清理,模式为SLOW,规则如下:

1、执行频率受server.hz的影响,默认为10,即每秒执行10次,每次执行周期100ms;
2、执行清理耗时不超过一次执行周期的25%;
3、逐个遍历DB,逐个遍历DB中的bucket,抽取20个可以是否过期;
4、如果没有达到时间上限(25ms)并且过期key比例大于10%,再进行一次抽样,否则结束

2、redis每个事件循环前会调用beforeSleep(),执行过期key清理,模式为:FAST,过期key比例小于10%不执行,规则如下:

1、执行频率受beforeSleep()调用频率影响,但两次FAST模式间隔不低于2ms;
2、执行清理耗时不超过1ms;
3、逐个遍历DB,逐个遍历DB中的bucket,抽取20个可以是否过期;
4、如果没有达到时间上限(1ms)并且过期key比例大于10%,再进行一次抽样,否则结束

内存淘汰策略

redis支持8种不通策略淘汰key

  1. noenviction:不淘汰任何key,但是内存满时不允许写入任何key,redis默认就是这种策略
  2. volatile-ttl:对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰;
  3. allkeys-random:对全体key,随机进行淘汰。也就是直接db->dict中随机挑选;
  4. volatile-random:对设置TTL的key,随机进行淘汰。也就是直接db->expires中随机挑选;
  5. allkeys-lru:对全体key,基于LRU算法进行淘汰;
  6. volatile-lru:对设置TTL的key,基于LRU算法进行淘汰;
  7. allkeys-lfu:对全体key,基于LFU算法进行淘汰;
  8. volatile-lfu:对设置TTL的key,基于LFU算法进行淘汰;

比较混淆的两种:

LRU(Least Recently Used):最少最近使用。用当前时间减去最近一次访问时间,这个值越大则淘汰的优先级越高。
LFU(Least Frequentl Used):最少频率使用。会统计每个可以的访问频率,值越小则太太的优先级越高。

Redis的数据都会封装到RedisObject结构中:

typeof struct redisObject {
	unsigned type:4;		// 对象类型
	unsigned encoding:4;	// 编码方式
	unsigned lru:LRU_BITS;	// LRU:以秒为单位记录最近一次访问时间,长度24bit
							// LFU:高16位以分钟为单位记录最近一次访问时间,低8位记录逻辑访问次数
	int rfcount;			// 引用计数,计数为0表示可以回收	
	void *ptr;				// 数据指针,指向真是数据
} robj;

LFU的访问次数之所以叫做逻辑访问次数,并不是每次的key被访问都计数,而是通过运算

  1. 生成0-1之间的随机数R(比如:0.5);
  2. 计算1/(旧次数 * lfu_log_factor + 1),记录为P,lfu_log_factor 默认为10;
  3. 如果R < P,则计数器 +1,且最大值不超过255;
  4. 访问次数会随机衰减,距离上一次访问时间每隔lfu_decay_time分钟(默认1),计数器 -1;

redis设置淘汰策略方式

  1. 获取当前内存淘汰策略和能使用内存大小:
# 获取内存淘汰策略命令
config get maxmemory-policy

#能使用的最大内存大小
config get maxmemory

redis命令查询

可以看到当前使用的默认的noeviction策略
如果不设置最大内存大小或者设置最大内存大小为0,在64位操作系统下不限制内存大小,在32位操作系统下最多使用3GB内存。32位的机器最大只支持 4GB 的内存,而系统本身就需要一定的内存资源来支持运行,所以 32 位机器限制最大 3 GB 的可用内存

  1. 在 redis.conf文件里,如下图
# 设置最大内存上线
# maxmemory <bytes>
maxmemory 1g

# 设置内存淘汰策略
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
# 
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key according to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don't expire at all, just return an error on write operations
# The default is:
maxmemory-policy noeviction 

redis惰性内存淘汰策略流程图

redis惰性内存淘汰策略流程图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值