缓存逐出多个键之一

Gle*_*hil 6 java spring caching spring-el evict

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

@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}")
Run Code Online (Sandbox Code Playgroud)

现在我想要一个 cachevict 方法来清除所有缓存,其中只有#projectId 键(UUID)匹配:

@CacheEvict(value = {"valueCodes", "fieldCodes", "qi"; "fieldCodes"}, key = "#projectId")
Run Code Online (Sandbox Code Playgroud)

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

只有 evict 注释的键正则表达式匹配每个 cacheNames 中的多个元素

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

所以我开始考虑将我的密钥合并为一个密钥:

@Cacheable(cacheNames="cahceName", key="concat(#projectId).concat(#otherKey)")
Run Code Online (Sandbox Code Playgroud)

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

我正在努力实现的可能吗?如果是这样,我该怎么做?

cru*_*tex 8

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

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

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

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

有关此内容的更多信息,请参阅问题:对于多级进程内缓存来说,更好的选择是什么?

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


Gle*_*hil 4

我没有使用注释通过部分键来查找键,而是创建了一个为我管理键的 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);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 不要使用多个键,而是将不同的键连接成一个字符串

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

    cacheEvictionService.addKeyToList(StringUtils.join(value, fieldId, projectId));
    
    Run Code Online (Sandbox Code Playgroud)
  5. 循环包含项目 id(或任何其他键)的每个现有键

    for (String cachedKey : cacheEvictionService.findKeyByPartialKey(projectId)) {
        cacheEvictionService.evict(cachedKey);
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 这是超级危险的。对于一个 LinkedHashSet 来说,它不是一个并发集合,因此您需要对其进行同步。其次,即使您这样做,也可能会出现奇怪的边缘情况,即连续意外重复的键。 (7认同)
  • 这不会驱逐缓存在其他应用程序节点上的条目。 (5认同)