我知道从缓存中获取所有值并不是最好的主意,但是我有一个特殊的内部工具用例。
我有以下代码:
public IEnumerable<KeyValuePair<K, V>> GetAllStoreEntries()
{
var keys = RedisStore.Server.Keys(pattern: $"{GetCompletePrefix()}:*", pageSize: pageSize).ToList();
foreach (var subList in Split(keys, pageSize))
{
var subKeys = subList.ToArray();
var values = RedisStore.RedisCache.StringGet(subKeys); <---------
for (var i = 0; i < values.Length; i++)
{
var value = values[i];
if (value.HasValue)
{
var svalue = JsonConvert.DeserializeObject<V>(value);
yield return new KeyValuePair<K, V>(GetKKey(subKeys[i]), svalue);
}
}
}
}
在标记的位置上,我想使用StringGetAsync,但是我不知道如何重组可以使用的代码。
如何重写GetAllStoreEntries,以便它并行获取拆分的键列表?
答案 0 :(得分:0)
我想知道您最好的选择是否是使用重叠的管道填充字典,就像(不是精确的代码,我正在做的那样):
async Task<Dictionary<K, V>> GetAllStoreEntries()
{
var keys = RedisStore.Server.Keys(pattern: $"{GetCompletePrefix()}:*", pageSize: pageSize);
var result = new Dictionary<K, V>();
const int OVERLAP = 10; // the number of in-flight operations
await keys.Pipe<RedisKey, RedisValue>(OVERLAP,
key => redis.StringGet(key),
(key, val) => result.Add(GetKey(key), Deserialize(val)));
return result;
}
使用(未试用):
public static async Task Pipe<TSource, TResult>(this IEnumerable<TSource> source,
int pipeLength,
Func<TSource, Task<TResult>> map, Action<TSource, TResult> reduce)
{
var buffer = new (TSource val, Task<TResult> task)[pipeLength];
uint index = 0;
foreach (var item in source)
{
var newTask = map(item);
if (newTask.IsCompleted)
{ // completed synchronously; check for error and move on
reduce(item, newTask.Result);
}
else
{
var thisIndex = index++ % pipeLength;
var oldPair = buffer[thisIndex];
if (oldPair.task != null) reduce(oldPair.val, await oldPair.task);
buffer[thisIndex] = (item, newTask);
}
}
for (int i = 0; i < pipeLength; i++)
{
var oldPair = buffer[(i + index) % pipeLength];
if (oldPair.task != null) reduce(oldPair.val, await oldPair.task);
}
}
此尝试要做的是:
ToList()
完全,未经测试,但是...从理论上讲应该可以!并且:由于流水线异步操作重叠,它不会为每个密钥支付延迟。 OVERLAP
的增加可能会进一步减少延迟,就像pageSize
的增加一样。