问题描述
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);
我有一个要求,我需要根据以下要求从内存缓存中删除特定的key
或keys
-
- 我需要从内存缓存中删除特定的
processId
+clientId
键。 - 我需要从内存缓存中删除所有与特定
clientId
密钥匹配的密钥,而不考虑processId
。 - 我还需要从内存缓存中删除与某个特定
processId
密钥匹配的所有密钥,而不管clientId
是什么。
现在,在阅读了SO和MemoryCache
教程之后,似乎无法迭代所有键(或查找与特定正则表达式模式匹配的键)并将其从缓存中删除。
问题陈述
经过各种link之后,看来我需要将缓存键存储在单独的数据结构中,然后使用该键将其删除。
- 如何在上述方法中以这种方式存储
cacheKey
,以便可以通过迭代此数据结构或任何线程安全的数据结构来轻松地删除基于以上3个要求的关键基础? - 输入结构也指定了要删除的键(以下3种模式之后)是来自其他服务,并且也未定义,因此我很愿意以任何格式表示可以帮助我做到这一点有效地完成任务。
基本上,我试图为上述所有三种模式提供一个示例,通过该示例,我可以使用MemoryCache
或RemoveByPattern
方法有效地从Remove
删除密钥。到目前为止,我仍无法弄清楚如何为这三种情况使用RemoveByPattern
或Remove
方法并有效地清除键来准备示例或测试用例。
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);
}
我该如何表达我的输入结构,使其理解我的上述三个要求,然后轻松地将其输入RemoveByPattern
或Remove
方法?
解决方法
我认为您的做法正确。目标是使CacheKeys
中的键与MemoryCache
中的键保持一致。
情况1:添加新密钥时
您需要同时向MemoryCache
和CacheKeys
添加新创建的密钥。
_memoryCache.Set<T>(cacheKey,value,memoryCacheOptions);
CacheKeys.Add(cacheKey);
情况2:删除现有密钥时
您的实施足够好。
情况3:密钥过期时
当密钥从MemoryCache
到期时,密钥集在MemoryCache
和CacheKeys
之间不一致。您需要实施一个定期在后台运行的计划任务,以检查和清理CacheKeys
(MemoryCache
是事实的来源)。
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
,具有相同的clientId
。 processId
与此类似。 (伪代码)
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