谈谈 Redis 的过期策略( 二 )

  • allkeys-lru:当内存超出 maxmemory,在所有的 key 中,移除最少使用的key 。只把 Redis 既当缓存是使用这种策略 。(推荐) 。
  • allkeys-random:当内存超出 maxmemory,在所有的 key 中,随机移除某个 key 。(应该没人用吧)
  • volatile-lru:当内存超出 maxmemory,在设置了过期时间 key 的字典中,移除最少使用的 key 。把 Redis 既当缓存,又做持久化的时候使用这种策略 。
  • volatile-random:当内存超出 maxmemory,在设置了过期时间 key 的字典中,随机移除某个key 。
  • volatile-ttl:当内存超出 maxmemory,在设置了过期时间 key 的字典中,优先移除 ttl 小的 。
  • LRU 算法实现 LRU 算法除了需要 key/value 字典外,还需要附加一个链表,链表中的元素按照一定的顺序进行排列 。当空间满的时候,会踢掉链表尾部的元素 。当字典的某个元素被访问时,它在链表中的位置会被移动到表头 。所以链表的元素排列顺序就是元素最近被访问的时间顺序 。
    使用 Python 的 OrderedDict(双向链表 + 字典) 来实现一个简单的 LRU 算法:
    from collections import OrderedDictclass LRUDict(OrderedDict):    def __init__(self, capacity):        self.capacity = capacity        self.items = OrderedDict()    def __setitem__(self, key, value):        old_value = self.items.get(key)        if old_value is not None:            self.items.pop(key)            self.items[key] = value        elif len(self.items) < self.capacity:            self.items[key] = value        else:            self.items.popitem(last=True)            self.items[key] = value    def __getitem__(self, key):        value = self.items.get(key)        if value is not None:            self.items.pop(key)            self.items[key] = value        return value    def __repr__(self):        return repr(self.items)d = LRUDict(10)for i in range(15):    d[i] = iprint d近似 LRU 算法Redis 使用的并不是完全 LRU 算法 。不使用 LRU 算法,是为了节省内存,Redis 采用的是随机LRU算法,Redis 为每一个 key 增加了一个24 bit的字段,用来记录这个 key 最后一次被访问的时间戳 。
    注意 Redis 的 LRU 淘汰策略是懒惰处理,也就是不会主动执行淘汰策略,当 Redis 执行写操作时,发现内存超出 maxmemory,就会执行 LRU 淘汰算法 。这个算法就是随机采样出5(默认值)个 key,然后移除最旧的 key,如果移除后内存还是超出 maxmemory,那就继续随机采样淘汰,直到内存低于 maxmemory 为止 。
    如何采样就是看 maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机 。每次采样多少个 key 看的是 maxmemory_samples 的配置,默认为 5 。
    LFURedis 4.0 里引入了一个新的淘汰策略 —— LFU(Least Frequently Used) 模式,作者认为它比 LRU 更加优秀 。
    LFU 表示按最近的访问频率进行淘汰,它比 LRU 更加精准地表示了一个 key 被访问的热度 。
    如果一个 key 长时间不被访问,只是刚刚偶然被用户访问了一下,那么在使用 LRU 算法下它是不容易被淘汰的,因为 LRU 算法认为当前这个 key 是很热的 。而 LFU 是需要追踪最近一段时间的访问频率,如果某个 key 只是偶然被访问一次是不足以变得很热的,它需要在近期一段时间内被访问很多次才有机会被认为很热 。
    【谈谈 Redis 的过期策略】Redis 对象的热度
    Redis 的所有对象结构头中都有一个 24bit 的字段,这个字段用来记录对象的热度 。
    // redis 的对象头typedef struct redisObject {    unsigned type:4; // 对象类型如 zset/set/hash 等等    unsigned encoding:4; // 对象编码如 ziplist/intset/skiplist 等等    unsigned lru:24; // 对象的「热度」    int refcount; // 引用计数    void *ptr; // 对象的 body} robj;


    推荐阅读