目录

一、本期个人核心任务

二、缓存策略设计

(一)需求分析

(二)方案设计

1.缓存模式选择

2.缓存key生成方案

3.缓存管理器配置方案

4.最终方案

三、开发实现

四、测试验证

五、开发过程中的问题与解决方案

问题:Redis反序列化失败

六、实训收获与技术成长

七、后续开发计划


一、本期个人核心任务

        负责Redis缓存优化方案的设计与落地,针对高频访问、低频更新的精选应用列表场景实现缓存能力,解决数据库查询压力大、接口响应慢的问题,提升平台首页访问的流畅度与用户体验。

二、缓存策略设计

        当前平台首页精选应用列表接口直接查询数据库,随着访问量增加,数据库查询压力持续增大,接口响应耗时变长,用户浏览首页时易出现加载延迟的问题。

        因此本次开发的核心目标是:构建基于Redis的旁路缓存体系,针对精选应用列表场景定制缓存策略,通过SpringCache注解简化缓存开发,实现“缓存优先、数据库兜底”的查询逻辑,降低数据库访问频次,提升接口响应速度

(一)需求分析

        平台首页精选应用列表是用户访问频次最高的核心接口之一,现有实现存在明显痛点:

  1. 精选应用列表无缓存层,每次请求均查询数据库,高并发下数据库压力大;

  2. 精选应用由管理员手动维护,更新频率低,但每次查询仍需走数据库链路,资源利用率低;

        为解决上述问题,本次开发需实现:

  1. 设计旁路缓存模式,针对精选应用列表场景定制“查询先查缓存、未命中查库并回写缓存”的逻辑;

  2. 开发缓存工具类,实现缓存key的统一生成与管理,避免key重复或过长问题;

  3. 基于Spring Data Redis配置缓存管理器,适配不同缓存区域的过期时间需求;

  4. 通过缓存注解实现接口缓存能力,仅缓存前10页高频访问数据,兼顾缓存效率与内存占用。

        通过本次需求落地,平台将新增“精选应用列表缓存”能力,形成“缓存层→数据库层”的双层查询体系,降低数据库压力,提升首页访问体验。

(二)方案设计

        针对Redis缓存优化的需求,梳理核心设计思路并明确技术方案:

1.缓存模式选择

        对比常见缓存模式(旁路缓存、读写穿透、写回缓存),结合精选应用“读多写少”的特点,选择旁路缓存模式

  • 查询逻辑:缓存命中→直接返回;缓存未命中→查数据库→回写缓存→返回结果;

  • 更新逻辑:无需主动删除缓存,通过设置合理过期时间,让缓存自然失效,避免缓存与数据库数据不一致;

  • 优势:实现简单,无需侵入数据库操作逻辑,适配现有代码架构。

2.缓存key生成方案

        为避免key重复、过长或格式不统一问题,设计“参数序列化+MD5哈希”的生成逻辑:

  • 第一步:将接口参数(如页码、页大小)通过JSON序列化转为字符串,保证相同参数生成相同字符串;

  • 第二步:对序列化后的字符串做MD5哈希,生成固定长度(32位)的唯一key,避免key过长占用Redis内存;

  • 第三步:增加null值/空参数边界处理,保证异常参数也能生成合法key。

3.缓存管理器配置方案

        基于SpringCache设计缓存管理器,兼顾通用性与定制化:

  • 基础配置:key序列化使用StringRedisSerializer(保证key可读),value序列化先简化为字符串(规避反序列化类信息丢失问题);

  • 差异化配置:默认缓存过期时间+精选应用缓存区域专属过期时间,适配不同场景需求;

4.最终方案

        综合场景特点与技术适配性,确定最终方案:

  • 核心模式:旁路缓存+前10页数据缓存;

  • key生成:JSON序列化+MD5哈希+边界处理

  • 配置体系:自定义CacheManager+注解式缓存;

  • 过期策略:设置合理过期时间,无需主动删除缓存,自然失效保证数据一致性。

三、开发实现

1)环境与依赖确认

        依赖检查:因前期引入spring-session-data-redis,已自动依赖spring-data-redis,无需额外添加Maven/Gradle依赖:

spring:
  data:
    redis:
      host: localhost
      port: 6379
      database: 0
      password:
      ttl: 3600  # 缓存过期时间(秒)

2)缓存工具类开发

        为保证缓存key生成的一致性与唯一性,定制缓存工具类(CacheKeyUtils),核心设计要点:

  1. 静态方法封装:提供generateKey(Object...params)方法,支持多参数传入生成唯一key;

  2. 序列化处理:使用Hutool的JSONUtil序列化参数,处理null值为空字符串,保证序列化结果统一;

  3. 哈希处理:通过MD5算法对序列化字符串哈希,生成固定长度key;

  4. 异常处理:捕获序列化过程中的异常,返回默认key前缀+异常标识,避免工具类抛出异常导致接口降级。

/**
 * 缓存 key 生成工具类
 *
 * @author yupi
 */
