cczf 2024-03-28 15:58 采纳率: 0%
浏览 22

关于SpringBoot使用caffeine缓存数据错乱的问题

我在使用caffeine缓存时遇到这样一个问题:

类B的方法b使用了Spring Cache的注解@Cacheable,调用第三方接口获取信息并缓存,返回BVO;
类A的方法a也使用了Spring Cache的注解@Cacheable,其中有调用方法b组合返回体;
类C的方法c直接调用方法b组合返回体。
类A和类C的方法分属于两个不同的请求接口:接口1、接口2。

现象:

调用接口1时,a方法获取id为1的BVO,正常地返回id为1的BVO信息,后续多次调用也使用了缓存中的信息;
之后调用接口2时,c方法获取id为2的BVO,也正常地返回id为2的BVO信息;
之后再调用接口1时,a方法获取id为1的BVO,却返回了缓存中id为2的BVO。
我确定方法a和方法b的@Cacheable使用的value和key都是不一样的,并且A和C调用b方法时作为缓存key值的传参一定是不一样的,去掉b方法的缓存一切正常,有人知道这是什么情况吗?

大致的逻辑如下:

class B  {
  @Cacheable(value = 'nameB', key = '#bId')
  public BVO methodB(Long bId) {
    return invokeOther(bId);
  }
}

class A {
  @Autowired
  private B bInstance;
  @Cacheable(value = 'nameA', key = '#A.id')
  public AVO methodA(ADO a) {
    AVO avo = new AVO();
    ...
    avo .setBVO(bInstance.methodB(a.getBId()));
    return avo;
  }
}

class C {
  @Autowired
  private B bInstance;

  public CVO methodC(CDO c) {
    CVO cvo = new CVO();
    ...
    cvo .setBVO(bInstance.methodB(c.getBId()));
    return cvo;
  }
}
  • 写回答

