You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Ehcache仅同请求内生效,页面刷新重复查询DB求助

缓存失效问题排查与解决方案

我来帮你梳理下这个缓存不命中的问题,结合你的配置和测试情况,咱们一步步拆解分析:

核心问题复盘

  • 同一次请求内重复调用服务方法能命中缓存,但页面刷新(新请求)时首次调用必查DB
  • 单元测试原本跨用例能命中缓存,但给对象实现hashCode()和equals()后,反而每次都触发DB查询

关键排查方向

1. 缓存Key的生成逻辑是核心痛点

Spring缓存的命中完全依赖Key的一致性,而你用{#options, #pageable}作为Key,这完全取决于这两个对象的equals()和hashCode()实现是否正确。

排查点:

  • 对象属性是否全量参与判断:确保PhoneOption和MyPage的equals()/hashCode()只包含与查询条件相关的核心属性:
    • 比如PhoneOption只需要比较name、status这些查询用的字段,无关属性别加进去
    • MyPage只需要比较page、size这两个分页参数,不要包含totalRows(这个是方法返回后赋值的,入参里并没有)
  • null安全的比较逻辑:一定要用Objects.equals()来比较属性,避免null值导致的判断错误。比如:
    // PhoneOption的正确equals示例
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PhoneOption that = (PhoneOption) o;
        return Objects.equals(status, that.status) && Objects.equals(name, that.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(status, name);
    }
    
  • 临时打印Key验证:在服务方法开头加日志,对比跨请求、单元测试中的Key哈希值是否一致:
    System.out.println("Combined Key Hash: " + Arrays.asList(options, pageable).hashCode());
    

2. 简化缓存Key,绕开对象相等性问题

如果怀疑对象的equals()/hashCode()有问题,可以先把Key改成具体的属性组合,直接用参数值生成Key,这样更可靠:

@Cacheable(value = "myPageCache", key = "{#options.name, #options.status, #pageable.page, #pageable.size}")
public MyPage<PhoneInfo>findAllByOption(PhoneOption options, MyPage<PhoneInfo> pageable){
    // ... 原有逻辑
}

这种方式不需要依赖对象的相等性判断,直接用查询的核心参数生成Key,能快速验证缓存是否能正常跨请求命中。

3. 单元测试的反向问题分析

你提到实现equals()/hashCode()后,单元测试反而每次都查DB,这说明:

  • 之前未实现这两个方法时,Spring用的是对象的内存地址生成Key,但你的单元测试可能因为某些原因(比如对象复用、测试框架的特殊处理)导致Key一致
  • 实现方法后,反而因为属性判断逻辑有误,导致每次生成的Key不同

建议检查:

  • MyPage的equals()是否误判了totalRows或其他非入参属性
  • PhoneOption的属性是否有默认值不一致的情况(比如包装类null和0的区别)

4. 缓存配置与生命周期验证

你的Ehcache配置看起来没问题,但可以做几个小验证:

  • 检查timeToLiveSeconds和timeToIdleSeconds设置为86400,缓存不会快速过期
  • 开启Spring缓存调试日志,看缓存命中的详细过程:
    在日志配置中添加:
    <logger name="org.springframework.cache" level="DEBUG"/>
    <logger name="net.sf.ehcache" level="DEBUG"/>
    
    日志会显示每次请求的Key、是否命中缓存、缓存写入的情况,能快速定位问题。

快速修复步骤

  1. 先把findAllByOption的缓存Key改成具体属性组合,验证跨请求是否能命中缓存
  2. 如果能命中,再回头调试PhoneOption和MyPage的equals()/hashCode()实现,确保只包含核心查询属性
  3. 调试单元测试中的对象属性,确认每次传入的参数属性完全一致

内容的提问来源于stack exchange,提问作者Philip YW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:49:51