有没有更好的方法来从Redis获取所有值?

时间:2018-07-20 11:05:02

标签: c# redis async-await

我知道从缓存中获取所有值并不是最好的主意,但是我有一个特殊的内部工具用例。

我有以下代码:

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,以便它并行获取拆分的键列表?

1 个答案:

答案 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的增加一样。