Эффективность массовой вставки для вставки списка с одной строкой

#c# #sql-server #entity-framework-core #bulkinsert #sqlbulkcopy

#c# #sql-сервер #entity-framework-core #bulkinsert #sqlbulkcopy

Вопрос:

Я пытаюсь использовать массовую вставку и заменить ее текущей общей вставкой в моем проекте. Некоторые запросы на вставку (BillType.Буклет) — это список с одной строкой, а другие — список с несколькими строками.

    public async Task CreateBill(List<BillReceiverDto> Receivers, BillType BillType)
    {
       
       var bulkList =new List<BillReceiverDto>();

        if (BillType == BillType.Booklet)
        {
            bulkList.Add(Receivers.FirstOrDefault());
        }
        else
        {
            bulkList.AddRange(Receivers);
        }

        await _dbContextProvider.GetDbContext().BulkInsertAsync(bulkList);
    }
 

Массовая вставка обладает отличной производительностью для вставки огромных данных, особенно более 100. Он вставляет 5000 объектов за 75 миллисекунд. Но эффективно ли использовать массовую вставку списка с одной строкой? Есть ли какие-либо недостатки, такие как накладные расходы или etc …?

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

1. Я так не думаю. Массовое копирование просто выполняет копирование и игнорирует типы. Если типы не совпадают, вы получите исключение. Вставка попытается преобразовать типы, что на самом деле может занять больше времени.

2. Проблема не в способности конвертировать. вопрос об эффективности массовой вставки списка с одной строкой.

3. Одно слово: бенчмарк.

4. Обязательное чтение . Сначала определите, будет ли разница иметь значение, задолго до того, как спрашивать случайных незнакомцев в Интернете, есть ли разница. Затем установите результаты самостоятельно. Если тогда еще остались вопросы, это другое дело.

Ответ №1:

Отказ от ответственности: Я владелец расширений Entity Framework

Это зависит от используемой библиотеки Aref Hemati,

В нашей библиотеке, если для вставки требуется 10 объектов или меньше, мы напрямую используем инструкцию SQL. Таким SqlBulkCopy образом, накладные расходы не используются.

Таким образом, использование нашей библиотеки даже с одной строкой прекрасно, но, очевидно, оптимизировано для сотен и тысяч строк.

Ответ №2:

Я полагаю, что расширение BulkInsertAsync использует SqlBulkCopy под капотом. В этом случае я не так давно опубликовал в блоге несколько тестов, которые могут быть полезны.

Хотя я не фокусировался на вставках одной строки, при использовании SqlBulkCopy, похоже, были затраты на меньшее количество строк (100) по сравнению с подходом с табличными параметрами. По мере увеличения объемов SqlBulkCopy снижается производительность, но при низком объеме были заметны накладные расходы. Сколько накладных расходов? По большому счету, вы, вероятно, не заметите 10 секунд миллисекунд.

Если вы имеете дело с сотнями строк, я бы на самом деле рекомендовал подход с табличными параметрами для повышения производительности. Большие объемы — SqlBulkCopy.

В зависимости от ваших потребностей / представлений о накладных расходах здесь, у меня возникнет соблазн проверить, сколько строк вам нужно вставить, и использовать механизм, который наилучшим образом соответствует объему. Лично я бы не стал использовать SqlBulkCopy для небольшого количества строк, если это очень типичный сценарий, из-за накладных расходов.

Блог: https://www.sentryone.com/blog/sqlbulkcopy-vs-table-valued-parameters-bulk-loading-data-into-sql-server

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

1. Три замечания. 1) В вашем тестовом жгуте не учитываются накладные расходы на запуск / прогрев, которые могут стать значительными при небольших нагрузках даже при многократных запусках; рассмотрите возможность рандомизации порядка сценариев. 2) Использование DataTable само по себе является очень неэффективным способом подачи механизма для больших нагрузок; IEnumerable<SqlDataRecord> это больше работы, но и заметно быстрее. Это не делает тест недействительным, просто полезно знать в целом. 3) Если буквально одна строка вставляется все или большую часть времени (сценарий OP), использование простого параметризованного INSERT , вероятно, быстрее, чем массовое копирование или TVP.

2. Многое было сделано для исключения накладных расходов, которые выходят за рамки фактического механизма процесса загрузки данных, в отличие от внешних накладных расходов — от инициализации файлов данных / журналов БД до исключения затрат на генерацию тестовых данных (так что все это делается в памяти заранее, следовательно, с возможностью обработки данных), для многократного выполнения тестов во многих разных порядках. Поэтому я думаю, что эта сторона хороша и соответствует MS docs о том, чего ожидать от накладных расходов SqlBulkCopy.

3. @JeroenMostert, у меня есть какая-нибудь документация, в которой говорится, что вставка выполняется быстрее? Я подозреваю, что массовое копирование выполняется быстрее. Вставка в диск может выполнять преобразование типов, в то время как массовое копирование не приводит к преобразованию типов.

4. Выделенная таблица данных vs . Сравнение IDataReader / DbDataReader было бы интересным — я сделал несколько других в SqlBulkCopy, но это все еще в моем списке задач. Конечно, потоковая передача данных в базу данных назначения полезна, особенно при больших объемах / ограничениях памяти. Со стороны одиночной вставки — абсолютно. В общем, я бы выбрал: одиночные вставки для очень малых чисел -> TVP до 1000 (при условии точного тестирования сценария) -> SqlBulkCopy

5. @jdweng: зачем вам нужны или доверять документам здесь? Ключевым моментом является сравнительный анализ. Нет оснований предполагать, что преобразования типов являются единственной (или даже самой важной) вещью, которая может повлиять на производительность, и для производственного использования мы можем предположить, что преобразования типов в любом случае не выполняются, поскольку, по-видимому, вы можете настроить это.