# simple-cache-n1-pub **Repository Path**: caijiecom/simple-cache-n1-pub ## Basic Information - **Project Name**: simple-cache-n1-pub - **Description**: 一个使用起来很简单的多级缓存注解(@CjieSimpleCache) - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-04-27 - **Last Updated**: 2026-01-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # simple-cache-n1-pub 一个使用起来很简单的多级缓存注解(@CjieSimpleCache)。 ## 代码地址: Gitee:https://gitee.com/caijiecom/simple-cache-n1-pub Github:https://github.com/caijiecom/simple-cache-n1-pub ## 特别说明🤝 此project中仅仅是一些通用的功能,不存在任何业务逻辑,不存在“机密信息”,目前很多开源的组件中也有类似的功能。 此project用于个人练习和记录笔记使用,这些代码都是在非工作时间重新写的,未直接复用原代码,代码结构、命名和原代码有很大的不同,并且只保留了一些主要的代码逻辑。 此project中仅仅是一些主要的代码逻辑,其它代码逻辑被**简化、省略**掉了, **一些获取数据的部分,目前直接写死了一些测试数据,实际使用中是通过发其它应用的接口、查数据库、经过一些繁琐的业务逻辑处理来获取数据的**。 🎉 🎉 🎆🎇 ## 功能描述✨ - 本地debug启动此project,并访问`ProductWithSimpleCacheController`类中提供的一些接口,可以看出代码执行流程;未连接到Redis时,使用**Redis缓存**的部分代码会**异常**,但使用**本地缓存**的代码逻辑是只要启动就能**正常执行**的。 - 此project中实现了一个使用起来很简单的多级缓存注解(`@CjieSimpleCache`),支持仅本地缓存(Caffeine)、仅分布式缓存(Redis)、多级缓存(本地+分布式)模式,具体的缓存逻辑已在切面中实现(**见`CjieSimpleCacheAspect`类中的`doCache`方法**),使用时,在业务代码中获取数据的目标方法上加上该注解就能实现缓存功能,并且可以通过该注解的属性配置缓存参数。 - 解决了缓存三大难题: ✓ **防穿透**:实现空值缓存与短过期机制 ✓ **防击穿**:采用双重检测锁+分布式锁(Lua脚本保证原子性),确保缓存过期后只有一个线程执行数据库查询来重建缓存(防止多台服务器同时查询数据库),设计了`PodClientIdGenerator`,生成唯一的客户端标识,确保分布式锁的安全性。使用Lua脚本提升了锁操作的可靠性。 ✓ **防雪崩**:动态随机过期时间算法 - 实现动态缓存键生成(SpEL表达式/OGNL表达式/自定义枚举值方法/自定义同类中方法/默认方式),支持业务多维度的灵活缓存策略 - 通过Caffeine本地缓存降低Redis负载30%,整体缓存命中率提升至98%。 - 对于List型数据,可以通过注解属性配置使用Redis的List数据类型存储,并可以配置是否分页、分页大小,读写性能高于转化为String存储。 - 实现缓存配置预检器(`CacheConfigValidator`类),应用启动时会扫描所有带@CjieSimpleCache注解的方法,如果发现存在“缓存互斥锁超时时间(相当于缓存刷新间隔)>=缓存过期时间”的缓存注解配置,则打印错误日志并抛出异常使应用启动失败,便于发现问题。 - 成果:降低Redis负载30%,减少业务代码中缓存逻辑重复开发量80% - 此project中写死了一些测试数据(实际项目中这些数据是查数据库并通过一些计算和处理得到的。) ## 使用示例🛠️ `ProductWithSimpleCacheServiceImpl`类中展示了`@CjieSimpleCache`注解的一些使用示例, 例如:`getProductByIdWithMultiCacheOgnl`这个方法加了` @CjieSimpleCache`注解,`keyOgnlExpression`是缓存key表达式, 在该注解对应的切面逻辑中,会将`"ProductWithSimpleCacheServiceImpl.getProductByIdWithMultiCacheOgnl.{id}"`中的{id}替换为`getProductByIdWithMultiCache`方法输入参数中的`id`的实际值,假如传入该方法的id值为“123456”,则生成的缓存key值为`"ProductWithSimpleCacheServiceImpl.getProductByIdWithMultiCacheOgnl.123456"`: ```java /** * 根据产品编号获取产品信息,带多级缓存,缓存键使用OGNL表达式生成。 * * @param id 产品编号 */ @CjieSimpleCache( keyOgnlExpression = "ProductWithSimpleCacheServiceImpl.getProductByIdWithMultiCacheOgnl.{id}",//用来生成缓存键的OGNL表达式 type = CjieSimpleCache.CacheType.MULTI_LEVEL,//缓存类型:多级缓存 expire = 3600, //分布式缓存过期时间 cacheNull = true, //是否缓存null值(防穿透) nullExpire = 60, //空值过期时间(秒,仅当cacheNull=true时有效) lockTimeout = 300, //互斥锁超时时间(毫秒,防击穿) local = @CjieSimpleCache.LocalConfig(//本地缓存配置 maxSize = 5000, expireAfterWrite = 60 ) ) @Override public Product getProductByIdWithMultiCacheOgnl(Long id) { // 数据库查询逻辑 return productWithSimpleCacheRepository.findById(id); } ``` ## 未来计划📡 这个project后续还可以优化: - 增加缓存预热功能 (**实际项目中有**,但这个project中没写) - 集成监控指标暴露缓存命中率 - 支持TTL动态调整策略 - ……