Redis-2 缓存过期与淘汰策略

366 阅读4分钟

1 缓存过期

1.1 maxmemory

maxmemory : 默认为0 不限制。问题:超过物理内存后性能急剧下架,甚至崩溃,内存与硬盘交换(swap) 虚拟内存 ,频繁IO 性能急剧下降。当趋近maxmemory时,通过缓存淘汰策略,从内存中删除对象。

设置方式:在redis.conf中

maxmemory 1024mb

##获取配置命令
config get maxmemory

1.2 expire

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

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

原理:

typedef struct redisDb { 
    dict *dict; 
    dict *expires; 
    dict *blocking_keys; 
    dict *ready_keys; 
    dict *watched_keys; 
    int id; 
} redisDb;

//dict 用来维护一个 Redis 数据库中包含的所有 Key-Value 键值对,
//expires则用于维护一个 Redis 数据库中设置了失效时间的键(即key与失效时间的映射)。
//当我们使用 expire命令设置一个key的失效时间时,Redis 首先到 dict 这个字典表中查找要设置的key是否存在,如果存在就将这个key和失效时间添加到 expires 这个字典表。
//当我们使用 setex命令向系统插入数据时,Redis 首先将 Key 和 Value 添加到 dict 这个字典表中,然后将 Key 和失效时间添加到 expires 这个字典表中。简单地总结来说就是,设置了失效时间的key和具体的失效时间全部都维护在 expires 这个字典表中。

2 淘汰策略

2.1 定时删除

创建定时器,设置键过期时间同时,立即对键执行删除操作,消耗cpu,不推荐

2.2 惰性删除

读取的时候,检查是否失效,失效就删除。调用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); 
}

2.3 主动删除

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

maxmemory-policy allkeys-lru

2.3.1 LRU

LRU (Least recently used) 最近最少使用,算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

#### 实现策略
1. 新数据插入到链表头部;
2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
3. 当链表满的时候,将链表尾部的数据丢弃。
4. 在Java中可以使用LinkHashMap(哈希链表)去实现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)中挑选最近最少使用的数据淘汰

2.3.2 RANDOM

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

allkeys-random从数据集(server.db[i].dict)中任意选择数据淘汰

2.3.3 TTL

volatile-ttl从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰redis 数据集数据结构中保存了键值对过期时间的表,即 redisDb.expires。

TTL 数据淘汰机制:从过期时间的表中随机挑选几个键值对,取出其中 ttl 最小的键值对淘汰。

2.3.4 noenviction

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