当前位置: 首页 > news >正文

【Redis】数据的淘汰策略

    

目录

淘汰策略方案(8种)

LRU和LFU策略的区别

使用建议

手搓LRU算法

方式一

方式二


        大家好,我是jstart千语。今天和大家回来聊一下redis,这次要讲的是它的淘汰策略。为什么需要淘汰策略呢,就是当redis里面的内存占满后,存不下数据了,那么新加入的数据该如何处理呢?这种处理的方式不同,就称为不同的数据淘汰策略。redis支持8种不同的淘汰策略。

淘汰策略方案(8种)

noeviction默认策略,不淘汰,当内存满时写入会报错(OOM)。适合读多写少的缓存场景
volatile-ttl从设置了过期时间的 key 中,优先淘汰即将过期的。
allkey-random随机淘汰一个 key。适用于不关心数据使用频率的场景。
volatile-random从设置了过期时间的 key 中随机淘汰一个。
allkeys-lru(最近最少使用)从所有 key 中淘汰最少使用的(LRU:Least Recently Used)。
volatile-lru(最近最少使用)从设置了过期时间的 key 中,淘汰最少使用的。
allkeys-lfu(频率)从所有 key 中淘汰最不常用的(LFU:Least Frequently Used)。
volatile-lfu(频率)从设置了过期时间的 key 中,淘汰最不常用的。


LRU和LFU策略的区别

LRU:最近最少使用,用当前时间减去最后一次访问的时间,这个值越大越先被淘汰。

例如:key1是5秒前被访问,key2是10秒前被访问,那么就是key2优先被淘汰。


LFU:最少频率使用,统计每个key的访问频率,值越小越先被淘汰。

例如:key1在5秒内被访问了3次,key2在10秒内被访问了5次。那么key2优先被淘汰。



使用建议

        1、优先使用allkeys-lru策略。充分利用LRU算法的优势,把最近最常访问的数据留在缓存中。如果业务中有明显的冷热数据区分,建议使用该策略。


为什么这里不使用LFU呢?:

因为数据使用频率可能是不固定的,在某一段时间内访问频率高,另外一段时间访问频率低,可能导致误删。具体还要看业务需求
 

        2、如果业务中数据访问频率区别不大,没有明显的冷热数据区分,建议使用allkeys-random,随机选择淘汰。

        3、如果业务中有置顶需求,可以使用volatile-lru策略,同时置顶数据不设置过期时间,这些置顶的数据就一直不被删除,会淘汰其他设置过期时间的数据。

        4、如果业务中有短时高频访问的数据,可以使用allkeys-lfu或者volatile-lfu策略。



手搓LRU算法

方式一

利用Java的LinkedHashMap天然支持按访问顺序排序,通过重写removeEldestEntry方法可实现LRU逻辑。

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int capacity;

    public LRUCache(int capacity) {
        // 初始容量,负载因子,accessOrder=true表示按访问顺序排序
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        // 当大小超过容量时移除最旧元素
        return size() > capacity;
    }
}

使用示例:

LRUCache<String, String> cache = new LRUCache<>(3);
cache.put("a", "1");
cache.put("b", "2");
cache.get("a"); // 访问"a"使其变为最近使用
cache.put("c", "3");
cache.put("d", "4"); // 插入"d"时,容量超限,淘汰最久未使用的"b"

 注意:非线程安全,需在单线程环境使用或通过Collections.synchronizedMap包装。


方式二

手动实现(哈希表 + 双向链表)。实现O(1)时间复杂度的get和put操作,需结合哈希表与双向链表。

步骤:

  1. 定义节点结构:包含前后指针及键值。
  2. 维护双向链表:头部放最新访问节点,尾部为待淘汰节点。
  3. 哈希表映射:快速定位节点位置。
import java.util.HashMap;
import java.util.Map;

public class ManualLRUCache<K, V> {
    static class Node<K, V> {
        K key;
        V value;
        Node<K, V> prev;
        Node<K, V> next;
        Node(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }

    private final int capacity;
    private final Map<K, Node<K, V>> cache = new HashMap<>();
    private Node<K, V> head; // 虚拟头节点
    private Node<K, V> tail; // 虚拟尾节点

    public ManualLRUCache(int capacity) {
        this.capacity = capacity;
        head = new Node<>(null, null);
        tail = new Node<>(null, null);
        head.next = tail;
        tail.prev = head;
    }

    public V get(K key) {
        Node<K, V> node = cache.get(key);
        if (node == null) return null;
        moveToHead(node); // 访问后移至头部
        return node.value;
    }

    public void put(K key, V value) {
        Node<K, V> node = cache.get(key);
        if (node != null) {
            node.value = value;
            moveToHead(node);
        } else {
            node = new Node<>(key, value);
            cache.put(key, node);
            addToHead(node);
            if (cache.size() > capacity) {
                Node<K, V> removed = removeTail();
                cache.remove(removed.key);
            }
        }
    }

    private void moveToHead(Node<K, V> node) {
        removeNode(node);
        addToHead(node);
    }

    private void addToHead(Node<K, V> node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    private void removeNode(Node<K, V> node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    private Node<K, V> removeTail() {
        Node<K, V> res = tail.prev;
        removeNode(res);
        return res;
    }
}

线程安全优化:

  • 使用ConcurrentHashMap替代HashMap。
  • 对链表操作加锁(如ReentrantLock或synchronized),但可能影响性能。
  • 考虑读写锁(ReadWriteLock),允许并发读,写时独占。

相关文章:

  • linux磁盘创建分区
  • 开源协议指南:主流许可证核心区别解析
  • 基于javaweb的SpringBoot汉服文化bbs系统设计与实现(源码+文档+部署讲解)
  • “Pseudo Global Warming”:伪全球变暖PGW
  • Diffusion-Based Planning for Autonomous Driving with Flexible Guidance论文细读
  • 【奇点时刻】GPT4o新图像生成模型底层原理深度洞察报告
  • 【Leetcode 994】腐烂的橘子 - 多源 BFS 解题思路与 Java 实现详解
  • Master节点重启k8s集群崩溃解决办法
  • AGI大模型(10):prompt逆向-巧借prompt
  • Windows 系统下安装 Docker
  • 构建网络 练习题三道
  • idea快捷键 Spring endpoint actions in the editor
  • Vue自定义指令最佳实践教程
  • 2022年蓝桥杯第十三届CC++大学B组真题及代码
  • Jmeter如何使用MD5进行加密?
  • 界面控件DevExpress WPF v25.1新功能预览 - AI功能增强
  • 大厂算法面试 7 天冲刺:第6天-树与图深度剖析——高频算法面试题 Java 实战
  • oracle WAIT 和 NOWAIT
  • 基于开源链动 2+1 模式 AI 智能名片 S2B2C 商城小程序的社群团购品牌命名策略研究
  • Kotlin 类委托深入解析:以 MMKV 为例看委托机制在 Android 中的巧妙应用
  • 大理州建设局官方网站/流量精灵
  • 如何将网站转成小程序/seo推广具体做什么
  • 织梦网站最新漏洞入侵/免费创建网站平台
  • 公司简介图片模板/南昌seo网站排名
  • 住房和城乡建设部干部学院网站/免费制作网站的软件
  • 江阴网站建设推广/百度入口的链接