2条回答 默认 最新

  • 专家-皆知 2024-03-28 15:58
    关注

    引自免费微信小程序:皆我百晓生

    在Java中,由于Spring Boot和Caffeine之间存在依赖关系,它们的缓存机制可能会影响到其他Spring项目中对象的性能。为了了解具体问题,我们需要明确以下几个步骤:

    1. 理解Spring Boot的缓存机制: Spring Boot应用通常会在启动时自动配置Spring的缓存系统,包括其默认的无竞争死锁缓存池(ConcurrentHashMap)和失效策略。这两种缓存机制可能会对内存资源造成影响,尤其是当多个实例共享同一个缓存池时,如果其他线程频繁访问同一份数据,可能会导致缓存的容量不足或过载。

    2. 确认@Cacheable注解正确性: @Cacheable注解用于标记一个方法或类的属性,它告诉Spring Cache管理这些属性的缓存策略。注解指定了一个数据类型(value)和一个哈希函数(key),用于将数据映射到持久化存储(如Redis、Memcached等)。在这个例子中,@Cacheable(value = 'nameB', key = '#bId')表示MethodB方法在全局作用域内查找value为"nameB"的value,并通过key值"#bId"对应ID,这个行为类似于使用Caffeine缓存特定元素。

    3. 理解@Cacheable如何影响ClassB和ClassA的缓存获取: 如果我们考虑到实际情况,可以考虑以下几种情况可能导致上述问题:

      a. 类B使用Caffeine缓存同时使用了Spring Cache注解,此时两个缓存之间的关联和冲突是存在的。例如,由于MethodB是Value为"nameB"的value,而在CacheB内部找到了这个value,就等于在同一时间内访问了缓存,这样就会导致多个instance(B和C)尝试从缓存中获取同一个Element对象,导致数据异常。

      b. 类A使用Caffeine缓存但未使用Spring Cache注解,这时在CacheB内部查找到值为"nameB"的value,自然不会与某个实际的对象匹配,从而解决了冲突问题。然而,这会导致一定程度的性能问题,因为单个请求的执行时间会增加。

      c. 类B和类A都使用了Caffeine缓存,但在Caffeine内部实现了不同类型的引用,比如B使用了Memcached作为持久化存储,而A使用了Redis。在这种情况下,Caffeine虽然能在内部处理并发访问问题,但由于同一内存块上的多条数据相互影响,当它们发生多次频繁读取缓存时,可能会产生数据不可达异常。

    4. 针对不同的场景提供解决方案:

      • 对于ClassB的情况,可以考虑以下方案:

        a. 首先,建议替换Caffeine在缓存中保存的数据类型为{"data": "nameB", "hashKey": "#bId"},这样每条Value只能对应唯一的key值。此外,可以在ClassB自身实现对应的脏读检测,当对象被频繁更新且没有重写getBVO()方法时,避免访问缓存,以免引发内存碎片、竞态条件等问题。

        b. 对于ClassA的情况,可以通过设置缓存的时间戳字段,使得用户需要手动刷新缓存时才返回最新的Data,而不是每次都查询同一部分的数据。这样既可以保证结果的一致性,也能减少不必要的同步操作,提升系统的整体性能。

        c. 若A使用的是分布式缓存系统(如Memcached或Redis),可能需要考虑在内存分配上进行优化。对于大规模的数据,合理规划内存的分配策略,确保其中一部分(通常是热点数据)的缓存容量充足,而大部分的Cache则处于最小负载状态,避免因长期未更新而导致的内存溢出和性能下降。

    5. 代码示例:

      // 在BaseBean类中配置@EnableCaching注解,启用Caffeine缓存
      @EnableCaching(cacheNames = {"NameCache", "ValueCache"})
      public class BaseBean {
      
         // ... Your base logic ...
      
         @Cacheable(value = "ValueCache", key = "#value")
         public String getValue(String value) {
            // Implement caching logic based on the requested value and key
            if (value == "nameB") {
               return invokeOther(value);
            } else {
               throw new RuntimeException("Invalid cache entry");
            }
         }
      
         // ... Other methods in your A, B, or C classes that use @Cacheable ...
      
         private String invokeOther(String value) {
            // ... Implement logic to retrieve data from another service or repository using a cached value
            return "Retrieved from Service或者其他Service";
         }
      }
      
    6. 绘制图形模拟: 使用Eclipse IDE,选择你的项目结构,然后右键点击你要关注的问题,选择“Open Perspective”,在弹出的新窗口中选择“Java”,然后选择要绘制的类和方法。接下来,在命令行中输入以下命令以绘制一个简单的动画:

      java -cp target/classes target/YourProjectName/spring-boot-starter-cache.gif yourApplicationClass你的ServiceClass
      

      这段命令会创建一个新的JFrame,其中包括一个由Spring Boot运行时引擎在内存中创建的对象表示你提到的类。对于@Cacheable注解的操作,它会在适当的位置绘制相应的依赖。例如,如果B方法使用Caffeine缓存并使用了一个Value为"nameB"的value,那么B方法将在文档页左侧绘制出一个资源图片,而其他的A、C类方法将会显示为空。当Spring Boot执行完第一个HTTP请求时,场景中的所有元素都会加载并开始运动,最终达到一种典型的缓存内容错乱效果。

    评论 编辑记录

报告相同问题?

问题事件

  • 修改了问题 3月28日
  • 修改了问题 3月28日
  • 创建了问题 3月28日

悬赏问题

  • ¥15 (标签-考研|关键词-set)
  • ¥15 求修改代码,图书管理系统
  • ¥15 请问有没求偏多标签数据集yeast,reference,recreation,scene,health数据集。
  • ¥15 传感网应用开发单片机实训
  • ¥15 Delphi 关于sAlphaImageList使用问题
  • ¥15 寻找将CAJ格式文档转txt文本的方案
  • ¥15 shein测试开发会问些啥我是写java的
  • ¥15 关于#单片机#的问题:我有个课程项目设计,我想在STM32F103veTX单片机,M3主控模块上设计一个程序,在Keil uVision5(C语言)上代码该怎么编译?(嫌钱少我可以加钱,急急急)
  • ¥15 opnet仿真网络协议遇到问题
  • ¥15 在安装python的机器学习程序包scikit-learn(1.1版本)时遇到如下问题