如何根据特定模式从内存缓存中删除一个或多个键?

问题描述

我有一个方法可以生成我们的缓存密钥,该缓存密钥如下所示:

private static readonly HashSet<string> CacheKeys = new HashSet<string>();

public string Generate(int clientId,int processId)
{
    StringBuilder sb = new StringBuilder();
    sb.Append("data:").Append("c:").Append(clientId).Append("::");
    sb.Append("pi::").Append(processId);

    // do I need to store everything in my CacheKeys?
    // or this can be improved so that I can fullfill below 3 requirements
    // CacheKeys.Add(sb.ToString());
    
    return sb.ToString();
}

然后,此字符串连同其值一起作为键存储在MemoryCache中(这里没有显示,因为它不相关)。

_memoryCache.Set<T>(cacheKey,value,memoryCacheOptions);

我有一个要求,我需要根据以下要求从内存缓存中删除特定的keykeys-

  • 我需要从内存缓存中删除特定的processId + clientId键。
  • 我需要从内存缓存中删除所有与特定clientId密钥匹配的密钥,而不考虑processId
  • 我还需要从内存缓存中删除与某个特定processId密钥匹配的所有密钥,而不管clientId是什么。

现在,在阅读了SO和MemoryCache教程之后,似乎无法迭代所有键(或查找与特定正则表达式模式匹配的键)并将其从缓存中删除

问题陈述

经过各种link之后,看来我需要将缓存键存储在单独的数据结构中,然后使用该键将其删除

  • 如何在上述方法中以这种方式存储cacheKey,以便可以通过迭代此数据结构或任何线程安全的数据结构来轻松地删除基于以上3个要求的关键基础?
  • 输入结构也指定了要删除的键(以下3种模式之后)是来自其他服务,并且也未定义,因此我很愿意以任何格式表示可以帮助我做到这一点有效地完成任务。

基本上,我试图为上述所有三种模式提供一个示例,通过该示例,我可以使用MemoryCacheRemoveByPattern方法有效地从Remove删除密钥。到目前为止,我仍无法弄清楚如何为这三种情况使用RemoveByPatternRemove方法并有效地清除键来准备示例或测试用例。

public void RemoveByPattern(MemoryCache memoryCache,string pattern)
{
    var keysToRemove = CacheKeys
        .Where(k => Regex.IsMatch(k,pattern,RegexOptions.IgnoreCase))
        .ToArray();

    foreach (var ktr in keysToRemove)
        Remove(memoryCache,ktr);
}

public void Remove(MemoryCache memoryCache,string key)
{
    memoryCache.Remove(key);
    CacheKeys.Remove(key);
}

我该如何表达我的输入结构,使其理解我的上述三个要求,然后轻松地将其输入RemoveByPatternRemove方法

解决方法

我认为您的做法正确。目标是使CacheKeys中的键与MemoryCache中的键保持一致。

情况1:添加新密钥时

您需要同时向MemoryCacheCacheKeys添加新创建的密钥。

_memoryCache.Set<T>(cacheKey,value,memoryCacheOptions);
CacheKeys.Add(cacheKey);

情况2:删除现有密钥时

您的实施足够好。

情况3:密钥过期时

当密钥从MemoryCache到期时,密钥集在MemoryCacheCacheKeys之间不一致。您需要实施一个定期在后台运行的计划任务,以检查和清理CacheKeysMemoryCache是事实的来源)。

private void ConsolidateKeys()
{
    var invalidKeys = new List<string>();
    foreach (var key in CacheKeys)
    {
        if (!_memoryCache.TryGetValue(key,out var _))
        {
            invalidKeys.Add(key);
        }
    }
    foreach (var key in invalidKeys)
    {
        CacheKeys.Remove(key);
    }
}

注意

由于这是多线程方案,因此需要考虑访问CacheKeys的线程安全性。关于CacheKeys的数据结构,建议使用ConcurrentDictionary<TKey,TValue>而不是HashSet<T>


更新

要有效地删除键(假设您是指速度),我将以空间换取速度。

这有点复杂,但是您可以定义一组名为processId的{​​{1}}。密钥为ProcessIdMap。生成缓存键时,该值为一组processId,具有相同的clientIdprocessId与此类似。 (伪代码)

ClientIdMap

情况1:添加新密钥时

ProcessIdMap = Map<processId,Set<clientId>>
ClientIdMap = Map<clientId,Set<cacheKey>>

情况2:删除现有密钥时private static readonly HashSet<string> CacheKeys = new HashSet<string>(); private static readonly Dictionary<string,HashSet<string>> ProcessIdMap = new Dictionary<string,HashSet<string>>(); private static readonly Dictionary<string,HashSet<string>> ClientIdMap = new Dictionary<string,HashSet<string>>(); private string BuildCacheKey(int clientId,int processId) { var sb = new StringBuilder(); sb.Append("data:").Append("c:").Append(clientId).Append("::"); sb.Append("pi::").Append(processId); return sb.ToString(); } public string AddToCache(int clientId,int processId) { // Populate processId map if (!ProcessIdMap.TryGetValue(processId,out var clientIdSet)) { clientIdSet = new HashSet<string>(); } clientIdSet.Add(clientId); // Populate clientId map if (!ClientIdMap.TryGetValue(clientId,out var processIdSet)) { processIdSet = new HashSet<string>(); } processIdSet.Add(processId); var cacheKey = BuildCacheKey(clientId,processId); // Populate cache key store CacheKeys.Add(cacheKey); // Populate memory cache _memoryCache.Set<T>(cacheKey,memoryCacheOptions); return cacheKey; } 方法与此类似)

RemoveClientId

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...