一、过期策略
懒惰处理
Redis在get操作时遇到过期的key会进行删除操作。
集中处理
Redis会将设置了过期时间的key放到一个独立的字典里,默认每秒10次过期扫描。扫描方式:
- 从过期字典里随机选20个key;
- 删除这20个key里已经过期的;
- 如果过期的key比率超过1/4,那么重复步骤1;
为防止扫描时间过长,扫描时间限制为25ms,开发时应尽量避免大量key同时过期。
从库不会进行过期扫描,主库删除时,会在AOF文件里增加一条del指令,同步到所有从库,从库通过此指令来删除。由于指令的同步存在异步,所以会出现主从数据不一致的情况。
二、内存溢出淘汰
当Redis内存超出物理内存限制时,内存数据会开始和磁盘产生频繁的交换,使得性能急剧下降。为了限制内存的使用,Redis提供参数maxmemory
来限制最大内存,当内存超出后,会有以下策略(maxmemory-policy
)来淘汰key以腾出空间:
- noeviction(默认) 不让写,只让读。
- volatile-lru LRU的方式淘汰设置了过期时间的key。
- volatile-ttl 淘汰ttl最小的设置了过期的key
- volatile-random 随机淘汰设置了过期时间的key
- allkeys-lru LRU的方式淘汰所有key中的一个
- allkeys-random 随机淘汰所有key中的一个
由于LRU算法需要消耗大量的额外内存,redis采用一种近似的LRU算法。它给每个 key 增加了一个额外的小字段(24bit),也就是最后一次被访问的时间戳。每次执行写操作时,如果发现内存超出maxmemory
,就随机采样5个(参数maxmemory_samples
配置)key,然后淘汰最旧的。如果淘汰之后还是超出,那就继续随机淘汰,直到不超出为止。如果maxmemory-policy
是volatile-xxx,就从设置过期时间的key里采样,否则就从所有key里采样。
Redis3.0里增加了一个淘汰池,就是一个大小为maxmemory_samples
的数组。每次淘汰时会将随机出来的key和数组里的key融合,淘汰掉最旧的一个,然后将剩下的较旧的key放到淘汰池里给下个循环用。
三、惰性删除
redis的删除del在删除一个大对象的时候有可能造成卡顿。为了解决这个问题Redis4.0引入了unlink指令,将这个key的对象引用从Redis内存数据里删除,将删除操作封装成一个任务丢到一个异步队列里。然后有个异步线程会从这个队列里取出任务并执行。
清空操作flushdb
和flushall
,在Redis4.0后,在指令后面增加async
,就也可以像上面一样异步执行。
参考资料
《Redis深度历险:核心原理和应用实践》