Удаление строк по пакетам. Как открыть / повторно использовать соединение с SQL Server?

#c# #sql-server #dapper

#c# #sql-сервер #dapper

Вопрос:

Какой был бы наиболее эффективный способ открыть / использовать соединение с SQL Server, если мы читаем строки, подлежащие удалению в пакетах?

 foreach(IEnumerable<Log> logsPage in LogsPages)
{
    foreach(Log logEntry in logsPage)
    {
        // 1. get associated filenames
        // 2. delete row
        // 3. try delete each file
    }
}
  
  • Размер страницы журнала составляет около 5000 строк
  • Файлы, связанные с записями журнала, могут различаться по размеру. Я не думаю, что они больше, чем, скажем, 500 Мб.
  • Мы используем Dapper

Должны ли мы позволять Dapper открывать соединения на каждом шаге foreach цикла? Я полагаю, что здесь происходит объединение пулов соединений с SQL Server?

Или мы должны открывать явное соединение для каждого пакета?

Комментарии:

1. Почему бы не прочитать все файлы за один шаг, а затем выполнить один DELETE , чтобы удалить их все на основе тех же критериев, которые вы использовали для SELECT ? Вы могли бы выполнить эту команду одновременно с операциями удаления файлов. Чтобы избежать файлов-призраков, если операция с файлом завершается неудачно, в то время как Db завершается успешно, вы можете сохранить список файлов на диск и перезапустить удаления после сбоя

2. Вы можете запускать удаления одновременно, до определенного момента. Операции с файлами не являются чистым вводом-выводом, поскольку ОС выполняет собственное хранение, а данные кэшируются на нескольких уровнях, от ОС до самого диска. Возможно, вы сможете сократить время удаления, удаляя файлы одновременно с ограниченным DOP, например, используя ActionBlock с DOP>1

3. @PanagiotisKanavos Вы имеете в виду запрос всех имен файлов для каждого пакета, а затем попытку удалить их [файлы] параллельно с удалением строк? Я никогда раньше не использовал поток данных TPL, что-то, что заставляет меня задуматься.

4. используйте SELECT Filename from sometable where ... для получения имен файлов, сохранения списка, затем используйте DELETE sometable where .... или delete sometable FROM sometable INNER JOIN ... , используя те же условия, что и запрос выбора. Пока это происходит, вы можете удалить файлы из списка с помощью File.Delete .

Ответ №1:

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

Итак, чтобы быть явным, я бы поставил Open[Async]() здесь выше первого foreach .

Примечание: для пакетной обработки вы можете обнаружить, что существуют способы сделать это с меньшим количеством обходов, в частности, с использованием IN перезаписи в Dapper на основе идентификаторов. Поскольку вы упомянули SQL-Server, Это можно объединить с установкой a SqlMapper.Settings.InListStringSplitCount на что-то положительное (5, 10 и т. Д. — разумный выбор; обратите внимание, что это глобальный параметр); например, для простого сценария:

 connection.Execute("delete from Foo where Id in @ids",
    new { ids = rows.Select(x => x.Id) });
  

намного эффективнее, чем:

 foreach (var row in rows)
{
    connection.Execute("delete from Foo where Id = @id",
        new { id = row.Id });
}
  

Без InListStringSplitCount этого первая версия будет переписана как что-то вроде:

 delete from Foo where Id in (@ids0, @ids1, @ids2, ..., @idsN)
  

При InListStringSplitCount этом первая версия будет переписана как что-то вроде:

 delete from Foo where Id in (select cast([value] as int) from string_split(@ids,','))
  

что позволяет использовать один и тот же запрос много раз, что хорошо для повторного использования плана запроса.