Redis是基于内存存储数据,当内存中存储的数据较多时,为了避免内存被耗尽,Redis就需要有一套机制来保证能够自动清除那些被规定为可以被清除的数据,这种规定就可以定义为内存淘汰策略。
Redis中的8种内存淘汰策略
- 不淘汰,满了直接抛出异常(MAXMEMORY_NO_EVICTION)
- 从所有KEY中,随机删除(MAXMEMORY_ALLKEYS_RANDOM)
- 从所有KEY中,采用LRU方式删除(MAXMEMORY_ALLKEYS_LRU)
- 从设置了过期值的KEY中,随机删除(MAXMEMORY_VOLATILE_RANDOM)
- 从设置了过期值的KEY中,采用LRU方式删除(MAXMEMORY_VOLATILE_LRU)
- 从所有KEY中,采用TTL方式删除(优先淘汰快过期的)(MAXMEMORY_VOLATILE_TTL)
- 从设置了过期值的KEY中,采用LFU方式删除(4.0版本新增)(MAXMEMORY_VOLATILE_LFU)
- 从所有KEY中,采用LFU方式删除(4.0版本新增)(MAXMEMORY_ALLKEYS_LFU)
Redis中的LRU算法
Redis中采用的是一种近似LRU算法,目的是为了避免额外的内存消耗,如果严格按照LRU算法,就需要维护一个双向链表,而Redis中并没有这样做,Redis中有一个默认大小为16的池子(EVPOOL_SIZE),每次随机抽取5个key(maxmemory_samples抽取数量可配置),让抽取的key与池子中原有的key进行比较,只有时间小于池中的最小时间的key才会被放入池中,如果池子被放满了,则将池中最大时间的key移除,当需要淘汰的时候,则将池中时间最小的key淘汰。
假设池子大小为6,每次随机抽取1个key。
redis给每一个key都预留了24bit的空间,用于记录key的最后访问时间
LFU算法
LRU算法只是预测最近被访问的数据将来最有可能被访问到,与访问的频率无关,而Redis淘汰的主要目的是淘汰那些将来最不可能再被访问的数据,那么如果出现如下场景:
A—A---A—A---A—A---A
B------------------------------B
如果是根据LRU算法,被淘汰的将是A,因为在判定的那一时刻B是最近被访问到的,而实际上B应该是符合将来最不可能再被访问的数据。
这时候就要用LFU算法了,LFU指的是最近最少使用,记录的是一定时间内数据访问的频次,假设数据如果在一段时间内很少被访问到,那么将来也很少被访问到。
LFU算法也用到24bit的空间,但是24bit被拆分为两部分,16bit用来记录最后的递减时间,8bit用来记录访问频次
拆分为两段的目的是为了能够更加精确的进行过滤,假设一个数据一段时间访问的频率非常的频繁,但是之后的很长时间没有再被访问,如果只记录访问频率,就不能满足这样的场景,所以还需要记录上一次访问的时间,这样就可以通过访问的时间来主动的递减访问频率,以此适应短时间内访问频率很高,之后几乎不被访问的场景。