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

Redis的内存淘汰策略

 Redis 的内存淘汰策略简介

Redis 支持以下内存淘汰策略:

1. **noeviction(默认策略)**:不删除任何键,当内存不足时,写入操作会直接返回错误。
2. **allkeys-lru**:在所有键中删除最近最少使用(LRU,Least Recently Used)的键。
3. **volatile-lru**:在设置了过期时间的键中删除最近最少使用的键。
4. **allkeys-random**:在所有键中随机删除某个键。
5. **volatile-random**:在设置了过期时间的键中随机删除某个键。
6. **volatile-ttl**:在设置了过期时间的键中删除剩余存活时间(TTL,Time-To-Live)最短的键。
7. **allkeys-lfu**:在所有键中删除使用频率最低(LFU,Least Frequently Used)的键。
8. **volatile-lfu**:在设置了过期时间的键中删除使用频率最低的键。

 代码实现与说明

在这部分,我们将通过 Java 代码示例,演示不同的 Redis 内存淘汰策略的使用方式。我们将使用 Jedis 库连接 Redis,并展示每种策略的特点和使用场景。

1. 设置和检查 Redis 配置

首先,确保 Redis 配置文件(`redis.conf`)中设置了 `maxmemory` 和 `maxmemory-policy` 参数。


maxmemory 100mb  # 设置最大内存为 100MB

可以通过 `redis-cli` 或者 Java 代码动态设置内存淘汰策略。

2. 添加依赖

确保您的项目包含 Jedis 依赖。对于 Maven 项目,在 `pom.xml` 中添加以下依赖项:


<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.3.1</version>
</dependency>

 3. Java 代码示例

下面是 Java 代码示例,展示如何使用不同的 Redis 内存淘汰策略。


import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisDataException;

public class RedisEvictionPoliciesExample {

    // Redis 连接配置
    private static final String REDIS_HOST = "localhost";
    private static final int REDIS_PORT = 6379;

    // 数据生成配置
    private static final int INITIAL_LOAD = 100000; // 初始插入数据数量
    private static final int TEST_LOAD = 50000;     // 测试插入数据数量
    private static final String VALUE_PREFIX = "value_"; // 数据前缀

    public static void main(String[] args) {
        // 初始化 Redis 连接
        Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT);

        try {
            // 1. 设置不同的内存淘汰策略
            String[] policies = {"noeviction", "allkeys-lru", "volatile-lru", "allkeys-random", "volatile-random", "volatile-ttl", "allkeys-lfu", "volatile-lfu"};

            for (String policy : policies) {
                // 设置内存淘汰策略
                jedis.configSet("maxmemory-policy", policy);
                System.out.println("当前 Redis 的内存淘汰策略设置为: " + policy);

                // 清空 Redis 数据库以便进行测试
                jedis.flushAll();

                System.out.println("开始插入初始数据...");
                // 2. 初始加载数据,模拟大量数据插入
                for (int i = 0; i < INITIAL_LOAD; i++) {
                    String key = "key_" + i;
                    String value = VALUE_PREFIX + i;
                    int ttl = (i % 300) + 1; // 设置 TTL 为 1 到 300 秒之间的随机数

                    if (policy.contains("volatile") || policy.equals("allkeys-lfu") || policy.equals("allkeys-lru")) {
                        jedis.setex(key, ttl, value); // 设置过期时间的键
                    } else {
                        jedis.set(key, value); // 不设置过期时间的键
                    }

                    if (i % 10000 == 0) {
                        System.out.println("已插入初始数据 " + i + " 条");
                    }
                }

                System.out.println("初始数据插入完成。");

                // 3. 访问部分键(对于LFU策略有影响)
                if (policy.contains("lfu")) {
                    System.out.println("访问部分数据,增加其使用频率...");
                    for (int i = 0; i < 100000; i++) {
                        String key = "key_" + (i % 100); // 反复访问前100个键
                        jedis.get(key);
                    }
                    System.out.println("频繁访问的数据设置完成。");
                }

                // 4. 插入更多数据,超过内存上限,触发淘汰机制
                System.out.println("插入更多数据以触发淘汰策略...");
                for (int i = INITIAL_LOAD; i < INITIAL_LOAD + TEST_LOAD; i++) {
                    String key = "key_" + i;
                    String value = VALUE_PREFIX + i;
                    int ttl = (i % 300) + 1; // 设置 TTL 为 1 到 300 秒之间的随机数
                    
                    try {
                        if (policy.contains("volatile") || policy.equals("allkeys-lfu") || policy.equals("allkeys-lru")) {
                            jedis.setex(key, ttl, value); // 设置过期时间的键
                        } else {
                            jedis.set(key, value); // 不设置过期时间的键
                        }
                    } catch (JedisDataException e) {
                        if (e.getMessage().contains("OOM")) {
                            System.out.println("内存不足!无法插入更多数据。写操作被拒绝: " + key);
                            break;
                        } else {
                            throw e; // 其他异常抛出
                        }
                    }

                    if (i % 10000 == 0) {
                        System.out.println("已插入测试数据 " + i + " 条");
                    }
                }

                // 5. 验证哪些数据被淘汰
                System.out.println("验证哪些数据被淘汰...");
                int missCount = 0;
                for (int i = 0; i < INITIAL_LOAD; i++) {
                    String key = "key_" + i;
                    String value = jedis.get(key);

                    if (value == null) {
                        missCount++;
                    }
                }
                System.out.println("初始数据中被 " + policy + " 策略淘汰的键数量: " + missCount);
                System.out.println("==========================================");
            }

        } finally {
            // 关闭 Redis 连接
            jedis.close();
        }
    }
}

