🤟致敬讀者
- 🟩感謝閱讀🟦笑口常開🟪生日快樂?早點睡覺
📘博主相關
- 🟧博主信息🟨博客首頁🟫專欄推薦🟥活動信息
文章目錄
- 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
📜文末寄語
- 🟠關注我,獲取更多內容。
- 🟡技術動態、實戰教程、問題解決方案等內容持續更新中。
- 🟢《全棧知識庫》技術交流和分享社區,集結全棧各領域開發者,期待你的加入。
- 🔵?加入開發者的《專屬社群》,分享交流,技術之路不再孤獨,一起變強。
- 🟣點擊下方名片獲取更多內容🍭🍭🍭👇