• 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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    过期数据-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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    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):最少最近使用。用当前时间减去最近一次访问时间,这个值越大则淘汰的优先级越高。
    LFULeast 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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

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

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

  • 相关阅读:
    python实现读取,修改excel数据
    Heterogeneous Parallel Programming 异构并行编程 - UIUC伊利诺伊大学(持续更新)
    异地寄件教程分析
    星卫士&陈卫俊表示要用心打造做真正好用、良心的健康手表
    Java《ArrayLIst》
    Java随笔-泛型
    提升个人认知的六个学习方法
    【Apache Spark 】第 11 章使用 Apache Spark 管理、部署和扩展机器学习管道
    转债打新监听
    Stack Overflow 2022 开发者调查:行业走向何方?
  • 原文地址:https://blog.csdn.net/u013415591/article/details/127782533