在Stackexchange.Redis中管道传输与批处理

时间:2015-01-06 09:51:22

标签: 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, row.Field<int>("Id")), row.Field<int>("Value"));
    addTasks.Add(addAsync);
}
Task[] tasks = addTasks.ToArray();
Task.WaitAll(tasks);

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);

我没有注意到任何显着的时间差异(实际上我预计批处理方法会更快):对于大约250K的插入,流水线操作大约需要7秒,批处理大约需要8秒。

阅读有关流水线的文档,

  

&#34;使用流水线操作可以让我们将两个请求都发送到网络上   立即消除大部分延迟。此外,它也   有助于减少数据包碎片:单独发送20个请求   (等待每个响应)将需要至少20个包,但是20个   在管道中发送的请求可能适合更少的数据包(也许   甚至只有一个。&#34;

对我而言,这听起来很像批处理行为。我想知道幕后是否存在两者之间的任何重大差异,因为通过procmon进行简单检查,我发现两个版本的TCP Send数量几乎相同。

1 个答案:

答案 0 :(得分:26)

在幕后,SE.Redis尝试避免数据包碎片做了相当多的工作,因此在您的情况下它非常相似并不奇怪。批处理和扁平流水线之间的主要区别是:

  • 批处理永远不会与同一多路复用器上的竞争操作交错(尽管它可能在服务器上交错;为避免您需要使用multi / exec事务或Lua脚本)
  • 批处理将始终避免小包的可能性,因为它提前知道所有数据
  • 但同时,必须先完成整个批处理才能发送任何内容,因此这需要更多的内存缓冲并可能会人为地引入延迟

在大多数情况下,您可以通过避免批处理来做得更好,因为SE.Redis只需添加工作即可实现自动的大部分功能。

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

redisDB.SetAdd(string.Format(keyFormat, row.Field<int>("Id")),
    row.Field<int>("Value"), flags: CommandFlags.FireAndForget);

这会发送所有内容,既不等待响应也不分配不完整的Task来表示未来的值。您可能希望在结束时执行类似Ping的操作,而无需点击,以检查服务器是否还在与您通话。请注意,使用“即发即弃”确实意味着您不会注意到报告的任何服务器错误。