別再寫冗余緩存代碼了!Easy-Cache 給你統(tǒng)一解決方案,爽到飛起!
在分布式系統(tǒng)的開發(fā)過程中,緩存始終是最讓人頭疼的部分之一。數(shù)據(jù)一致性如何保證?Redis 萬一宕機怎么辦?緩存穿透、緩存擊穿、緩存雪崩這些問題如何優(yōu)雅處理? 更糟糕的是,每個項目幾乎都要重復寫一堆緩存相關(guān)的代碼:增刪查改、異常兜底、二級緩存切換……既浪費時間,又極易出錯。
為了讓開發(fā)者不再被這些細節(jié)“綁架”,我們基于 RocksCache 的核心理念 實現(xiàn)了一個統(tǒng)一的緩存一致性解決框架 —— Easy-Cache。它通過 Spring AOP + 注解驅(qū)動 的方式,讓緩存變得像加一行注解一樣輕松,還支持 Redis 集群 + 本地二級緩存,并內(nèi)置了 自動降級、彈性過期、最終一致性保障 等特性。
換句話說,開發(fā)者只需要關(guān)注業(yè)務(wù)邏輯,緩存的問題交給 Easy-Cache。
核心理念
Easy-Cache 的目標很明確:讓開發(fā)者告別冗余的緩存處理代碼,只需一行注解就能啟用緩存能力,而一致性、容錯、降級等復雜邏輯全部由框架接管。
例如:
// /src/main/java/com/icoderoad/user/service/UserService.java
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 查詢用戶信息,優(yōu)先從緩存獲取
@Cacheable(clusterId = "cluster1", prefix = "user", keys = {"#userId"})
public User getUserById(Long userId) {
return userRepository.findById(userId);
}
// 更新用戶信息時,自動刷新緩存
@UpdateCache(clusterId = "cluster1", prefix = "user", keys = {"#user.userId"})
public User update(User user) {
return userRepository.update(user);
}
}開發(fā)人員完全不需要再手寫緩存處理邏輯。
核心實現(xiàn)
實現(xiàn)目標
Easy-Cache 的設(shè)計目標是:簡單、低侵入、自動化。 利用 Spring AOP 攔截注解,在切面中實現(xiàn)完整的緩存邏輯:
- 查詢時:自動判斷是否命中緩存
- 更新時:自動刷新緩存并保證一致性
- 異常時:觸發(fā)降級或兜底策略
開發(fā)者寫業(yè)務(wù)邏輯,框架管緩存。
設(shè)計思路
Easy-Cache 的運行機制分為以下幾個步驟:
- 注解攔截:
@Cacheable和@UpdateCache觸發(fā)緩存邏輯 - 統(tǒng)一調(diào)度:中央調(diào)度器負責協(xié)調(diào)查詢、更新與容錯
- 容錯機制:防止緩存穿透、緩存擊穿等問題
- 多級緩存:Redis + 本地緩存,保障高可用
- 一致性保障:通過 Lua 腳本保證操作原子性
多級緩存動態(tài)升降級
Easy-Cache 默認使用 Redis 作為一級緩存,本地緩存作為二級緩存。 當 Redis 集群不可用時,系統(tǒng)自動切換到本地緩存,避免全局雪崩。
工作流程:
- 請求進入時先訪問 Redis
- 如果 Redis 異常,觸發(fā)故障事件,自動降級到本地緩存
- 系統(tǒng)啟動探活任務(wù),檢測 Redis 是否恢復
- 一旦 Redis 可用,緩存自動升級回 Redis
這樣,開發(fā)者無需寫一行降級代碼,服務(wù)即可保持穩(wěn)定。
數(shù)據(jù)一致性機制
數(shù)據(jù)一致性是緩存框架的核心難點。Easy-Cache 借鑒了 RocksCache 的思路,采用 Redis Hash 結(jié)構(gòu) + Lua 腳本 實現(xiàn)。
關(guān)鍵字段:
- value:緩存數(shù)據(jù)
- lockInfo:鎖狀態(tài)(locked/unLock)
- unlockTime:鎖過期時間
- owner:鎖持有者
這些字段通過 Lua 腳本實現(xiàn)分布式鎖,保證緩存的最終一致性。
并發(fā)場景:
- 讀讀并發(fā):只有一個線程能查庫,其余線程等待或直接讀緩存,避免重復查詢
- 讀寫并發(fā):更新線程會強制刷新緩存,保證新數(shù)據(jù)不會被舊值覆蓋
- 彈性過期:緩存不會立即刪除,而是“軟刪除”,允許短時間內(nèi)讀取舊值,避免雪崩
Lua 腳本預加載
為了減少網(wǎng)絡(luò) IO 開銷,Easy-Cache 在服務(wù)啟動時會將 Lua 腳本通過 SCRIPT LOAD 預加載到 Redis,并記錄 SHA1 哈希。 后續(xù)執(zhí)行時只需傳輸哈希值(40 bytes),相比每次傳輸完整腳本(500 bytes),性能提升顯著。
同時,框架內(nèi)置了重試機制,保證在 Redis 不穩(wěn)定時依然能完成腳本預加載。
核心特性
分布式鎖保證一致性
- Lua 腳本實現(xiàn)原子操作
- 分布式鎖確保最終一致性
- 預加載優(yōu)化性能
多級緩存架構(gòu)
- Redis + 本地緩存雙保險
- 宕機自動降級
- 恢復后自動升級
彈性過期機制
- 標記刪除,避免擊穿
- 默認 1.5s 異步更新,保證最終一致性
- 支持設(shè)置為 0s,強制實時一致
注解驅(qū)動
- 一行注解替代上百行緩存邏輯
- 降低學習成本
- 操作模式統(tǒng)一規(guī)范
實戰(zhàn)示例
下面給出一個最簡化的可運行示例,展示 Easy-Cache 如何集成到 Spring Boot 項目中。
啟動類
// /src/main/java/com/icoderoad/EasyCacheApplication.java
package com.icoderoad;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class EasyCacheApplication {
public static void main(String[] args) {
SpringApplication.run(EasyCacheApplication.class, args);
}
}application.yml
# /src/main/resources/application.yml
spring:
application:
name: easy-cache-demo
redis:
host: 127.0.0.1
port: 6379
password: ""
database: 0
timeout: 5000
# Easy-Cache 自定義配置
easycache:
cluster:
cluster1:
enable: true
expire: 30s # 緩存過期時間
soft-expire: 1s # 彈性過期時間
retry-times: 3 # Redis 故障重試次數(shù)UserRepository 示例
// /src/main/java/com/icoderoad/user/repository/UserRepository.java
package com.icoderoad.user.repository;
import com.icoderoad.user.model.User;
import org.springframework.stereotype.Repository;
import java.util.HashMap;
import java.util.Map;
@Repository
public class UserRepository {
private final Map<Long, User> storage = new HashMap<>();
public User findById(Long id) {
return storage.get(id);
}
public User update(User user) {
storage.put(user.getUserId(), user);
return user;
}
}User 實體
// /src/main/java/com/icoderoad/user/model/User.java
package com.icoderoad.user.model;
public class User {
private Long userId;
private String username;
// Getter & Setter
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}UserController 示例
// /src/main/java/com/icoderoad/user/controller/UserController.java
package com.icoderoad.user.controller;
import com.icoderoad.user.model.User;
import com.icoderoad.user.service.UserService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// 查詢用戶
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
// 更新用戶
@PostMapping("/update")
public User updateUser(@RequestBody User user) {
return userService.update(user);
}
}測試示例
服務(wù)啟動后,可以用 curl 或瀏覽器訪問:
# 查詢用戶(緩存命中前會訪問 repository)
curl http://localhost:8080/users/1
# 更新用戶(會自動刷新緩存)
curl -X POST http://localhost:8080/users/update \
-H "Content-Type: application/json" \
-d '{"userId":1,"username":"Tom"}'總結(jié)
Easy-Cache 的設(shè)計初衷是:讓開發(fā)者只寫業(yè)務(wù)邏輯,不再為緩存操心。 它解決了分布式系統(tǒng)中緩存常見的五大痛點:
- 冗余代碼:注解驅(qū)動,零重復
- 緩存穿透:內(nèi)置防護機制
- 緩存擊穿:分布式鎖與彈性過期機制兜底
- 數(shù)據(jù)不一致:Redis-Hash + Lua 腳本保障最終一致性
- Redis 宕機:自動降級 + 探活機制,保障高可用
在實際項目中,開發(fā)人員只需在方法上加注解,緩存邏輯就能自動完成。 這不僅提升了開發(fā)效率,也讓系統(tǒng)更穩(wěn)定、更健壯。
Easy-Cache,就是開發(fā)者的緩存“終極解法”。
























