Optimization Stackexchange.Redis中的流水线与批处理

Optimization Stackexchange.Redis中的流水线与批处理,optimization,redis,stackexchange.redis,Optimization,Redis,Stackexchange.redis,我试图在尽可能短的时间内插入大量元素,我尝试了以下两种选择: 1) 管道: List<Task> addTasks = new List<Task>(); for (int i = 0; i < table.Rows.Count; i++) { DataRow row = table.Rows[i]; Task<bool> addAsync = redisDB.SetAddAsync(string.Format(keyFormat, ro

我试图在尽可能短的时间内插入大量元素,我尝试了以下两种选择:

1) 管道:

List<Task> addTasks = new List<Task>();
for (int i = 0; i < table.Rows.Count; i++)
{
    DataRow row = table.Rows[i];
    Task<bool> addAsync = redisDB.SetAddAsync(string.Format(keyFormat, row.Field<int>("Id")), row.Field<int>("Value"));
    addTasks.Add(addAsync);
}
Task[] tasks = addTasks.ToArray();
Task.WaitAll(tasks);
List addTasks=new List();
for(int i=0;i
2) 配料:

List<Task> addTasks = new List<Task>();
IBatch batch = redisDB.CreateBatch();
for (int i = 0; i < table.Rows.Count; i++)
{
    DataRow row = table.Rows[i];
    Task<bool> addAsync = batch.SetAddAsync(string.Format(keyFormat, row.Field<int>("Id")), row.Field<int>("Value"));
    addTasks.Add(addAsync);
}
batch.Execute();
Task[] tasks = addTasks.ToArray();
Task.WaitAll(tasks);
List addTasks=new List();
IBatch batch=redisDB.CreateBatch();
for(int i=0;i
我没有注意到任何明显的时间差异(实际上我希望批处理方法更快):对于大约250K的插入,我得到大约7秒的流水线处理时间,而批处理大约8秒

阅读有关管道的文档

“使用管道技术,我们可以将两个请求都传送到网络上 立即消除大部分延迟。此外,它还 帮助减少数据包碎片:单独发送20个请求 (等待每个响应)将需要至少20个数据包,但 通过管道发送的请求可以装入更少的数据包(也许 哪怕只有一个。”


对我来说,这听起来很像批处理行为。我想知道这两个版本在幕后是否有很大的区别,因为在使用
procmon
进行简单检查时,我发现两个版本上的
TCP Send
s的数量几乎相同。

在幕后,SE.Redis做了大量工作,试图避免数据包碎片,因此,在您的案例中,情况非常相似也就不足为奇了。配料和平面管道的主要区别在于:

  • 批处理永远不会与同一多路复用器上的竞争操作交错(尽管它可能在服务器上交错;以避免您需要使用
    multi
    /
    exec
    事务或Lua脚本)
  • 批处理将始终避免数据包过小的可能性,因为它提前知道所有数据
  • 但同时,在发送任何内容之前,必须完成整个批处理,因此这需要更多内存缓冲,并可能人为引入延迟
在大多数情况下,通过避免批处理,您会做得更好,因为SE.Redis实现了它在简单添加工作时自动完成的大部分功能

作为最后说明;如果要避免本地开销,最后一种方法可能是:

redisDB.SetAdd(string.Format(keyFormat, row.Field<int>("Id")),
    row.Field<int>("Value"), flags: CommandFlags.FireAndForget);
redisDB.SetAdd(string.Format(keyFormat,row.Field(“Id”)),
字段(“值”),标志:CommandFlags.FireAndForget);

这将发送所有信息,既不等待响应,也不分配不完整的
Task
s来表示未来的值。您可能希望在最后执行类似于
Ping
的操作,以检查服务器是否仍在与您通话。请注意,使用fire and forget确实意味着您不会注意到报告的任何服务器错误。

Re:最后一种方法。如果使用
SetAddAsync+fireanddforget
+final
Ping
。排除未知瞬时错误的情况,是否保证在
Ping
完成时添加到集合?或者它们可能是无序到达的?@ttugates假设我们不是在谈论“集群”,那么目前无论哪种方式,秩序都应该得到保证;然而,我想引入一种新的选择加入“池”模式,在这种模式下,当出现问题时,我们使用更多的连接来避免大的堆积。在这种用法中:批处理可以保证顺序,但非批处理可以使用多个连接而不保证顺序。由于这种语义变化,这种模式将是选择性加入。