【问题标题】:Is there any better way to get all values from redis?有没有更好的方法从 redis 中获取所有值?
【发布时间】:2018-07-20 19:35:12
【问题描述】:

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

我有以下代码:

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,使其并行获取拆分后的键列表?

【问题讨论】:

  • 你可以从documentationtutorials开始。
  • true IEnumerable&lt;...&gt; 不太适合并发(也不适合等待);我们在这里谈论什么样的音量? 看起来你在那里使用 SE.Redis,它支持异步的完整流水线,但是......你不能使用带有 async 的迭代器块 (yield return),所以不一定有帮助。 SE.Redis 也支持MGET,但这会导致一个redis op;再说一遍:这里的数据量是多少?
  • 嗨,Marc,它是 SE.Redis。我拆分了键列表,因为我得到了超时。有 >250000 个条目。
  • 看看这个,但这是一条危险的道路stackoverflow.com/a/50488373/78569也许你可以把它和马克的答案结合起来。

标签: c# redis async-await


【解决方案1】:

我想知道您最好的选择是否可能是使用重叠管道填充字典,例如(不是确切的代码,我正在编造这个):

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 一样。

【讨论】:

  • 它工作正常,但运行速度比我的代码慢得多。如果我们可以将其与不单独询问每个密钥相结合......
猜你喜欢
  • 2021-07-05
  • 1970-01-01
  • 2017-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多