Повышение производительности массовых вставок SQLite с помощью Dapper ORM

Я работаю над настольным приложением, которое использует SQLite для массовой вставки десятков тысяч строк в базу данных SQLite. Мне нужна помощь в оптимизации производительности объемной вставки. В настоящее время вставка 60 мегабайт данных в базу данных занимает до 50 секунд.

  • # P2 #
    # P3 #
  • Я использую Dapper ORM. (построен ребятами из StackOverflow) Есть ли более быстрый способ массовой вставки в Sqlite в .net?

  • System.Data.Sqlite используется для вставки в SQLite. Как насчет получения специальной скомпилированной версии sqlite, улучшающей производительность? Одна версия SQLite лучше другой? В настоящее время используется System.Data.SQLite с сайта http://sqlite.phxsoftware.com

  • В настоящее время я оборачиваю вставки внутри транзакции, чтобы сделать их быстрее (это хорошее улучшение).

  • Я вставляю по одной таблице по 17 таблиц. Могу ли я распараллелить это на разных потоках и сделать это быстрее?

Текущая производительность. Это типично? Могу я сделать лучше?

  • 55000 строк в таблице с 19 столбцами: 2,25 с для вставки (24 тыс. Вставок / с)
  • 10 000 строк в таблице с 63 столбцами: 2,74 с для вставки (3,7 к / с)

Мне нравится SQLite, но я бы хотел сделать его немного быстрее. В настоящее время сохранение моих объектов в файл XML с использованием сериализации XML происходит быстрее, чем сохранение в базе данных SQLite, поэтому мой босс спрашивает: зачем переходить на SQLite? Или мне следует использовать MongoDB или другую объектную базу данных?


person BrokeMyLegBiking    schedule 25.01.2012    source источник
comment
возможный дубликат Как повысить производительность SQLite? ... Хотя в этом вопросе написан код на языке C, я использовал его в качестве руководства при написании материала на C # с помощью System.Data.SQLite. Важным является получение правильного PRAGMA и использование транзакции. Но как это соотносится с ORM, я не знаю.   -  person Chris J    schedule 25.01.2012
comment
55000 строк за 1,73 секунды ... сложно быть лучше;)   -  person Felice Pollano    schedule 25.01.2012
comment
используете ли вы пакетный интерфейс cnn.Execute (insert ..., new [] {val1, val2, val3}), он повторно использует команды, так что немного быстрее (возможно, на 5-10%)   -  person Sam Saffron    schedule 26.01.2012
comment
@ChrisJ ... это требует особого внимания к вещам, я думаю, что это не обман   -  person Sam Saffron    schedule 26.01.2012


Ответы (2)


Итак, я наконец нашел способ высокопроизводительной массовой вставки в SQLite с использованием .NET. Этот трюк повысил производительность пластин в 4,1 раза! Мое общее время сохранения увеличилось с 27 до 6,6 секунды. Вау!

В этой статье объясняется самый быстрый способ выполнить массовую вставку в SQLite. Ключ повторно использует одни и те же объекты параметров, но для каждой вставляемой записи присваивает другое значение. Время, затрачиваемое .NET на создание всех этих объектов DbParameter, действительно увеличивается. Например, со 100 тыс. Строк и 30 столбцами = 3 миллиона объектов параметров, которые необходимо создать. Вместо этого гораздо быстрее создать и повторно использовать всего 30 объектов параметров.

Новый спектакль:

  • 55000 строк (19 столбцов) за 0,53 секунды = 100 тыс. Вставок в секунду

        internal const string PeakResultsInsert = @"INSERT INTO PeakResult values(@Id,@PeakID,@QuanPeakID,@ISTDRetentionTimeDiff)";
    
                var command = cnn.CreateCommand();
                command.CommandText = BatchConstants.PeakResultsInsert;
    
                string[] parameterNames = new[]
                                     {
                                       "@Id",
                                       "@PeakID",
                                       "@QuanPeakID",
                                       "@ISTDRetentionTimeDiff"
                                      };
    
                DbParameter[] parameters = parameterNames.Select(pn =>
                {
                    DbParameter parameter = command.CreateParameter();
                    parameter.ParameterName = pn;
                    command.Parameters.Add(parameter);
                    return parameter;
                }).ToArray();
    
                foreach (var peakResult in peakResults)
                {
                    parameters[0].Value = peakResult.Id;
                    parameters[1].Value = peakResult.PeakID;
                    parameters[2].Value = peakResult.QuanPeakID;
                    parameters[3].Value = peakResult.ISTDRetentionTimeDiff;
    
                    command.ExecuteNonQuery();
                }
    

В итоге я не мог использовать Dapper для вставки в мои большие таблицы. (Для своих небольших таблиц я все еще использую Dapper).

Обратите внимание на некоторые другие вещи, которые я обнаружил:

  • Я пробовал использовать несколько потоков для вставки данных в одну и ту же базу данных, это не дало никаких улучшений. (не имело значения)

  • Обновление System.Data.Sqlite с 1.0.69 до 1.0.79. (я не видел никакой разницы в производительности)

  • Я не назначаю Type для DbParameter, это не влияет на производительность в любом случае.

  • Что касается чтения, мне не удалось улучшить производительность Dapper.

person BrokeMyLegBiking    schedule 02.02.2012
comment
@samsaffron. Спасибо за подсказку, попробую DynamicParameters. Но похоже, что Dapper должен делать это автоматически. Когда вы передаете список объектов для вставки в БД, он должен повторно использовать одни и те же объекты параметров для каждой строки. Создает ли он новый массив объектов параметров для каждой строки? Это почему Dapper медленный для вставок? - person BrokeMyLegBiking; 03.02.2012
comment
перечитывая ваш вопрос, dapper должен работать нормально здесь с точно такой же перфорацией ... если вы передадите IEnumerable ‹T› для вставки, он сгенерирует тот же код, что и выше - person Sam Saffron; 03.02.2012
comment
@samsaffron, мои тесты производительности ясно показывают, что массовые вставки с помощью приведенного выше кода в 4 раза быстрее, чем использование IEnumerable ‹T› в dapper insert. (к несчастью). Мой код dapper: (я что-то упустил?) Cnn.Execute (BatchConstants.PeakResultsInsert, peakResults, trans); - person BrokeMyLegBiking; 03.02.2012
comment
вы можете связать с сутью с помощью тестовой оснастки? - person Sam Saffron; 04.02.2012
comment
Я загрузил свое ConsoleApp, которым пользуюсь. легкий документ, который включает в себя два метода тестирования, который включает в себя () и InsertPeakResultsNativeAdo () - person BrokeMyLegBiking; 04.02.2012
comment
Не могли бы вы объяснить, какая часть этого кода препятствует запуску одного оператора SQL для каждой вставленной записи? На мой взгляд, это не похоже на ОБЪЕМНУЮ вставку. - person nuzzolilo; 01.11.2013
comment
Ссылка на статью мертва, но +1 за объяснение, ответ отличный даже без ссылки. Пища для размышлений, это может относиться и к другим ORM. - person CAD bloke; 08.01.2016

В настоящее время я оборачиваю вставки внутри транзакции, чтобы сделать их быстрее (это хорошее улучшение).

Самый большой прирост скорости пластин, который я видел, - это разбиение пластин на более мелкие куски. Я уверен, насколько маленький фрагмент зависит от платформы / схемы / и т. Д. Я считаю, что во время моих тестов это было около 1000 или около того.

person Tom Kerr    schedule 25.01.2012