1. 缓存过期
1.1 maxmemory+
设置方式:在redis.conf中
maxmemory 1024mb
##获取配置命令
config get maxmemory
不设置的场景
- Redis的key是固定的,不会增加
- Redis作为DB使用,保证数据的完整性,不能淘汰,可以做集群,横向扩展
- 缓存淘汰侧列:禁止驱逐(默认)
设置的场景
- Redis 是作为缓存使用,不断增加key
- maxmemory:默认为0,不限制。
问题:- 达到物理内存后性能急剧下降,甚至崩溃
- 内存与硬盘交换(swap)虚拟内存,频繁IO性能急剧下降设置多少?
与业务有关。 - 1个Redis实例,保证系统运行 1 G ,剩下的就都可以设置Redis物理内存的3/4
slaver:留出一定的内存。
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. 淘汰策略
Redis的数据有定时删除、惰性删除和主动删除三种。
Redis目前采用惰性删除+主动删除的方式。
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
LRU
LRU(Least recently used)最近最少使用,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也很高”。
最常见的实现是使用一个链表保存缓存数据,详细算法如下:
- 新数据插入到链表头部;
- 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
- 当链表满的时候,将链表尾部的数据丢弃。
- 在Java中可以使用LinkHashMap(哈希链表)去实现LRU。
Redis的LRU数据淘汰机制
在服务器配置中保存了lru计数器server.lrulock,会定时(redis定时程序serverCorn())更新,server.lrulock的值是根据server.unixtime计算出来的。
从redisObject中可以发现,每一个redis对象都会设置响应的lru。可以想象的是,每一次访问数据的时候,会更新redisObject.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 缓存穿透