大数跨境
0
0

别再写冗余缓存代码了!Easy-Cache 给你统一解决方案,爽到飞起!

别再写冗余缓存代码了!Easy-Cache 给你统一解决方案,爽到飞起! 终码一生
2025-11-03
0
点击“终码一生”,关注,置顶公众号
每日技术干货,第一时间送达!
在分布式系统的开发过程中,缓存始终是最让人头疼的部分之一。数据一致性如何保证?Redis 万一宕机怎么办?缓存穿透、缓存击穿、缓存雪崩这些问题如何优雅处理? 更糟糕的是,每个项目几乎都要重复写一堆缓存相关的代码:增删查改、异常兜底、二级缓存切换……既浪费时间,又极易出错。
为了让开发者不再被这些细节“绑架”,我们基于 RocksCache 的核心理念 实现了一个统一的缓存一致性解决框架 —— Easy-Cache。它通过 Spring AOP + 注解驱动 的方式,让缓存变得像加一行注解一样轻松,还支持 Redis 集群 + 本地二级缓存,并内置了 自动降级、弹性过期、最终一致性保障 等特性。
换句话说,开发者只需要关注业务逻辑,缓存的问题交给 Easy-Cache。
01
核心理念
Easy-Cache 的目标很明确:让开发者告别冗余的缓存处理代码,只需一行注解就能启用缓存能力,而一致性、容错、降级等复杂逻辑全部由框架接管。
例如:
@Service
publicclass UserService {

    privatefinal UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // 查询用户信息,优先从缓存获取
    @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);
    }
}
开发人员完全不需要再手写缓存处理逻辑。
02
核心实现
实现目标
Easy-Cache 的设计目标是:简单、低侵入、自动化。 利用 Spring AOP 拦截注解,在切面中实现完整的缓存逻辑:
  • 查询时:自动判断是否命中缓存
  • 更新时:自动刷新缓存并保证一致性
  • 异常时:触发降级或兜底策略
开发者写业务逻辑,框架管缓存。
设计思路
Easy-Cache 的运行机制分为以下几个步骤:
  • 注解拦截:@Cacheable 和 @UpdateCache 触发缓存逻辑
  • 统一调度:中央调度器负责协调查询、更新与容错
  • 容错机制:防止缓存穿透、缓存击穿等问题
  • 多级缓存:Redis + 本地缓存,保障高可用
  • 一致性保障:通过 Lua 脚本保证操作原子性
多级缓存动态升降级
Easy-Cache 默认使用 Redis 作为一级缓存,本地缓存作为二级缓存。 当 Redis 集群不可用时,系统自动切换到本地缓存,避免全局雪崩。
工作流程:
  1. 请求进入时先访问 Redis
  2. 如果 Redis 异常,触发故障事件,自动降级到本地缓存
  3. 系统启动探活任务,检测 Redis 是否恢复
  4. 一旦 Redis 可用,缓存自动升级回 Redis
这样,开发者无需写一行降级代码,服务即可保持稳定。
数据一致性机制
数据一致性是缓存框架的核心难点。Easy-Cache 借鉴了 RocksCache 的思路,采用 Redis Hash 结构 + Lua 脚本 实现
关键字段:
  • value:缓存数据
  • lockInfo:锁状态(locked/unLock)
  • unlockTime:锁过期时间
  • owner:锁持有者
这些字段通过 Lua 脚本实现分布式锁,保证缓存的最终一致性。
并发场景:
  • 读读并发:只有一个线程能查库,其余线程等待或直接读缓存,避免重复查询
  • 读写并发:更新线程会强制刷新缓存,保证新数据不会被旧值覆盖
  • 弹性过期:缓存不会立即删除,而是“软删除”,允许短时间内读取旧值,避免雪崩
Lua 脚本预加载
为了减少网络 IO 开销,Easy-Cache 在服务启动时会将 Lua 脚本通过 SCRIPT LOAD 预加载到 Redis,并记录 SHA1 哈希。 后续执行时只需传输哈希值(40 bytes),相比每次传输完整脚本(500 bytes),性能提升显著。
同时,框架内置了重试机制,保证在 Redis 不稳定时依然能完成脚本预加载。
03
核心特性
分布式锁保证一致性
  • Lua 脚本实现原子操作
  • 分布式锁确保最终一致性
  • 预加载优化性能
多级缓存架构
  • Redis + 本地缓存双保险
  • 宕机自动降级
  • 恢复后自动升级
弹性过期机制
  • 标记删除,避免击穿
  • 默认 1.5s 异步更新,保证最终一致性
  • 支持设置为 0s,强制实时一致
注解驱动
  • 一行注解替代上百行缓存逻辑
  • 降低学习成本
  • 操作模式统一规范
04
实战示例
下面给出一个最简化的可运行示例,展示 Easy-Cache 如何集成到 Spring Boot 项目中。
启动类
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
spring:
application:
    name: easy-cache-demo
redis:
    host127.0.0.1
    port6379
    password""
    database0
    timeout5000

# Easy-Cache 自定义配置
easycache:
cluster:
    cluster1:
      enable: true
      expire30s      # 缓存过期时间
      soft-expire1s  # 弹性过期时间
      retry-times3   # Redis 故障重试次数
UserRepository 示例
package com.icoderoad.user.model;

publicclass 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 示例
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")
publicclass UserController {

    privatefinal 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);
    }
}
测试示例
服务启动后,可以用 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"}'
05
总结
Easy-Cache 的设计初衷是:让开发者只写业务逻辑,不再为缓存操心。 它解决了分布式系统中缓存常见的五大痛点:
  1. 冗余代码:注解驱动,零重复
  2. 缓存穿透:内置防护机制
  3. 缓存击穿:分布式锁与弹性过期机制兜底
  4. 数据不一致:Redis-Hash + Lua 脚本保障最终一致性
  5. Redis 宕机:自动降级 + 探活机制,保障高可用
在实际项目中,开发人员只需在方法上加注解,缓存逻辑就能自动完成。 这不仅提升了开发效率,也让系统更稳定、更健壮。
Easy-Cache,就是开发者的缓存“终极解法”。
来源:blog.csdn.net/nihao2q/article/details/151687452
END
PS:防止找不到本篇文章,可以收藏点赞,方便翻阅查找哦。



往期推荐



千万级大表如何删除数据?

开源项目|用Java开发一款AI系统,支持文案/PPT/图片/视频生成

SpringBoot 时间轮实现延时任务

Spring Event,贼好用的业务解耦神器!

Postman替代品:一款极简的网页版 API 调试神器!

不好意思,HttpClient该换了!


【声明】内容源于网络
0
0
终码一生
开发者聚集地。分享Java相关开发技术(JVM,多线程,高并发,性能调优等),开源项目,常见开发问题和前沿科技资讯!
内容 1876
粉丝 0
终码一生 开发者聚集地。分享Java相关开发技术(JVM,多线程,高并发,性能调优等),开源项目,常见开发问题和前沿科技资讯!
总阅读1.4k
粉丝0
内容1.9k