Java 中 Redis 过期策略深度解析(含拓展-redis内存淘汰策略列举)
🤟致敬读者
- 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉
📘博主相关
- 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息
文章目录
- Java 中 Redis 过期策略深度解析
- 一、Redis 过期策略核心原理回顾
- 二、Java 中的过期操作 API
- 1. Jedis 客户端操作
- 2. Spring Data Redis 操作
- 三、Java 中的策略实践要点
- 1. 过期时间设置策略
- 2. 大Key过期优化
- 3. 缓存穿透/击穿防护
- 四、生产环境最佳实践
- 1. 过期键监控
- 2. 动态调整策略
- 3. 集群环境注意事项
- 五、常见问题排查
- 1. 内存未释放问题
- 2. 过期键未删除问题
- 六、高级特性应用
- 1. Redisson 过期监听
- 2. RedisJSON 过期扩展
- 总结:Java 开发者必备技能
- 拓展(Redis内存淘汰策略列举)
- noeviction 默认的
- volatile-lru
- volatile-ttl
- volatile-random
- allkeys-lru
- allkeys-random
📃文章前言
- 🔷文章均为学习工作中整理的笔记。
- 🔶如有错误请指正,共同学习进步。
Java 中 Redis 过期策略深度解析
在 Java 应用中,Redis 的过期策略是缓存管理的核心机制,直接关系到内存使用效率和系统性能。下面从原理到实践全面解析:
一、Redis 过期策略核心原理回顾
-
双重删除策略:
- 惰性删除:访问时检查过期时间,若过期则立即删除
- 定期删除:Redis 每秒执行 10 次(可配置)的过期扫描
# redis.conf 配置 hz 10 # 每秒扫描频率
-
内存淘汰机制:
二、Java 中的过期操作 API
1. Jedis 客户端操作
// 设置键值对并指定过期时间(秒)
jedis.setex("user:session:1001", 1800, "session_data"); // 单独设置过期时间
jedis.expire("cache:product:2023", 3600); // 秒
jedis.pexpire("temp:data", 5000L); // 毫秒// 获取剩余时间
long ttl = jedis.ttl("user:session:1001"); // 秒
long pttl = jedis.pttl("cache:product:2023"); // 毫秒
2. Spring Data Redis 操作
// 注解方式设置缓存过期
@Cacheable(value = "users", key = "#userId", cacheManager = "customCacheManager")
public User getUser(String userId) {// ...
}// 配置自定义 CacheManager
@Bean
public RedisCacheManager customCacheManager(RedisConnectionFactory factory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30)) // 全局默认30分钟.serializeValuesWith(SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<>(User.class)));return RedisCacheManager.builder(factory).cacheDefaults(config).withCacheConfiguration("users", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(2))) // 特定缓存2小时.build();
}
三、Java 中的策略实践要点
1. 过期时间设置策略
-
动态 TTL:避免缓存雪崩
// 基础过期时间 + 随机偏移量 int baseExpire = 3600; // 1小时 int randomOffset = new Random().nextInt(600); // 0-10分钟随机 jedis.setex("hot_product", baseExpire + randomOffset, productData);
-
分级过期:
数据类型 建议 TTL 说明 用户会话 30-60分钟 高安全性要求 商品详情 2-4小时 中等更新频率 全局配置 永久(不设) 极少变更
2. 大Key过期优化
Redis 6.0+ 异步删除配置:
// 启动Redis时配置异步删除
new RedisServer("redis-server", "--lazyfree-lazy-expire yes","--lazyfree-lazy-eviction yes");// Redisson 处理大Hash
RMapCache<String, Product> map = redisson.getMapCache("products");
map.expire(2, TimeUnit.HOURS); // 整个Map过期
3. 缓存穿透/击穿防护
// 双重检查锁解决缓存击穿
public Product getProduct(String id) {String key = "product:" + id;String data = jedis.get(key);if ("".equals(data)) return null; // 空值缓存if (data == null) {synchronized (this) {data = jedis.get(key);if (data == null) {Product product = db.getProduct(id);if (product == null) {jedis.setex(key, 300, ""); // 空值缓存5分钟return null;}jedis.setex(key, 3600, serialize(product));return product;}}}return deserialize(data);
}
四、生产环境最佳实践
1. 过期键监控
// 获取Redis统计信息
String stats = jedis.info("stats");
Pattern pattern = Pattern.compile("expired_keys:(\\d+)");
Matcher matcher = pattern.matcher(stats);
if (matcher.find()) {long expiredKeys = Long.parseLong(matcher.group(1));metrics.record("redis.expired_keys", expiredKeys);
}// Spring Boot Actuator 监控
@Bean
public MeterRegistryCustomizer<MeterRegistry> redisMetrics() {return registry -> {registry.gauge("redis.expired_keys", Tags.of("host", redisHost),() -> jedis.info("stats").contains("expired_keys:") ? Long.parseLong(jedis.info("stats").split("expired_keys:")[1].split("\r")[0]) : 0);};
}
2. 动态调整策略
// 根据负载动态调整过期时间
int getDynamicTTL() {double load = getSystemLoad();if (load > 0.8) return 600; // 高负载时缩短TTLif (load < 0.3) return 3600; // 低负载时延长TTLreturn 1800; // 默认30分钟
}jedis.setex("cache:data", getDynamicTTL(), data);
3. 集群环境注意事项
- 主从延迟:主节点删除后从节点可能短暂存在过期数据
// 强制读主节点解决脏读 if (consistencyRequired) {jedis.readonly(); // 关闭只读模式(默认从主节点读) }
- 跨数据中心:使用 Redisson 的 RRemoteService
RRemoteService remoteService = redisson.getRemoteService(); remoteService.register(ProductService.class, productServiceImpl, RemoteInvocationOptions.defaults().timeout(3, TimeUnit.SECONDS));
五、常见问题排查
1. 内存未释放问题
现象:INFO memory
显示内存未减少
排查步骤:
- 检查
maxmemory-policy
配置 - 监控
evicted_keys
和expired_keys
计数器 - 使用
redis-cli --bigkeys
分析大Key - 检查是否启用异步删除(Redis 6.0+)
2. 过期键未删除问题
原因:
- 键长期未被访问(惰性删除未触发)
- 定期删除扫描未命中(概率性遗漏)
- 主从同步延迟
解决方案:
// 主动触发过期扫描(生产慎用)
jedis.configSet("hz", 100); // 临时提高扫描频率
Thread.sleep(5000); // 等待5秒
jedis.configSet("hz", 10); // 恢复默认
六、高级特性应用
1. Redisson 过期监听
// 监听特定键过期事件
RMapCache<String, String> map = redisson.getMapCache("sessions");
map.addListener(new ExpiredListener<String, String>() {@Overridepublic void onExpired(EntryEvent<String, String> event) {log.info("Session expired: {}", event.getKey());// 触发清理动作}
});
2. RedisJSON 过期扩展
// 使用 RedisJSON 模块设置字段级过期
JSONObject product = new JSONObject();
product.put("id", 1001);
product.put("name", "Laptop");
product.put("price", 999.99);// 设置整体过期
jedis.jsonSetWithEscape("product:1001", product, 3600);// 设置字段级过期(需要RedisJSON 2.6+)
jedis.sendCommand(Command.JSON_SET, "product:1001", ".price", "\"899.99\"", "EX", "600" // 价格字段10分钟后过期
);
总结:Java 开发者必备技能
-
策略选择:
- 会话数据 →
volatile-ttl
- 高频访问数据 →
volatile-lru
- 全局数据 →
allkeys-lru
- 会话数据 →
-
性能口诀:
“小Key高频用惰删,大Key过期启异步;
动态TTL防雪崩,双删机制保一致” -
监控指标:
指标 健康阈值 报警条件 expired_keys/sec <1000 持续>5000 evicted_keys/sec 0 任何驱逐发生 mem_fragmentation_ratio 1.0-1.5 >1.8 或 <0.9
掌握这些知识,你将在 Java 项目中构建高效可靠的 Redis 缓存系统,轻松应对高并发场景下的数据过期挑战。
拓展(Redis内存淘汰策略列举)
Redis 提供了几种内存淘汰策略来处理当可用内存不足时如何自动删除键以释放空间的问题。以下是 Redis 中常见的几种内存淘汰策略:
noeviction 默认的
这是默认的策略。当内存使用达到上限并且客户端尝试执行会导致更多内存使用的命令(比如添加新数据)时,Redis 会返回错误。
实现方式:Redis 直接拒绝执行可能导致内存增加的命令。
例子:假设 Redis 已经达到内存上限,此时执行SET命令添加新的键值对,Redis 会返回错误并拒绝该操作。
volatile-lru
从设置了过期时间的键值对中,移除最近最少使用的键值对。
实现方式:Redis 会维护一个记录设置了过期时间的键的访问时间的队列,当需要淘汰数据时,从队列尾部移除元素。
例子:有多个设置了过期时间的键key1、key2和key3,其中key1最近访问最少,当内存不足时,key1会被淘汰。
volatile-ttl
移除即将过期的键值对,也就是剩余生存时间(TTL)最短的键值对。
实现方式:Redis 会遍历设置了过期时间的键,比较它们的 TTL,选择 TTL 最小的进行淘汰。
例子:键keyA的 TTL 为 10 秒,键keyB的 TTL 为 5 秒,当内存不足时,keyB会被优先淘汰。
volatile-random
在设置了过期时间的键值对中,随机移除某个键值对。
实现方式:通过随机算法从设置了过期时间的键集合中选择一个进行淘汰。
例子:在一组设置了过期时间的键中,随机选取一个如keyX进行淘汰。
allkeys-lru
从所有键值对中,移除最近最少使用的键值对。
实现方式:Redis 维护一个所有键的访问时间队列,淘汰时从队列尾部移除。
例子:包括设置了过期时间和未设置过期时间的多个键,如keyC最近访问最少,当内存不足时,keyC被淘汰。
allkeys-random
从所有键值对中,随机移除某个键值对。
实现方式:通过随机算法从所有键集合中选择一个进行淘汰。
例子:在所有键中,随机选择如keyY进行淘汰。
该拓展部分参考文章:https://cloud.tencent.com/developer/news/1677151
📜文末寄语
- 🟠关注我,获取更多内容。
- 🟡技术动态、实战教程、问题解决方案等内容持续更新中。
- 🟢《全栈知识库》技术交流和分享社区,集结全栈各领域开发者,期待你的加入。
- 🔵加入开发者的《专属社群》,分享交流,技术之路不再孤独,一起变强。
- 🟣点击下方名片获取更多内容🍭🍭🍭👇