【问题标题】:Cache evict on one of multiple keys在多个键之一上缓存驱逐
【发布时间】:2016-12-06 10:04:22
【问题描述】:

在我的应用程序中,我有多个带有多个键的可缓存方法:

@Cacheable(cacheNames = "valueCodes", key = "{#value, #fieldId, #projectId}")
@Cacheable(cacheNames = "fieldNames", key = "{#field, #value, #projectId}")
@Cacheable(cacheNames = "qi", key = "{#langCode, #question, #projectId}")
@Cacheable(cacheNames = "fieldCodes", key = "{#name, #projectId}")

现在我想要一个 cachevict 方法,它可以清除 only #projectId 键(它是一个 UUID)匹配的所有缓存:

@CacheEvict(value = {"valueCodes", "fieldCodes", "qi"; "fieldCodes"}, key = "#projectId")

我在this 文章中读到这是不可能的,而

只有驱逐注释的键正则表达式匹配多个元素 在每个缓存名称中

我不太确定他们的意思,但我想这与在SpEL 中使用正则表达式有关。

所以我开始考虑将我的键连接成一个键:

@Cacheable(cacheNames="cahceName", key="concat(#projectId).concat(#otherKey)")

并使用正则表达式将所有键与 projectId 后跟通配符匹配。但我真的找不到办法做到这一点。

我想要完成的事情可能吗?如果是这样,我该怎么做?

【问题讨论】:

    标签: java spring caching spring-el evict


    【解决方案1】:

    我想要完成的事情可能吗?如果是这样,我该怎么做?

    你喜欢做的事是不可能的。

    一般来说,缓存就像一个哈希表,你只能对一个唯一的键进行操作。选择属于项目 ID 的所有内容将需要缓存中的索引和查询机制。一些缓存有,但不是全部,并且没有通用标准如何做到这一点。

    仔细检查单独缓存属于一个项目的所有点点滴滴是否真的有意义。如果所有东西都需要一起被驱逐,也许它一直都被一起使用。或者,例如,在缓存中保留 ConcurrentHashMap 作为值,该缓存包含属于项目的各种组件。

    更多信息,请参阅问题:What is the better option for a multi-level, in-process cache?

    可能删除注释并直接使用缓存是有意义的。带有注释的选项是有限的。

    【讨论】:

    • 我找到了一种简单的方法来做到这一点,但我需要更多的代码。如果您对解决方案感兴趣,请查看我的解决方案。
    【解决方案2】:

    我没有使用注释通过部分键来查找键,而是创建了一个 bean 来为我管理键

    1. 我删除了所有 @CacheEvict 注释。
    2. 创建了一个新服务来管理我们所有缓存的驱逐

      public interface CacheEvictionService {
          /**
           * Adds the provided key to a global list of keys that we'll need later for eviction
           *
           * @param key the cached key for any entry
           */
          void addKeyToList(String key);
      
          /**
           * Find keys that contain the partial key
           *
           * @param partialKey the cached partial key for an entry
           * @return List of matching keys
           */
          List<String> findKeyByPartialKey(String partialKey);
      
          /**
           * Evicts the cache and key for an entry matching the provided key
           *
           * @param key the key of the entry you want to evict
           */
          void evict(String key);
      
      }
      
      @Service
      public class CacheEvictionServiceImpl implements CacheEvictionService {
          LinkedHashSet<String> cachedKeys = new LinkedHashSet<>();
      
          @Override
          public void addKeyToList(String key) {
              this.cachedKeys.add(key);
          }
      
          @Override
          public List<String> findKeyByPartialKey(String partialKey) {
              List<String> foundKeys = new ArrayList<>();
              for (String cachedKey : this.cachedKeys) {
                  if (cachedKey.contains(partialKey)) {
                      foundKeys.add(cachedKey);
                  }
              }
              return foundKeys;
          }
      
          @Override
          @CacheEvict(value = {"valueCodes", "fieldCodes", "qi", "fieldNames", "fieldsByType"}, key = "#key")
          public void evict(String key) {
              this.cachedKeys.remove(key);
          }
      }
      
    3. 不要使用多个键,而是将不同的键连接成一个字符串

      @Cacheable(cacheNames = "valueCodes", key = "#value.concat(#fieldId).concat(#projectId)")
      
    4. 每次缓存某些内容时将密钥发送给服务

      cacheEvictionService.addKeyToList(StringUtils.join(value, fieldId, projectId));
      
    5. 遍历包含项目 ID(或任何其他键)的每个现有键

      for (String cachedKey : cacheEvictionService.findKeyByPartialKey(projectId)) {
          cacheEvictionService.evict(cachedKey);
      }
      

    【讨论】:

    • 这是超级危险的。对于一个 LinkedHashSet 不是并发集合,因此您需要对其进行同步。其次,即使您这样做了,也可能存在 concat 意外重复键的奇怪边缘情况。
    • 这不会驱逐缓存在其他应用程序节点上的条目。
    猜你喜欢
    • 2016-09-10
    • 2014-10-12
    • 1970-01-01
    • 1970-01-01
    • 2013-01-31
    • 2018-08-20
    • 1970-01-01
    • 2021-03-27
    • 2017-06-17
    相关资源
    最近更新 更多