Redis内存淘汰策略

一:Redis最大占用内存

打开redis的redis.conf配置文件,设置maxmemory参数,注意maxmemory是byte字节类型。

如果不设置最大内存大小或者设置最大内存大小为0,在64位操作系统下不限制内存大小,在32位

操作系统下最多使用3GB内存。一般推荐Redis设置内存为最大物理内存的的四分之三。

 如何修改reids内存设置?

1.手动进入redis.conf配置文件修改

2.通过命令修改

 查看redis内存使用情况命令:info memory

二:Redis内存使用超出了设置的最大值会怎样?

我们可以故意将redis内存设置一个字节模拟一下

 然后进入redis随便设置一个值就会报OOM

 所以为了不报OOM,才会有内存淘汰策略去杜绝内存打满。

三:内存淘汰策略

首先,我们有必要了解一下redis的过期策略。

在Redis中过期的key不会立刻从内存中删除,而是会同时以下面两种策略进行删除:

惰性删除:当key被访问时检查该key的过期时间,若已过期则删除;已过期未被访问的数据仍保持在内存中,消耗内存资源;
定期删除:每隔一段时间,随机检查设置了过期的key并删除已过期的key;维护定时器消耗CPU资源;

以上两种的过期策略都有各自的缺陷,所以redis提供了内存淘汰策略来进行补充。
8中策略:

1. noeviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键

2. allkeys-lru:加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键

3. volatile-lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键

4. allkeys-random:加入键的时候如果过限,从所有key随机删除

5. volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐

6. volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键

7. volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键

8. allkeys-lfu:从所有键中驱逐使用频率最少的键

redis默认使用的noeviction,推荐使用allkeys-lru

四:LRU

LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法。

LRU的算法核心是哈希链表(hashmap + DoubleLinkedList),查找用哈希,增删用链表,能很好的满足查找增删都是o(1)。

利用LinkedHashMap手写LRU算法:

public class LRUDemo extends LinkedHashMap {
  private int capacity;
  public LRUDemo(int capacity) {
    //accessOrder参数要设置为true,不然不会将最近使用顶到后面去
    super(capacity,0.75F,true);
    this.capacity = capacity;
  }

  @Override
  protected boolean removeEldestEntry(Entry eldest) {
    return super.size() > capacity;
  }

  public static void main(String[] args) {
    LRUDemo lruDemo = new LRUDemo(3);
    lruDemo.put(1,"a");
    lruDemo.put(2,"b");
    lruDemo.put(3,"c");
    System.out.println(lruDemo.keySet());
    lruDemo.put(4,"d");
    System.out.println(lruDemo.keySet());
    lruDemo.get(3);
    System.out.println(lruDemo.keySet());
    lruDemo.get(3);
    System.out.println(lruDemo.keySet());
    lruDemo.get(2);
    System.out.println(lruDemo.keySet());
  }
}

打印结果:

从结果可以看出,当容器已满的时候再向容器新增数据时,会删除掉排在第一个位置的数据,且当一个数据被最近使用时,会将这个数据排到末尾(这与accessOrder参数设置为true有关)

 完全手写LRU算法:

// map 负责查找,构建一个虚拟的双向链表,它里面安装的是一个个的node节点,作为数据载体
public class LRUCacheDemo {

  //1.构建一个Node节点,作为数据载体
  class Node<K,V> {
    K key;
    V value;
    Node<K,V> prev;
    Node<K,V> next;

    public Node() {
      this.prev = this.next = null;
    }

    public Node(K key,V value) {
      this.key = key;
      this.value = value;
      this.prev = this.next = null;
    }
  }

  //2.构建一个虚拟的双向列表,里面放的就是我们的Node
  class DoubleLinkedList<K,V> {
    Node<K,V> head;
    Node<K,V> tail;

    public DoubleLinkedList() {
      head = new Node<>();
      tail = new Node<>();
      head.next = tail;
      tail.prev = head;
    }

    //添加到头
    public void addHead(Node<K,V> node) {
      node.next = head.next;
      node.prev = head;
      head.next.prev = node;
      head.next = node;
    }

    //删除节点
    public void removeNode(Node<K,V> node) {
      node.next.prev = node.prev;
      node.prev.next = node.next;
      node.prev = null;
      node.next = null;
    }

    //获得最后一个节点
    public Node getLastNode() {
      return tail.prev;
    }
  }

  private int cacheSize;
  Map<Object,Node<Object,Object>> map;
  DoubleLinkedList<Object,Object> doubleLinkedList;

  public LRUCacheDemo(int cacheSize) {
    this.cacheSize = cacheSize;
    map = new HashMap<>();
    doubleLinkedList = new DoubleLinkedList<>();
  }

  public Object get(Object key) {
    if (!map.containsKey(key)) {
      return -1;
    }
    Node<Object,Object> node = map.get(key);
    doubleLinkedList.removeNode(node);
    doubleLinkedList.addHead(node);
    return node.value;
  }

  public void put(Object key, Object value) {
    // 已经存在这个key,执行更新操作
    if (map.containsKey(key)) {
      Node<Object, Object> node = map.get(key);
      node.value = value;
      map.put(key,node);
      doubleLinkedList.removeNode(node);
      doubleLinkedList.addHead(node);
    } else {
      // 容器满了
      if (map.size() == cacheSize) {
        Node lastNode = doubleLinkedList.getLastNode();
        map.remove(lastNode.key);
        doubleLinkedList.removeNode(lastNode);
      }
      // 新增
      Node<Object, Object> newNode = new Node<>(key, value);
      map.put(key,newNode);
      doubleLinkedList.addHead(newNode);
    }
  }

  public static void main(String[] args) {
    LRUCacheDemo lruCacheDemo = new LRUCacheDemo(3);
    lruCacheDemo.put(8,1);
    lruCacheDemo.put(6,2);
    lruCacheDemo.put(7,3);
    System.out.println(lruCacheDemo.map.keySet());
    lruCacheDemo.put(4,4);
    System.out.println(lruCacheDemo.map.keySet());
    lruCacheDemo.put(6,5);
    System.out.println(lruCacheDemo.map.keySet());
    lruCacheDemo.put(5,5);
    System.out.println(lruCacheDemo.map.keySet());
    lruCacheDemo.put("a",5);
    System.out.println(lruCacheDemo.map.keySet());
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值