代码解释

1. **初始化 Redis 连接**:
   - 使用 Jedis 连接到本地 Redis 实例。

2. **设置不同的内存淘汰策略**:
   - 通过循环,逐个设置内存淘汰策略(`noeviction`、`allkeys-lru`、`volatile-lru`、`allkeys-random`、`volatile-random`、`volatile-ttl`、`allkeys-lfu`、`volatile-lfu`)。
   - 使用 `jedis.configSet("maxmemory-policy", policy)` 设置策略。

3. **插入初始数据**:
   - 使用一个 `for` 循环向 Redis 插入数据,模拟达到内存上限的场景。
   - 对于某些策略(如 `volatile` 和 `allkeys-lfu`),设置 TTL(1 到 300 秒之间的随机数)。

4. **访问频率管理**:
   - 对于 `LFU` 策略,频繁访问前 100 个键,以增加这些键的使用频率,确保它们不被淘汰。

5. **插入更多数据以触发淘汰机制**:
   - 继续插入额外的数据,这将导致 Redis 达到内存上限并触发指定的内存淘汰策略。

6. **验证哪些数据被淘汰**:
   - 遍历初始插入的数据,统计哪些键被淘汰,输出被淘汰的数量。

各策略的适用场景和特点

1. **noeviction**:适用于不希望任何数据被删除的场景。当内存不足时,写入操作会返回错误,防止数据丢失。

2. **allkeys-lru**:适合缓存场景,保留最近最常用的数据。删除那些最近最少使用的键,保持热点数据在内存中。

3. **volatile-lru**:适用于希望删除临时数据(设置了 TTL)的场景,保留经常访问的临时数据。

4. **all

keys-random**:适用于不关心具体删除哪个键的场景,数据访问模式比较均匀时效果较好。

5. **volatile-random**:类似于 `allkeys-random`,但只会删除设置了 TTL 的键。适用于希望随机淘汰临时数据的场景。

6. **volatile-ttl**:优先删除即将过期的键。适用于需要动态管理数据生命周期的场景。

7. **allkeys-lfu**:适合缓存场景,需要根据使用频率来决定哪些数据被淘汰。删除使用频率最低的键。

8. **volatile-lfu**:类似于 `allkeys-lfu`,但只会删除设置了 TTL 的键。适用于希望根据使用频率管理临时数据的场景。

配置和调优

- **合理设置 `maxmemory`**:根据实际应用的内存需求和服务器的物理内存,合理设置 `maxmemory` 参数。
- **监控和调整**:通过 Redis 的 `INFO` 命令或其他监控工具,定期监控 Redis 的内存使用情况,确保内存管理策略的有效性。
- **优化数据访问模式**:根据不同策略的特性,优化数据访问模式,使高频数据能够更长时间地保留在内存中。

 总结

     Redis中的内存淘汰策略是用于在内存不足时选择哪些键需要被删除以释放内存空间。以下是Redis中常用的内存淘汰策略:

  1. noeviction:不删除任何键,当内存不足时,所有写操作都会报错。
  2. allkeys-lru:在所有键中,选择最近最少使用(Least Recently Used)的键进行删除。
  3. volatile-lru:在设置了过期时间的键中,选择最近最少使用的键进行删除。
  4. allkeys-random:在所有键中,随机选择一个键进行删除。
  5. volatile-random:在设置了过期时间的键中,随机选择一个键进行删除。
  6. volatile-ttl:在设置了过期时间的键中,选择剩余时间最短的键进行删除。
  7. volatile-lfu:在设置了过期时间的键中,选择最不经常使用(Least Frequently Used)的键进行删除。
  8. allkeys-lfu:在所有键中,选择最不经常使用的键进行删除。

    可以通过配置文件或使用命令来选择合适的内存淘汰策略。不同的淘汰策略适用于不同的场景,例如,如果希望保留最近最常访问的键,可以选择LRU策略;如果希望保留剩余时间最短的键,可以选择TTL策略。


http://www.kler.cn/a/288838.html

相关文章:

  • VirtualBox下ubuntu23.04使用主机串口以及使用 minicom 进行串口调试
  • NTLM 中继到 LDAP 结合 CVE-2019-1040 接管全域
  • docker commit生成的镜像瘦身
  • 【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
  • GitCode 光引计划投稿 | GoIoT:开源分布式物联网开发平台
  • React第十八节 useEffect 用法使用技巧注意事项详解
  • 正则表达式实现带有条件的爬取
  • 动态规划-最大子数组和
  • [解决]Prometheus 与 Grafana进行组合,但是不显示数据与图像
  • 【王树森】Transformer模型(1/2): 剥离RNN,保留Attention(个人向笔记)
  • Java开发学习Kotlin 笔记
  • 每天学习一个基础算法之插入排序
  • 谷歌地图广告指南
  • P1438 无聊的数列
  • React 实现PDF预览(数据源使用文件流而不是url)
  • 哪些好用的待办事项清单值得推荐:待办任务清单app
  • (二十八)STL set(集合)
  • 前端vue中怎么判断接口请求返回的时长
  • 【量化交易的数学基础】文科生也能搞懂的线性代数基础:矩阵和向量的那些事儿
  • 学习日志29
  • 【IT工具】Windows下XMind安装教程【不要米】及常用快捷键
  • 翻译_Clock Domain Crossing Design
  • 【RSA】简单说说什么是RSA非对称加密
  • C++封装:栈、队列
  • Vue.js 模板语法详解:插值表达式与指令使用指南
  • 企业微信hook协议接口,聚合群聊客户管理工具开发