当前位置:   article > 正文

【后端-SpringCache】基于Spring Cache 封装支持Redis缓存批量操作的方式_记录下踩坑历程(pipeline或mget封装)_cachemanager.class 批量保存缓存

cachemanager.class 批量保存缓存

Spring Cache是一个非常优秀的缓存组件,我们的应用系统正是使用的Spring Cache。但最近在优化应用系统缓存的过程中意外发现了Spring Cache的很多坑点,特意记录一下。

背景

应用系统中存在部分接口循环调用redis获取缓存的场景(例如:通过多个 userId 来批量获取用户信息),此时我们的代码是类似于这样的(仅示例):

List<User> users = ids.stream().map(id -> getUserById(id)).collect(Collectors.toList());

@Cacheable(key = "#p0", unless = "#result == null")
public User getUserById(Long id) {
 // ···
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

就像上面说的,这种写法的缺点在于:在循环中操作 Redis 。如果数据都命中缓存还好,但大量频繁的连接也会产生一定的影响降低 qps ,再说一旦缓存没有命中,则会访问数据库,效率也可想而知了。

期望达到的效果

理想的逻辑是优先去 redis 获取缓存,redis 查询结束后筛选出来缓存中不存在的去批量查库,最后再将查库得到的结果存入redis。同时整个过程中应该控制减少请求 redis 的次数。

解决过程

通常做法

面向百度编程后发现有些同学可能会这样做:

@Cacheable(key = "#ids.hash")
public Collection<User> getUsersByIds(Collection<Long> ids) {
    // ···
}
  • 1
  • 2
  • 3
  • 4

这种做法的问题是:

缓存是基于 id 列表的 hashcode ,只有在 id 列表的 hashcode 值相等的情况下,缓存才会命中。而且,一旦列表中的其中一个数据被修改,整个列表缓存都要被清除。

例如:

第一次请求 id 列表是 1,2,3

第二次请求的 id 列表为 1,2,4

在这种情况下,前后两次的缓存不能共享。 如果 id 为 1 的数据发生了改变,那么,这两次请求的缓存都要被清空

看看 Spring 官方是怎么说的

Spring Issue:

  • https://github.com/spring-projects/spring-framework/issues/24139
  • https://github.com/spring-projects/spring-framework/issues/23221

image-20211231171845912

简单翻译一下,具体内容大家可以自行查阅相关 issue

译文:

谢谢你的报告。缓存抽象没有这种状态的概念,如果你返回一个集合,这实际上是您要求存储在缓存中的内容。也没有什么强迫您为给定的缓存保留相同的项类型,所以这种假设并不适合这样的高级抽象。

我的理解是,对于 Spring Cache 这种高级抽象框架来说,Cache 是基于方法的,如果方法返回 Collection,那整个 Collection 就是需要被缓存的内容。

我的解决方案

纠结一段时间后,我决定自己来造个轮子

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/745013
推荐阅读
相关标签