【问题标题】:What's the role of spring boot cache annotations in a application which use Redis for caching?Spring Boot 缓存注解在使用 Redis 进行缓存的应用程序中的作用是什么?
【发布时间】:2021-07-04 07:21:18
【问题描述】:

我在我的 Spring Boot 应用程序中使用 Redis 作为内存数据存储,用于缓存目的。目前,我已经为需要缓存的实体实现了具有基本 CRUD 功能的 Redis 支持 [场景 1]。但突然间我发现有很多资源使用额外的 Spring Boot 缓存注释,如 @Cachable @CahceEvict 来实现 Redis [场景 2] 的缓存。我监控到,当我们开始在诸如 find(params) 之类的操作中使用这些注释时,只有第一个方法调用会转到 Redis。从第二种方法开始,Redis 不会受到攻击。所以根据我的观察,我认为,Spring boot 维护了一个单独的缓存。但我的问题是我们已经在使用 Redis 作为我们的缓存。那么停止第二次 Redis 数据存储命中并维护另一个缓存有什么好处。我的意思是 Redis 已经在 RAM 中并且它具有很强的缓存能力。为什么我们需要维护两个缓存?拥有这种机制有什么好处吗?或者只实现 Redis 就足够了?

场景 1:

public class RestController{

@GetMapping("/{id}")
public Product findProductById(@PathVariable int Id){
           return dao.findProductById(id);
}


}
@Repository
public class ProductDao {

    public static final String HASH_KEY = "Product";

    @Autowired
    private RedisTemplate template;
  

    public Product findProductById(int id){
        return (Product) template.opsForHash().get(HASH_KEY,id);
    }

}

场景 2:

public class RestController{

@GetMapping("/{id}")
@Cachable(key = "#id" ,value="Product")
public Product findProductById(@PathVariable int Id){
           return dao.findProductById(id);
}

}
@Repository
public class ProductDao {

    public static final String HASH_KEY = "Product";

    @Autowired
    private RedisTemplate template;
  

    public Product findProductById(int id){
        System.out.println("called findProductById() from DB");//Here only for the first time method will be called
        return (Product) template.opsForHash().get(HASH_KEY,id);
    }

}

References for Scenario 1 implementation

References for Scenario 2 implementation

【问题讨论】:

  • 假设代码直接处理缓存 - 这将要求缓存中涉及的每个方法都知道,并使用一些(希望注入的)依赖项。这是可能的,但噪音很大,可能至少有一些重复的代码。每个单元测试都需要额外的设置,这不提供商业价值(也许这样的测试没有功能价值)。注释抽象出缓存实现,因此一旦配置了缓存提供程序,您就完成了。但请考虑添加一些集成测试来验证缓存是否正常工作。
  • @Andrew S 实际上,我不确定您是否正确理解了我的问题。我的意思是认为您正在实现您的 redis 存储库(数据访问层)并且您正在调用一个示例方法调用 findById(params) ,我在视频中看到当您使用 Cachable 注释时,只有第一次数据将从实际的 redis 中检索数据存储。但是从第二次调用实际上 findById(params) 不会被触发。因为spring会将值缓存在其他地方并交出。所以我问为什么我们需要这样的机制。因为我们已经在使用 Redis 作为缓存了。
  • 请查看参考资料。

标签: java spring caching redis spring-data-redis


【解决方案1】:

Spring Cache abstraction 提供了一个缓存抽象层,您可以对其进行配置以支持不同的、可插入的底层缓存机制,例如Redis through Spring Data 等。

您正在使用 Redis 作为实际的持久性机制。因此,您可能不需要任何缓存,并且由于 Redis 数据库的内存特性,场景 1 可能是合适的。

话虽如此,请考虑不同的观点。

让我们考虑一个后端,它使用持久层和底层非内存数据库、关系或其他类型的 NoSQL。

这是 Spring Cache、@Cacheable 和其他相关注释的完美用例,可有效缓存您的结果。

还有,Redis 如何解决这个难题?因为你configure Spring 使用 Redis 作为实际的cache manager。例如:

@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
    return RedisCacheManager.create(connectionFactory);
}

在后台RedisCacheManager 将提供必要的机制,以根据应用程序缓存的要求透明地从 Redis 写入和读取。

有了这种新的观点,具有上述差异的方案 2 是可供选择的方案,实际上是您在构建企业应用程序时通常会发现的常见方案。

无论如何,您问题中描述的场景2,我的意思是,除了Spring Cache之外,直接使用Redis作为持久性机制,如果您尝试也可以适用:

  • 减少对 Redis 执行的请求数量和相关成本。如果您在云提供商中使用 Redis(或类似 Redis 的服务,例如 GCP Memorystore),这可能很重要。
  • 尽管 Redis 足够快,但您可以使用缓存将结果存储在运行应用程序的计算机的本地 RAM 中,以提高性能,就像使用任何其他数据库系统一样。
  • 或者因为您需要以某种特定方式与 Redis 交互。

如果您仅将 Redis 用于缓存并且不需要任何特定的东西,那么最好通过 Spring Cache 抽象来使用它而不是手动执行缓存操作:它不会提供任何性能优势,但您会获得多个优势,例如结构良好的缓存框架、一组有用的注释以及可移植性/可用性,因为您可以仅在需要时使用不同的配置来切换缓存实现,例如用于测试或本地开发。

【讨论】:

  • 我 100% 同意在这样的场景中使用它,例如在使用 HDD 的存储中缓存数据。但我要问的是根据这种场景,我们的优势是什么可以通过遵循这种方法获得。我的意思是我已经在互联网上的许多其他地方看到了使用 Spring 缓存注释实现 Redis 的第二种情况。那么为什么他们都在互联网上分享这些不正确的混淆内容。我的意思是必须是一个原因。
  • 我的意思是,至少这些注释的实现会驱动应用程序将这些数据缓存在 L1、L2 或 L3 等存储中。因为那样的话性能肯定会提高。所以根据你对第二个方法调用的解释,即使我们没有看到任何 Redis 数据库命中实际上该方法正在调用。我的意思是如果这些注释也使用 Redis 作为缓存然后绝对应该像往常一样从 Redis 获取数据。
  • @HasinduDahanayake 我尝试在答案中提供一些额外的想法。但是,是的,这就是重点,在后台 RedisCacheManager 将提供必要的机制,以根据缓存的要求透明地从 Redis 写入和读取。
  • 所以只要把事情弄清楚,所以根据你的说法,使用 Spring 引导缓存抽象实现 Redis 是正确的调用。我的意思是根据你的解释,正如我之前问过的,当涉及到性能时,这两种情况都是相同。我的意思是,即使我们看到 Redis 在第二种情况下的第二次方法调用后可能不会被命中,它实际上是被命中的。(我引用的是 findProductById() 方法)。所以在第二种情况下也只有一个缓存可用的存储是 Redis。
  • 我的意思是 Spring Boot 不会将需要从第二个方法调用返回的数据存储在单独的缓存存储或其他任何地方,如 L1、L2 或 L3 内存。它是相同的存储这是Redis。所以我是正确的?
猜你喜欢
  • 2021-04-21
  • 1970-01-01
  • 2020-09-24
  • 2020-04-23
  • 1970-01-01
  • 2016-08-24
  • 1970-01-01
  • 2022-01-17
  • 1970-01-01
相关资源
最近更新 更多