#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,','))
что позволяет использовать один и тот же запрос много раз, что хорошо для повторного использования плана запроса.