Redis LRU

Redis LRU

应用背景

​ 当Redis内存超出物理内存限制时, 内存的数据会开始和磁盘产生频繁的交换(swap)。交换会让Redis的性能急剧下降, 对于访问量比较频繁的Redis来说, 这样龟速的存取效率基本上等于不可用。

​ 在生成环境中我们是不允许Redis出现交换行为的, 为了限制最大使用内存,Redis提供了配置参数maxmemory来限制内存超出期望大小。

​ 当实际内存超出maxmemory时, Redis提供了几种可选策略(maxmemory-policy)来让用户自己决定该如何腾出新的空间以继续提供读写服务。

  • noeviction 不会继续服务写请求(DEL请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。
  • volatile-lru 尝试淘汰设置了过期时间的key,最少使用的key优先被淘汰, 没有设置过期时间的key不会被淘汰, 这样可以保证需要持久化的数据不会突然丢失。
  • volatile-ttl 和上面一样, 除了淘汰的策略不是LRU, 而是key的剩余ttl的值, ttl越小越优先被淘汰
  • volatile-random 和上面一样,从以设置过期时间的数据中任意选择数据淘汰。
  • allkeys-lru 区别于volatile-lru,这个策略要淘汰的key对象是全体的key集合, 而不是过期的key集合。这意味着没有设置过期时间的key也会被淘汰。
  • allkeys-random 和上面一样, 不过淘汰的策略是随机的key

LRU算法

​ 是心啊LRU算法除了需要key/value字典外, 还需要附加一个链表, 链表中的元素按照一定的顺序进行排列。 当空间满的时候, 会踢掉链表尾部的元素。当字典的某个元素被访问时,它再链表中的位置会被移动到表头。所以链表的元素就是元素最近被访问的时间顺序了。

​ 位于链表尾部的元素就是不被重用的元素,所以会被踢掉。位于表头的元素就是刚刚被人用过的元素,所以暂时不会被踢掉。

​ 下面使用Python的OrderedDict(双向链表+字典)来实现一个简单的LRU算法。

from collections import OrderedDict
class 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] = i
print(d)

近似LRU算法

​ Redis使用的是一种近似LRU算法, 它跟LRU算法不太一样, 之所以不使用LRU算法, 是因为需要消耗大量的额外内存,需要对现有的数据结果进行较大的改造。近似LRU算法则很简单, 在现有数据结构的基础上使用随机采样法来淘汰元素, 能达到和LRU算法非常近似的效果。 Redis为实现近似LRU算法, 它给每个key增加了一个额外的小字段, 这个字段的长度是24bit, 也就是最后一次被访问的时间戳。

​ 处理key过期的方式分为集中式处理和懒惰处理,LRU淘汰不一样, 它的处理方式只有懒惰处理。当Redis执行写操作时, 发现内存超出maxmemory,就会执行一次LRU淘汰算法。这个算法也很简单, 就是随机采样出5(可以配置)个key, 然后淘汰掉最旧的key, 如果淘汰后内存还是存在超出maxmemory, 那就继续随机采样淘汰, 直到内存低于maxmemory为止。

​ 如何采用就看maxmemory-policy的配置, 如果是allkeys就是是从所有的key字典中随机, 如果是volatile就是从过期的key字典中随机。每次采用多少key看的是maxmemory——samples的配置, 默认是5。

posted @ 2020-09-27 15:51  phper-liunian  阅读(156)  评论(0编辑  收藏  举报