【问题标题】:Reload/Refresh cache in spring boot在 Spring Boot 中重新加载/刷新缓存
【发布时间】:2019-10-20 20:40:23
【问题描述】:

我正在使用 Spring Boot,而对于缓存,我正在使用 Ehcache。到目前为止它工作正常。但是现在我必须重新加载/刷新,所以我该怎么做才能让我的应用程序不会有任何停机时间。

我在 Spring Ehcache 中尝试了很多方法,但都不起作用,否则必须编写调度程序并重新加载数据。

@Override
@Cacheable(value="partTypeCache", key="#partKey")
public List<PartType> loadPartType(String partKey) throws CustomException {
        return productIdentityDao.loadPartType();
}

【问题讨论】:

  • @ItFreak CacheEvict 用于删除/清除缓存,但在我的情况下,我必须重新加载/刷新缓存,以便它与任何数据库同步。
  • 我不认为有一个开箱即用的解决方案,因为这意味着框架知道所有底层数据源。
  • CacheEvict 将从缓存中删除值。我想在固定时间间隔后重新加载孔缓存而不会出现问题

标签: java spring-boot caching memcached ehcache


【解决方案1】:

显然所有关于您的问题的 cmets 都是正确的。您应该使用 CacheEvict。我在这里找到了解决方案:https://www.baeldung.com/spring-boot-evict-cache,它看起来像这样:

您所要做的就是创建一个名为 e.g. 的类。 CacheService 和 create 方法将驱逐您拥有的所有缓存对象。然后你注释那个方法@Scheduled 并输入你的间隔率。

@Service
public class CacheService {

    @Autowired
    CacheManager cacheManager;

    public void evictAllCaches() {
        cacheManager.getCacheNames().stream()
          .forEach(cacheName -> cacheManager.getCache(cacheName).clear());
    }

    @Scheduled(fixedRate = 6000)
    public void evictAllcachesAtIntervals() {
        evictAllCaches();
    }

}

【讨论】:

  • 我想对 cacheservice.evictAllcaches 进行单元测试。我观察到 cacheManager.getCacheNames() 在清除缓存后返回相同的缓存值。知道如何测试空缓存场景。
【解决方案2】:

尝试这样的事情,正如 cmets 中提到的那样:

    @Caching(evict={@CacheEvict(value="partTypeCache", key="#partKey")})
    public boolean deletePartType(String partKey) { 
      //when this method is invoked the cache is evicted for the requested key
    }

【讨论】:

  • 感谢 response.CacheEvict 将清除缓存,但我必须刷新缓存以便它与数据库同步
  • @Cacheable 执行时,在缓存中找不到数据,会自动刷新/重新填充缓存。
  • 但我知道@Cacheable 用于在缓存中加载数据,这工作正常,但假设从不同的来源添加新值,如 ETLDifferent Application。那么在这种情况下,我需要重新加载缓存。
  • @Cacheable 调用也会更新缓存。但是,您也可以使用@CachePut 来更新缓存参考:concretepage.com/spring/…。现在,当有更新从任何源缓存时,需要调用带有注释 @CachePut 的方法。我假设您有消息/链接/触发器/句柄来从您的不同应用程序更新缓存
  • 没有那些触发器没有被处理,因为有各种 SYNC 进程直接将数据转储到数据库中。春天有什么方法可以直接将孔数据/新添加的数据重新加载到缓存中。一种方法是编写调度程序并定期执行它。你有更好的建议吗?
【解决方案3】:

许多人建议使用@CacheEvict 的选项是正确的。此外,为了确保您的缓存(附近)始终加载最新数据,即使数据库中的数据更新超出了您正在运行的应用程序的范围,您也需要定期重新加载整个数据集,其间隔与您的应用程序的 SLA 相匹配。在建议的解决方案above 中,添加逻辑以重新加载所有数据,如下所示:

@Service
public class CacheService {

    @Autowired
    CacheManager cacheManager;

    public void refreshAllCaches() {
        cacheManager.getCacheNames().stream()
          .forEach(cacheName -> cacheManager.getCache(cacheName).clear());
        // reload whole dataset here, dummy example here:
        dataRepository.findAll().forEach(a -> cacheManager.getCache("cache-name")).put(a.getKey(), a));
    }

    @Scheduled(fixedRate = 6000)
    public void refreshAllcachesAtIntervals() {
        refreshAllCaches();
    }

}

【讨论】:

    【解决方案4】:

    您知道具体要更新哪些 partKey 吗?如果不这样做,则必须从缓存本身中找到密钥。您可以使用 @CachePut 更新缓存值而无需停机。我的建议的表现不会很好,但它应该完全符合您的要求。

    专门针对 EhCache:

    public void renewCache() {
        net.sf.ehcache.EhCache cache = (net.sf.ehcache.EhCache) org.springframework.cache.CacheManager.getCache("partTypeCache").getNativeCache();
        for (Object key : cache.getKeys()) {
              updatePartTypeCache((String) key)
        }
    }
    
    @CachePut(value="partTypeCache", key="#partKey")
    public List<PartType> updatePartTypeCache(String partKey) throws CustomException {
            return productIdentityDao.loadPartType();
    }
    

    需要明确的是,最糟糕的部分是从现有缓存中获取密钥。如果您想要保留在缓存中的“热”部分的简短列表,则可以为这些部分调用 updatePartTypeCache。

    【讨论】:

      【解决方案5】:

      除了上述答案之外,您还可以在调度程序中使用 cron 来提供更大的灵活性。您可以每天、每周、每年、每天下午 12 点等运行调度程序,而无需编写大量代码。

      @服务 公共类 CacheService {

      @Autowired
      CacheManager cacheManager;
      
      public void evictAllCaches() {
          cacheManager.getCacheNames().stream()
            .forEach(cacheName -> cacheManager.getCache(cacheName).clear());
      }
      
      @Scheduled(cron = "@weekly")
      public void evictAllcachesAtIntervals() {
          evictAllCaches();
      }
      

      }

      【讨论】:

        【解决方案6】:

        要求是在 X 间隔刷新 1 个缓存,在 Y 间隔刷新另一个缓存。如果我们只在 1 个类中编写以下代码,那么缓存不会重新加载。

        
        public class XXXSchedulers {
        
            @Autowired
            private XXXUtil util;
        
            @Scheduled(fixedDelay = 10 * 60 * 1000) // Running after 10 minutes
            public void clearSpecificABCCache() {
                util.clearSpecificABCCache();
                util.getABC();//Again gets value in the cache
            }
        
            @Scheduled(cron = "0 0 2 * * ?") //Running Everyday at 2 AM public void 
            public void clearSpecificXYZCache() {
                util.clearSpecificABCCache();
                util.getXYZ();
            }
        }
        
        @Component
        public class XXXUtil {
            @Autowired
            private CacheManager cacheManager;
        
            @Autowired
            private XXXService service;
        
            @Cacheable("abc")
            public ABC getABC() {
                ABC abc = service.getABC();
            }
        
            public void clearSpecificABCCache() {
                cacheManager.getCache("abc").clear();
            }
        
            @Cacheable("xyz")
            public XYZ getXYZCache() {
                XYZ xyz = service.getXYZCache();
            }
            
            public void clearSpecificXYZCache() {
                cacheManager.getCache("xyz").clear();
            }
          }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-07-25
          • 2014-12-05
          • 2021-01-07
          • 1970-01-01
          • 1970-01-01
          • 2018-06-27
          • 1970-01-01
          • 2023-04-09
          相关资源
          最近更新 更多