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

2024-01-18

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

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 秒的批处理时间。

从有关管道的文档中读取,

“使用管道使我们能够将两个请求发送到网络上 立即,消除了大部分延迟。此外,它还 有助于减少数据包碎片:单独发送 20 个请求 (等待每个响应)至少需要 20 个数据包,但是 20 在管道中发送的请求可以容纳在更少的数据包中(也许 即使只有一个)”。

对我来说,这听起来很像批处理行为。我想知道在幕后两者之间是否有很大的区别,因为通过简单的检查procmon我看到的数量几乎相同TCP Send两个版本都有。


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

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

在大多数情况下,避免批处理会做得更好,因为 SE.Redis 实现了它的大部分功能自动地当简单地添加工作时。

最后一点;如果您想避免本地开销,最后一种方法可能是:

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

这会发送所有内容,既不等待响应也不分配不完整的数据Tasks 代表未来值。你可能想做一些类似的事情Ping在最后without即发即忘,检查服务器是否仍在与您通信。请注意,使用“即发即弃”确实意味着您不会注意到报告的任何服务器错误。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Stackexchange.Redis 中的流水线与批处理 的相关文章

随机推荐