我正试图提高从Redis读取哈希值的性能,我试图实现流水线,这是正确的方法吗?
public Task FetchHashesFromRedis(List<string> redisKeys, CancellationToken cancellationToken)
{
var parallel = Environment.ProcessorCount;
var semafore = new SemaphoreSlim(initialCount: parallel, maxCount: parallel);
var tasks = new List<Task>();
for (int i = 0; i < redisKeys.Count; i++)
{
var currentKey = redisKeys.ElementAt(i);
var task = FetchFromRedis(currentKey, semafore, cancellationToken)
tasks.Add(task);
}
return Task.WhenAll(tasks);
}
这段代码有什么可以改进的?
如果你使用异步API和一组任务,Redis会自动为你输送所有的东西。你还需要确保在哈希的情况下,你利用HSET/HMGET命令的可变性来减少你需要向Redis发出的原始命令的数量。
我不确定FetchFromRedis
在您提供的代码上下文中做了什么。假设它异步执行HGET/HMGET/HGETALL操作之一,并从中返回任务,我怀疑您离得太远了。但我还没看到你是怎么得出结果的?
下面的代码在Redis中创建并获取10k哈希的数据。这是一个微不足道的细节,但它展示了您正在寻找的行为。在我的机器上(当然,我将使用localhost),执行这个命令大约需要75ms,或者大约3.8 μs/operation。当这个过程完成后,所有的结果都可以在getTasks
的任务中使用。
你会想要确保(用实际的数据负载)你不会把这个推得太重,因为砸了StackExchange。Redis可以导致它抛出超时异常(基本上,如果你排队太多的东西操作容易卡在多路复用器的消息队列和失败)。
var stopwatch = Stopwatch.StartNew();
var setTasks = new List<Task>();
for (var i = 1; i < 10000; i++)
{
setTasks.Add(db.HashSetAsync($"hash:{i}", new HashEntry[]{new HashEntry("foo", "bar"), new HashEntry("baz", 3)}));
}
await Task.WhenAll(setTasks);
var getTasks = new List<Task<RedisValue[]>>();
for (var i = 1; i < 10000; i++)
{
getTasks.Add(db.HashGetAsync($"hash:{i}", new RedisValue[]{"foo","baz"}));
}
await Task.WhenAll(getTasks);
stopwatch.Stop();
Console.Write($"total execution time:{stopwatch.ElapsedMilliseconds}");