可能な限り短い時間で多数の要素を挿入しようとしていますが、次の2つの方法を試しました。
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のリクエストが送信されます。パイプラインでは、はるかに少ないパケット(おそらく1つだけ)に収まる可能性があります。」
私には、これはバッチ処理の動作によく似ています。 procmon
で簡単にチェックすると、両方のバージョンでほぼ同じ数のTCP Send
が表示されるため、舞台裏で2つの間に大きな違いがあるのではないかと思います。
舞台裏では、SE.Redisはパケットの断片化を回避するためにかなりの作業を行っているため、あなたの場合と非常に似ていることは驚くことではありません。バッチ処理とフラットパイプラインの主な違いは次のとおりです。
multi
/exec
トランザクションまたはLuaスクリプトを使用する必要がないようにするため)ほとんどの場合、SE.Redisは単に作業を追加するだけで、ほとんどのことを実行するため、バッチ処理を回避することでより良い結果が得られます自動的に。
最後に、ローカルオーバーヘッドを回避したい場合、最後のアプローチの1つは次のとおりです。
redisDB.SetAdd(string.Format(keyFormat, row.Field<int>("Id")),
row.Field<int>("Value"), flags: CommandFlags.FireAndForget);
これにより、応答を待つことも、将来の値を表すために不完全なTask
sを割り当てることもなく、すべてがネットワークに送信されます。最後にPing
のようなことをしたいと思うかもしれませんなしファイアアンドフォーゲット、サーバーがまだあなたと話していることを確認するために。ファイアアンドフォーゲットを使用すると、報告されるサーバーエラーに気付かないことに注意してください。