两级缓存(Redis + Caffeine)

siw*_*wel 5 java spring caching redis spring-boot

在对应用程序进行分析时,我们发现 Redis 正在影响执行时间,因为线程中有很多睡眠。我需要实现两级缓存或考虑解决这个问题。

我想要两级缓存

  • L1 - 每个部署实例的本地,
  • L2 - 同一部署的所有实例的全局缓存,

我想出的解决方案是

  • 创建两个CacheManager(CaffeineCacheManager和RedisCacheManager),
  • 为每个缓存管理器初始化相同的缓存,
  • 使用注解@Caching和cacheable={}来使用两个缓存,

    @Caching(cacheable = {
            @Cacheable(cacheNames = CacheConfiguration.HELLO_WORLD),
            @Cacheable(cacheNames = CacheConfiguration.HELLO_WORLD, cacheManager = "cacheManagerRedis")
    })
    public String generate(String name)
    {
        log.info("  Cached method call...");
        return helloWorldService.generate(name);
    }

Run Code Online (Sandbox Code Playgroud)

类的结构类似于:CachedService(此处注释)-> NonCachedService

我面临的问题

我想让它正常工作(是的 - 有效/n - 不工作):

  • [ y ] 数据被获取,然后缓存到 Redis 和本地缓存 - 这有效
  • [ y ] 如果数据存在于本地缓存中,则不要将其移动到 redis - 这有效
  • [ y ] 如果任何缓存包含数据,将从缓存中获取数据
  • [ n ] 如果数据存在于 Redis 中,则将其移动到本地 - 这不起作用

修改 @Caching 注释以将值放入本地缓存中的put={}会使整个缓存无法工作。


    @Caching(cacheable = {
            @Cacheable(cacheNames = CacheConfiguration.HELLO_WORLD),
            @Cacheable(cacheNames = CacheConfiguration.HELLO_WORLD, cacheManager = "cacheManagerRedis")
    }, put = {
            @CachePut(cacheNames = CacheConfiguration.HELLO_WORLD),
    })
    public String generate(String name)
    {
        log.info("  Cached method call...");
        return helloWorldService.generate(name);
    }

Run Code Online (Sandbox Code Playgroud)
  • 您知道任何适用于两级缓存的春季就绪解决方案吗?
  • 我读过有关 Redis 本地缓存的内容,但这并不意味着与我的情况类似(这只是标准的 redis 用例),
  • 我只剩下双层服务结构来实现这个目标吗?类似于CachedLocal -> CachedRedis -> NonCached

msl*_*sap 2

对于其他正在寻找此功能的人,我能够实现 Ankit 建议的 CacheInterceptor。

例子:

 public class RedisMultiCacheInterceptor extends CacheInterceptor {

    @Autowired
    private CacheManager caffeineCacheManager;

    @Override
    protected Cache.ValueWrapper doGet(Cache cache, Object key) {
        //Get item from cache
        var superGetResult = super.doGet(cache, key);

        if (superGetResult == null) {
            return superGetResult;
        }

        //If retrieved from Redis, check if it's missing from caffeine on local and add it
        if (cache.getClass() == RedisCache.class) {
            var caffeineCache = caffeineCacheManager.getCache(cache.getName());

            if (caffeineCache != null) {
                caffeineCache.putIfAbsent(key, superGetResult.get());
            }
        }

        return superGetResult;
    }
}
Run Code Online (Sandbox Code Playgroud)