Redis LRU 策略

Redis LRU 策略

使用 redis 作为缓存,当添加新数据时,若有内存大小等限制,系统默认会根据一定的规则自动清理旧数据。

LRU(Least Recently Used,即最近最久未使用)实际上只是 Redis 支持的内存回收策略中的一种。其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

Redis 的 maxmemory 配置选项用来限制 Redis 的内存使用大小,当实际内存超出 maxmemory 时,Redis 提供了几种策略来让用户决定如何腾出新的空间。

LRU 策略

这六种策略分别是:

  • volatile-lru:使用 LRU 算法删除带有过期设置的 key
  • allkeys-lru:使用 LRU 算法删除任意 key
  • volatile-random:随机删除带有过期设置的 key
  • allkeys-random:随机删除任意 key
  • volatile-ttl:删除过期时间最近的 key
  • noeviction:不过期,在执行写入操作时返回一个错误

Java 基于双向链表和 Map 集合的 LRUCache 实现

package com.lvshui5u.algorithms.basis;


import java.util.HashMap;
import java.util.Map;

/**
 * @author: lvshui5u
 * @date: 2021/7/18 19:30
 * @describe:
 */
public class LRUCache<K, V> {
    /**
     * 缓存容量
     */
    private final int capacity;
    /**
     * 头、尾节点
     */
    private Node<K, V> head;
    private Node<K, V> tail;
    /**
     * LRU 字典,用来存储 Node 节点
     */
    private final Map<K, Node<K, V>> map;

    /**
     * 构造函数
     * @param capacity LRU 最大容量
     */
    public LRUCache(int capacity){
        this.capacity = capacity;
        this.map = new HashMap<>(capacity);
    }

    public void put(K key, V value){
        Node<K, V> node = map.get(key);
        if(node != null){
            // 该 node 存在在 map 和双向链表里
            // 将其移动到链表头部
            node.value = value;
            moveToHead(node);
        }else {
            // 该 node 不存在 map 和双向链表里,new 出来
            node = new Node<>();
            node.key = key;
            node.value = value;
            if(capacity == map.size()){
                // 容量已满
                // 把链表尾部元素删除
                removeTail();
            }
            // 把该元素添加到头部
            addToHead(node);
            map.put(key,node);
        }
    }

    public V get(K key){
        Node<K, V> node = map.get(key);
        if(node == null){
            return null;
        }
        // 提到队首
        moveToHead(node);
        return node.value;
    }

    private void removeTail() {
        // 1、从 map 中移除
        map.remove(tail.key);
        ///2、从链表中移除
        if(tail.pre != null){
            tail = tail.pre;
            tail.next = null;
        }else {
            head = tail = null;
        }
    }

    private void addToHead(Node<K, V> node) {
        if(head == null){
            head = tail = node;
        }else {
            node.next = head;
            head.pre = node;
            head = node;
        }
    }

    private void moveToHead(Node<K, V> node) {
        if(node == head){
            return;
        }
        // 先断
        Node<K, V> pre = node.pre;
        Node<K, V> next = node.next;
        pre.next = next;
        if(next!=null){
        	// 此时的 node 为尾,又挪到前面了,只能 pre 为尾
            next.pre = pre;
        }else{
            tail = pre;
        }

        // 再连
        node.next = head;
        head.pre = node;
        node.pre = null;
        head = node;
    }

    public void show(){
        Node<K, V> cur = head;
        while (cur != null){
            System.out.print(cur.value+"->");
            cur = cur.next;
        }
        System.out.println();
    }

    /**
     * 节点类
     * @param <V> 值
     */
    private static class Node<K, V>{
        K key;
        V value;
        Node<K, V> pre;
        Node<K, V> next;
    }

}

测试


public class Test {

    public static void main(String[] args) {

        LRUCache<Integer, Integer> lruCache = new LRUCache<>(16);
        for (int i = 0; i < 16; i++) {
            lruCache.put(i,i);
        }
        lruCache.show();
        System.out.println("get "+lruCache.get(5)+" 之后: ");
        lruCache.show();
        System.out.println("get "+lruCache.get(10)+" 之后: ");
        lruCache.show();
        System.out.println("get "+lruCache.get(1)+" 之后: ");
        lruCache.show();

        lruCache.put(20,20);
        System.out.println("put 20 之后");
        lruCache.show();
        lruCache.put(55,55);
        System.out.println("put 55 之后");
        lruCache.show();
        lruCache.put(100,100);
        System.out.println("put 100 之后");
        lruCache.show();
    }

}

结果

15->14->13->12->11->10->9->8->7->6->5->4->3->2->1->0->
get 5 之后: 
5->15->14->13->12->11->10->9->8->7->6->4->3->2->1->0->
get 10 之后: 
10->5->15->14->13->12->11->9->8->7->6->4->3->2->1->0->
get 1 之后: 
1->10->5->15->14->13->12->11->9->8->7->6->4->3->2->0->
get 0 之后: 
0->1->10->5->15->14->13->12->11->9->8->7->6->4->3->2->
put 20 之后
20->0->1->10->5->15->14->13->12->11->9->8->7->6->4->3->
put 55 之后
55->20->0->1->10->5->15->14->13->12->11->9->8->7->6->4->
put 100 之后
100->55->20->0->1->10->5->15->14->13->12->11->9->8->7->6->

Process finished with exit code 0


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值