public class CacheKeyUtils {
    /**
     * 根据对象生成缓存key (JSON + MD5)
     *
     * @param obj 要生成key的对象
     * @return MD5哈希后的缓存key
     */
    public static String generateKey(Object obj) {
        if (obj == null) {
            return DigestUtil.md5Hex("null");
        }
        // 先转JSON,再MD5
        String jsonStr = JSONUtil.toJsonStr(obj);
        return DigestUtil.md5Hex(jsonStr);
    }
}

3)启用缓存

        启用缓存功能:在SpringBoot启动类添加@EnableCaching注解,开启Spring Cache支持:

@EnableCaching
@SpringBootApplication
public class YuAiCodeMotherApplication {
    public static void main(String[] args) {
        SpringApplication.run(YuAiCodeMotherApplication.class, args);
    }
}

4)配置缓存管理器

        必须配置Redis缓存管理器CacheManager,这是Spring Cache的核心组件。

        如果不配置的话,使用缓存注解时可能会报错:

        在config包下新增代码,定制CacheManager:

@Configuration
public class RedisCacheManagerConfig {
    @Resource
    private RedisConnectionFactory redisConnectionFactory;
    @Bean
    public CacheManager cacheManager() {
        // 配置 ObjectMapper 支持 Java8 时间类型
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        
        // 默认配置
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(30)) // 默认 30 分钟过期
                .disableCachingNullValues() // 禁用 null 值缓存
                // key 使用 String 序列化器
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                // value 使用 JSON 序列化器(支持复杂对象)
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)));
        
        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(defaultConfig)
                // 针对 good_app_page 配置5分钟过期
                .withCacheConfiguration("good_app_page",
                        defaultConfig.entryTtl(Duration.ofMinutes(5)))
                .build();
    }
}

        这个配置的几个关键点:

        1.序列化器选择:StringRedisSerializer用于序列化key,确保Redis中的key是可读的字符串;GenericJackson2JsonRedisSerializer用于序列化value,支持复杂对象的序列化和反序列化。

        2.时间类型支持:注册JavaTimeModule来支持Java8时间类型LocalDateTime

        3.差异化配置:既提供了默认配置,又为特定的缓存区域设置不同的过期时间

        但是要注意,如果对value进行JSON序列化,可能会出现无法反序列化的情况,因为Redis中并没有存储Java类的信息,不知道要反序列化成哪个类,就会报错。所以我们可以先注释掉这些代码:

// value 使用 JSON 序列化器(支持复杂对象)
// .serializeValuesWith(RedisSerializationContext.SerializationPair
//     .fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)));

5)接口缓存注解应用

        在AppController中为精选应用列表接口添加缓存注解:

@PostMapping("/good/list/page/vo")
@Cacheable(
        value = "good_app_page",
        key = "T(com.yupi.yuaicodemother.utils.CacheKeyUtils).generateKey(#appQueryRequest)",
        condition = "#appQueryRequest.pageNum <= 10"
)
public BaseResponse<Page<AppVO>> listGoodAppVOByPage(@RequestBody AppQueryRequest appQueryRequest) {
    // 方法实现保持不变...
}

        核心注解说明:

  • value:指定缓存区域,对应缓存管理器中定制的过期时间;

  • key:通过SpEL调用静态方法生成唯一缓存key;

  • condition:缓存条件,仅前10页数据缓存,符合用户浏览习惯,减少无效缓存。

四、测试验证

        通过Redis客户端(Redis Desktop Manager)查看缓存数据的效果。

        每个页面的缓存都有独立的key:

        缓存的内容以JSON格式存储,便于阅读和调试

五、开发过程中的问题与解决方案

问题:Redis反序列化失败

现象:配置GenericJackson2JsonRedisSerializer序列化value后,接口返回时出现“无法反序列化为指定类”异常。

原因:JSON序列化未存储Java类的全限定名,Redis中仅保存纯JSON字符串,反序列化时无法确定目标类类型。

解决方案

        临时方案:简化value序列化方式为StringRedisSerializer,直接存储JSON字符串,接口返回时自行反序列化;

        长期方案:定制GenericJackson2JsonRedisSerializer,注册Jackson2ObjectMapperBuilder并开启enableDefaultTyping,让序列化结果包含类信息,解决反序列化类匹配问题。

六、实训收获与技术成长

        本次Redis缓存优化开发,核心收获体现在:

  1. 掌握缓存架构设计思路,理解不同缓存模式的适用场景,能结合业务特点选择最优方案(如旁路缓存适配读多写少场景);

  2. 深入理解Spring Cache注解与Redis缓存管理器的协同机制,解决了序列化、key生成、过期策略等核心问题;

  3. 具备缓存问题排查能力,能定位反序列化、缓存穿透/失效等常见问题,并设计临时/长期解决方案;

七、后续开发计划

        增加缓存监控:接入Spring Boot Actuator + Prometheus + Grafana,监控缓存命中率、过期率、内存占用等指标,及时发现缓存异常;

        扩展缓存场景:将缓存策略复用至其他读多写少接口(如应用详情、分类列表),统一缓存工具类与配置体系;

        优化缓存一致性:针对精选应用更新场景,实现“更新数据库+主动删除缓存”逻辑,避免缓存过期前的数据不一致问题。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