优胜劣汰——LRU
当Redis内存超出物理内存限制时,内存数据会开始和磁盘产生频繁的交换。交换让Redis性能急剧下降,是不允许的。
在生产环境中,为了限制最大使用内存,Redis提供了配置参数maxmemory来限制内存超出期望大小。
当实际内存超出maxmemory时,Redis提供了几种可选策略。
- noeviction:不会继续服务写请求(del请求可继续服务),读请求可以继续进行。这样保证不会丢失数据,但是让线上的业务不能持续进行。这是默认的淘汰策略
- volatile-lru:尝试淘汰设置了过期时间的key,最少使用的key优先被淘汰。没有设置过期时间的key不会被淘汰,这样可以保证需要持久化的数据不会突然丢失
- volatile-ttl:淘汰策略是比较key的剩余寿命ttl的值,ttl越小越优先被淘汰
- volatile-random:淘汰的key是过期key集合中随机的key
- allkeys-lru:这个策略淘汰的key对象是全体的key集合,而不是过期的key集合。
- allkeys-random:淘汰的key是随机的key
volatile-xxx策略只会针对带过期时间的key进行淘汰,allkeys-xxx策略会对所有的key进行淘汰。如果只是拿redis做缓存,那么应该使用allkeys-xxx策略,客户端缓存时不必携带过期时间。如果还想使用Redis的持久化功能,那就使用volatile-xxx策略,这样可以保留没有设置过期时间的key,它们是永久的key,不会被LRU算法淘汰。
LRU算法
实现LRU算法除了需要key/value字典外,还需要附加一个链表,链表中的元素按照一定的顺序进行排列。当空间满的时候,会踢掉链表尾部的元素。当字典的某个元素被访问时,它在链表的位置会被移动到表头,所以链表的元素排列顺序就是元素最近被访问的时间顺序。
近似LRU算法
Redis使用的是一种近似LRU算法,跟LRU算法还不太一样。之所以不适用LRU算法,是因为其需要消耗大量的额外内存,需要对现有的数据结构进行较大的改造。近似LRU算法很简单,在现有数据结构的基础上使用随机采用法来淘汰元素,能达到和LRU算法非常近似的效果。Redis为实现近似LRU算法,给每个key增加了一个额外的小字段,这个字段的长度是24bit,也就是最后一次被访问的时间戳。
Redis 3.0算法中增加了淘汰池,淘汰池是一个数组,它的大小是maxmemory_samples,在每一次淘汰循环中,新的随机得出的key列表会和淘汰池中的key列表进行融合,淘汰池最旧的一个key之后,保留剩余较旧的key列表放入淘汰池中留待下一个循环。