Конвейерная обработка против пакетной обработки в 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 секунд для пакетной обработки.

Читая документацию по конвейеризации,

«Использование конвейерной обработки позволяет нам немедленно получить оба запроса в сети, устраняя большую часть задержки. Кроме того, это также помогает уменьшить фрагментацию пакетов: 20 запросов, отправленных по отдельности (ожидающих каждого ответа), потребуют как минимум 20 пакетов, но отправлено 20 запросов. в конвейере может поместиться гораздо меньшее количество пакетов (возможно, даже один) ».

Для меня это звучит очень похоже на пакетное поведение. Интересно, есть ли за кадром какая-то большая разница между ними, потому что при простой проверке с помощью procmon я вижу почти одинаковое количество TCP Send в обеих версиях.


person CyberDude    schedule 06.01.2015    source источник


Ответы (1)


За кулисами SE.Redis проделывает довольно много работы, чтобы избежать фрагментации пакетов, поэтому неудивительно, что в вашем случае это очень похоже. Основное различие между дозированием и плоской конвейерной обработкой:

  • пакет никогда не будет чередоваться с конкурирующими операциями на одном мультиплексоре (хотя он может чередоваться на сервере; чтобы избежать этого, вам нужно использовать транзакцию _1 _ / _ 2_ или сценарий Lua)
  • пакет всегда будет избегать вероятности получения пакетов меньшего размера, потому что он знает обо всех данных заранее
  • но в то же время весь пакет должен быть завершен, прежде чем что-либо может быть отправлено, поэтому это требует большей буферизации в памяти и может искусственно вызвать задержку

В большинстве случаев вы добьетесь большего успеха, отказавшись от пакетной обработки, поскольку SE.Redis выполняет большую часть того, что делает автоматически, просто добавляя работу.

В качестве заключительного примечания; если вы хотите избежать локальных накладных расходов, последний подход может быть следующим:

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

Это отправляет все по сети, не ожидая ответов и не выделяя неполные Task для представления будущих значений. Возможно, вы захотите сделать что-то вроде Ping в конце без включения и выключения, чтобы проверить, что сервер все еще разговаривает с вами. Обратите внимание, что использование «запустил и забыл» означает, что вы не заметите никаких ошибок сервера, о которых будет сообщено.

person Marc Gravell    schedule 18.03.2015
comment
Re: последний подход. При использовании SetAddAsync + FireAndForget + final Ping. За исключением случая неизвестных временных ошибок, будет ли гарантировано добавление Наборов к моменту завершения Ping? Или они могли прибыть не по порядку? - person ttugates; 02.05.2021
comment
@ttugates, предполагая, что мы не говорим о кластере, порядок должен быть в настоящее время гарантирован в любом случае; тем не менее, я хочу представить новый объединенный режим подписки, в котором мы используем больше подключений, чтобы избежать больших скоплений, когда что-то пойдет не так. В этом случае: пакетная обработка будет гарантировать порядок, но без пакетной обработки может использоваться несколько соединений без каких-либо гарантий порядка. Этот режим будет включен из-за этого семантического изменения. - person Marc Gravell; 02.05.2021