Улучшить производительность SQLite INSERT-в-секунду? - PullRequest
2790 голосов
/ 11 ноября 2009

Оптимизировать SQLite сложно. Массовая вставка приложения C может варьироваться от 85 вставок в секунду до более 96 000 вставок в секунду!

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

Обоснование: Первоначально я был разочарован производительностью, которую я видел. Оказывается, что производительность SQLite может значительно различаться (как для массовых вставок, так и для выборок) в зависимости о том, как база данных настроена и как вы используете API. Было непросто выяснить, каковы были все варианты и методы, поэтому я подумал, что было бы разумно создать эту вики-статью сообщества, чтобы поделиться результатами с читателями Stack Overflow, чтобы избавить других от проблем, связанных с теми же исследованиями.

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

  • Текстовый файл с разделителями табуляции в 28 МБ (приблизительно 865 000 записей) полного расписания транзита для города Торонто
  • Мой тестовый компьютер - P4 с тактовой частотой 3,60 ГГц, работающий под управлением Windows XP.
  • Код скомпилирован с Visual C ++ 2005 как «Релиз» с «Полной оптимизацией» (/ Ox) и Favor Fast Code (/Ot).
  • Я использую SQLite "Amalgamation", скомпилированный непосредственно в мое тестовое приложение. Моя версия SQLite немного старше (3.6.7), но я подозреваю, что эти результаты будут сопоставимы с последней версией (пожалуйста, оставьте комментарий, если вы думаете иначе).

Давайте напишем некоторый код!

Код: Простая программа на C, которая считывает текстовый файл построчно, разбивает строку на значения и затем вставляет данные в базу данных SQLite. В этой «базовой» версии кода база данных создана, но мы не будем вставлять данные:

/*************************************************************
    Baseline code to experiment with SQLite performance.

    Input data is a 28 MB TAB-delimited text file of the
    complete Toronto Transit System schedule/route info
    from http://www.toronto.ca/open/datasets/ttc-routes/

**************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "sqlite3.h"

#define INPUTDATA "C:\\TTC_schedule_scheduleitem_10-27-2009.txt"
#define DATABASE "c:\\TTC_schedule_scheduleitem_10-27-2009.sqlite"
#define TABLE "CREATE TABLE IF NOT EXISTS TTC (id INTEGER PRIMARY KEY, Route_ID TEXT, Branch_Code TEXT, Version INTEGER, Stop INTEGER, Vehicle_Index INTEGER, Day Integer, Time TEXT)"
#define BUFFER_SIZE 256

int main(int argc, char **argv) {

    sqlite3 * db;
    sqlite3_stmt * stmt;
    char * sErrMsg = 0;
    char * tail = 0;
    int nRetCode;
    int n = 0;

    clock_t cStartClock;

    FILE * pFile;
    char sInputBuf [BUFFER_SIZE] = "\0";

    char * sRT = 0;  /* Route */
    char * sBR = 0;  /* Branch */
    char * sVR = 0;  /* Version */
    char * sST = 0;  /* Stop Number */
    char * sVI = 0;  /* Vehicle */
    char * sDT = 0;  /* Date */
    char * sTM = 0;  /* Time */

    char sSQL [BUFFER_SIZE] = "\0";

    /*********************************************/
    /* Open the Database and create the Schema */
    sqlite3_open(DATABASE, &db);
    sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);

    /*********************************************/
    /* Open input file and import into Database*/
    cStartClock = clock();

    pFile = fopen (INPUTDATA,"r");
    while (!feof(pFile)) {

        fgets (sInputBuf, BUFFER_SIZE, pFile);

        sRT = strtok (sInputBuf, "\t");     /* Get Route */
        sBR = strtok (NULL, "\t");            /* Get Branch */
        sVR = strtok (NULL, "\t");            /* Get Version */
        sST = strtok (NULL, "\t");            /* Get Stop Number */
        sVI = strtok (NULL, "\t");            /* Get Vehicle */
        sDT = strtok (NULL, "\t");            /* Get Date */
        sTM = strtok (NULL, "\t");            /* Get Time */

        /* ACTUAL INSERT WILL GO HERE */

        n++;
    }
    fclose (pFile);

    printf("Imported %d records in %4.2f seconds\n", n, (clock() - cStartClock) / (double)CLOCKS_PER_SEC);

    sqlite3_close(db);
    return 0;
}

«Контроль»

Выполнение кода «как есть» на самом деле не выполняет никаких операций с базой данных, но даст нам представление о том, насколько быстры операции ввода-вывода и обработки строк в необработанном C-файле.

импортировано 864913 записей в 0,94 секунд

Отлично! Мы можем сделать 920 000 вставок в секунду, при условии, что мы фактически не делаем никаких вставок: -)


«Наихудший сценарий»

Мы собираемся сгенерировать строку SQL, используя значения, считанные из файла, и вызвать эту операцию SQL, используя sqlite3_exec:

sprintf(sSQL, "INSERT INTO TTC VALUES (NULL, '%s', '%s', '%s', '%s', '%s', '%s', '%s')", sRT, sBR, sVR, sST, sVI, sDT, sTM);
sqlite3_exec(db, sSQL, NULL, NULL, &sErrMsg);

Это будет медленно, потому что SQL будет скомпилирован в код VDBE для каждой вставки, и каждая вставка будет происходить в своей собственной транзакции. Как медленно?

импортировано 864913 записей в 9933,61 секунд

Хлоп! 2 часа 45 минут! Это всего 85 вставок в секунду.

Использование транзакции

По умолчанию SQLite оценивает каждый оператор INSERT / UPDATE в пределах уникальной транзакции. Если выполняется большое количество вставок, рекомендуется заключить транзакцию в транзакцию:

sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);

pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {

    ...

}
fclose (pFile);

sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);

импортировано 864913 записей за 38.03 секунд

Так лучше. Простое объединение всех наших вставок в одну транзакцию повысило нашу производительность до 23 000 вставок в секунду.

Использование подготовленного оператора

Использование транзакции было огромным улучшением, но перекомпиляция оператора SQL для каждой вставки не имеет смысла, если мы используем один и тот же SQL снова и снова. Давайте с помощью sqlite3_prepare_v2 скомпилируем наш оператор SQL один раз, а затем свяжем наши параметры с этим оператором, используя sqlite3_bind_text:

/* Open input file and import into the database */
cStartClock = clock();

sprintf(sSQL, "INSERT INTO TTC VALUES (NULL, @RT, @BR, @VR, @ST, @VI, @DT, @TM)");
sqlite3_prepare_v2(db,  sSQL, BUFFER_SIZE, &stmt, &tail);

sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);

pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {

    fgets (sInputBuf, BUFFER_SIZE, pFile);

    sRT = strtok (sInputBuf, "\t");   /* Get Route */
    sBR = strtok (NULL, "\t");        /* Get Branch */
    sVR = strtok (NULL, "\t");        /* Get Version */
    sST = strtok (NULL, "\t");        /* Get Stop Number */
    sVI = strtok (NULL, "\t");        /* Get Vehicle */
    sDT = strtok (NULL, "\t");        /* Get Date */
    sTM = strtok (NULL, "\t");        /* Get Time */

    sqlite3_bind_text(stmt, 1, sRT, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, sBR, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 3, sVR, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 4, sST, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 5, sVI, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 6, sDT, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 7, sTM, -1, SQLITE_TRANSIENT);

    sqlite3_step(stmt);

    sqlite3_clear_bindings(stmt);
    sqlite3_reset(stmt);

    n++;
}
fclose (pFile);

sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);

printf("Imported %d records in %4.2f seconds\n", n, (clock() - cStartClock) / (double)CLOCKS_PER_SEC);

sqlite3_finalize(stmt);
sqlite3_close(db);

return 0;

импортировано 864913 записей в 16.27 секунд

Nice! Есть немного больше кода (не забудьте назвать sqlite3_clear_bindings и sqlite3_reset), но мы более чем удвоили нашу производительность до 53 000 вставок в секунду.

PRAGMA синхронно = ВЫКЛ.

По умолчанию SQLite приостанавливается после выдачи команды записи на уровне ОС. Это гарантирует, что данные будут записаны на диск. Установив synchronous = OFF, мы инструктируем SQLite просто передать данные в ОС для записи и затем продолжить. Существует вероятность того, что файл базы данных может быть поврежден, если на компьютере произойдет катастрофический сбой (или сбой питания) перед записью данных на диск:

/* Open the database and create the schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA synchronous = OFF", NULL, NULL, &sErrMsg);

импортировано 864913 записей в 12,41 секунд

Улучшения теперь меньше, но мы до 69 600 вставок в секунду.

PRAGMA journal_mode = MEMORY

Рассмотрите возможность сохранения журнала отката в памяти, оценив PRAGMA journal_mode = MEMORY. Ваша транзакция будет быстрее, но если вы потеряете энергию или ваша программа выйдет из строя во время транзакции, ваша база данных может остаться в поврежденном состоянии с частично завершенной транзакцией:

/* Open the database and create the schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA journal_mode = MEMORY", NULL, NULL, &sErrMsg);

импортировано 864913 записей в 13.50 секунд

Немного медленнее, чем предыдущая оптимизация: 64 000 операций вставки в секунду.

PRAGMA синхронный = OFF и PRAGMA journal_mode = MEMORY

Давайте объединим две предыдущие оптимизации. Это немного более рискованно (в случае сбоя), но мы просто импортируем данные (без банка):

/* Open the database and create the schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA synchronous = OFF", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA journal_mode = MEMORY", NULL, NULL, &sErrMsg);

импортировано 864913 записей в 12.00 секунд

Fantastic! Мы можем сделать 72 000 вставок в секунду.

Использование базы данных в памяти

Просто для удовольствия, давайте опираться на все предыдущие оптимизации и переопределим имя файла базы данных, чтобы мы полностью работали в оперативной памяти:

#define DATABASE ":memory:"

импортировано 864913 записей в 10,94 секунд

Хранение нашей базы данных в оперативной памяти не является сверхпрактичным, но впечатляет, что мы можем выполнять 79 000 операций вставки в секунду.

Рефакторинг кода С

Хотя это не улучшение SQLite, мне не нравятся дополнительные операции присваивания char* в цикле while. Давайте быстро проведем рефакторинг этого кода, чтобы передать вывод strtok() непосредственно в sqlite3_bind_text(), и позволим компилятору попытаться ускорить процесс для нас:

pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {

    fgets (sInputBuf, BUFFER_SIZE, pFile);

    sqlite3_bind_text(stmt, 1, strtok (sInputBuf, "\t"), -1, SQLITE_TRANSIENT); /* Get Route */
    sqlite3_bind_text(stmt, 2, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT);    /* Get Branch */
    sqlite3_bind_text(stmt, 3, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT);    /* Get Version */
    sqlite3_bind_text(stmt, 4, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT);    /* Get Stop Number */
    sqlite3_bind_text(stmt, 5, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT);    /* Get Vehicle */
    sqlite3_bind_text(stmt, 6, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT);    /* Get Date */
    sqlite3_bind_text(stmt, 7, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT);    /* Get Time */

    sqlite3_step(stmt);        /* Execute the SQL Statement */
    sqlite3_clear_bindings(stmt);    /* Clear bindings */
    sqlite3_reset(stmt);        /* Reset VDBE */

    n++;
}
fclose (pFile);

Примечание. Мы вернулись к использованию реального файла базы данных. Базы данных в памяти быстрые, но не обязательно практичные

импортировано 864913 записей в 8,94 секунд

Небольшой рефакторинг кода обработки строки, используемого в нашей привязке параметров, позволил нам выполнить 96,700 вставок в секунду. Я думаю, можно с уверенностью сказать, что это достаточно быстро . Когда мы начнем настраивать другие переменные (например, размер страницы, создание индекса и т. Д.), Это будет нашим эталоном.


Резюме (пока)

Надеюсь, вы все еще со мной! Причина, по которой мы пошли по этому пути, заключается в том, что производительность массовой вставки так сильно меняется в SQLite, и не всегда очевидно, какие изменения необходимо внести в скорость до нашей операции. Используя тот же компилятор (и опции компилятора), ту же версию SQLite и те же данные, мы оптимизировали наш код и наше использование SQLite, чтобы перейти от от наихудшего сценария 85 вставок в секунду к более 96 000 вставок в секунду!


СОЗДАТЬ ИНДЕКС, затем ВСТАВИТЬ против ВСТАВИТЬ, затем СОЗДАТЬ ИНДЕКС

Прежде чем мы начнем измерять производительность SELECT, мы знаем, что будем создавать индексы. В одном из ответов ниже было предложено, чтобы при массовых вставках индекс быстрее создавался после вставки данных (в отличие от создания индекса сначала, а затем вставки данных). Давайте попробуем:

Создать индекс, затем вставить данные

sqlite3_exec(db, "CREATE  INDEX 'TTC_Stop_Index' ON 'TTC' ('Stop')", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);
...

импортировано 864913 записей в 18,13 секунд

Вставить данные, затем создать индекс

...
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "CREATE  INDEX 'TTC_Stop_Index' ON 'TTC' ('Stop')", NULL, NULL, &sErrMsg);

импортировано 864913 записей в 13,66 секунд

Как и ожидалось, массовая вставка выполняется медленнее, если индексируется один столбец, но имеет значение, если индекс создается после вставки данных. Наш базовый уровень без индекса составляет 96 000 вставок в секунду. Сначала создание индекса, а затем вставка данных дает 47 700 вставок в секунду, тогда как вставка данных сначала, а затем создание индекса дает 63 300 вставок в секунду.


Я бы с удовольствием принял предложения по другим сценариям, чтобы попробовать ... И скоро собираю аналогичные данные для запросов SELECT.

Ответы [ 10 ]

726 голосов
/ 11 ноября 2009

Несколько советов:

  1. Поместите вставки / обновления в транзакцию.
  2. Для более старых версий SQLite - рассмотрите менее параноидальный режим журнала (pragma journal_mode). Есть NORMAL, а затем OFF, что может значительно увеличить скорость вставки, если вы не слишком беспокоитесь о возможном повреждении базы данных в случае сбоя ОС. Если ваше приложение дает сбой, данные должны быть в порядке. Обратите внимание, что в более новых версиях настройки OFF/MEMORY небезопасны для сбоев на уровне приложений.
  3. Игра с размерами страниц также имеет значение (PRAGMA page_size). Наличие страниц большего размера может сделать чтение и запись более быстрыми, поскольку большие страницы хранятся в памяти. Обратите внимание, что для вашей базы данных будет использовано больше памяти.
  4. Если у вас есть индексы, рассмотрите возможность вызова CREATE INDEX после выполнения всех ваших вставок. Это значительно быстрее, чем создание индекса и вставка.
  5. Вы должны быть очень осторожны, если у вас есть параллельный доступ к SQLite, так как вся база данных блокируется, когда завершаются записи, и, хотя возможно несколько считывателей, записи будут заблокированы. Это было несколько улучшено с добавлением WAL в более новых версиях SQLite.
  6. Воспользуйтесь преимуществом экономии места ... меньшие базы данных работают быстрее. Например, если у вас есть пары ключ-значение, попробуйте сделать ключ INTEGER PRIMARY KEY, если это возможно, что заменит столбец подразумеваемого уникального номера строки в таблице.
  7. Если вы используете несколько потоков, вы можете попробовать использовать общий кэш страниц , который позволит разделять загруженные страницы между потоками, что позволит избежать дорогостоящих вызовов ввода-вывода.
  8. Не использовать !feof(file)!

Я также задавал похожие вопросы здесь и здесь .

121 голосов
/ 17 сентября 2014

Попробуйте использовать SQLITE_STATIC вместо SQLITE_TRANSIENT для этих вставок.

SQLITE_TRANSIENT заставит SQLite скопировать строковые данные перед возвратом.

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

92 голосов
/ 08 августа 2012

Избегать sqlite3_clear_bindings (stmt);

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

В введении C API из документации по SQLite написано

До вызова sqlite3_step () в первый раз или сразу после sqlite3_reset () приложение может вызвать один из sqlite3_bind () интерфейс для добавления значений к параметрам. каждый вызов sqlite3_bind () отменяет предыдущие привязки для того же параметра

(см .: sqlite.org / cintro.html ). В документации ничего нет, эта функция говорит, что вы должны вызывать ее в дополнение к простой установке привязок.

Подробнее: Avoid_sqlite3_clear_bindings ()

54 голосов
/ 20 мая 2011

На объемных вставках

Вдохновленный этим постом и вопросом о переполнении стека, который привел меня сюда - Можно ли одновременно вставлять несколько строк в базу данных SQLite? - я опубликовал свой первый Git хранилище:

https://github.com/rdpoor/CreateOrUpdate

, который массово загружает массив ActiveRecords в MySQL , SQLite или PostgreSQL . Он включает в себя возможность игнорировать существующие записи, перезаписать их или вызвать ошибку. Мои элементарные тесты показывают 10-кратное улучшение скорости по сравнению с последовательной записью - YMMV.

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

45 голосов
/ 11 ноября 2009

Массовый импорт лучше всего работает, если вы можете разделить свои операторы INSERT / UPDATE . Значение 10 000 или около того хорошо для меня работало на таблице с несколькими строками, YMMV ...

37 голосов
/ 04 июня 2014

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

Сначала найдите предметы в таблице:

SELECT COUNT(*) FROM table

затем прочитайте на страницах (LIMIT / OFFSET):

SELECT * FROM table ORDER BY _ROWID_ LIMIT <limit> OFFSET <offset>

где и рассчитываются для потока, как это:

int limit = (count + n_threads - 1)/n_threads;

для каждой нити:

int offset = thread_index * limit

Для нашего маленького (200 МБ) дБ это ускорилось на 50-75% (64-разрядная версия 3.8.0.2 в Windows 7). Наши таблицы сильно ненормализованы (1000-1500 столбцов, примерно 100000 или более строк).

Слишком много или слишком мало потоков этого не сделают, вам нужно провести тестирование и профилировать себя.

Также для нас SHAREDCACHE замедлил производительность, поэтому я вручную поставил PRIVATECACHE (потому что он был включен для нас глобально)

27 голосов
/ 15 апреля 2015

Я не смог получить никакой прибыли от транзакций, пока не увеличил значение cache_size, т.е. PRAGMA cache_size=10000;

18 голосов
/ 12 апреля 2017

Прочитав это руководство, я попытался внедрить его в свою программу.

У меня есть 4-5 файлов, которые содержат адреса. Каждый файл имеет около 30 миллионов записей. Я использую ту же конфигурацию, которую вы предлагаете, но мое количество INSERT в секунду слишком мало (~ 10.000 записей в секунду).

Вот где ваше предложение проваливается. Вы используете одну транзакцию для всех записей и одну вставку без ошибок / сбоев. Допустим, вы разбиваете каждую запись на несколько вставок в разных таблицах. Что произойдет, если запись будет битой?

Команда ON CONFLICT не применяется, потому что, если у вас есть 10 элементов в записи, и вам нужно, чтобы каждый элемент был вставлен в другую таблицу, если элемент 5 получает ошибку CONSTRAINT, то все предыдущие 4 вставки также должны выполняться.

Так вот, где происходит откат. Единственная проблема с откатом состоит в том, что вы теряете все свои вставки и начинаете сверху. Как вы можете решить это?

Мое решение состояло в том, чтобы использовать несколько транзакций. Я начинаю и заканчиваю транзакцию каждые 10.000 записей (не спрашивайте, почему это число, оно было самым быстрым, которое я проверял). Я создал массив размером 10.000 и вставил туда успешные записи. Когда возникает ошибка, я выполняю откат, начинаю транзакцию, вставляю записи из моего массива, фиксирую, а затем начинаю новую транзакцию после поврежденной записи.

Это решение помогло мне обойти проблемы, возникающие у меня при работе с файлами, содержащими плохие / повторяющиеся записи (у меня было почти 4% плохих записей).

Алгоритм, который я создал, помог мне сократить процесс на 2 часа. Окончательный процесс загрузки файла 1 час 30 минут, который все еще медленный, но не сравнивается с 4 часами, которые он изначально занимал. Мне удалось ускорить вставки с 10.000 / с до ~ 14.000 / с

Если у кого-то есть какие-либо идеи о том, как ускорить его, я открыт для предложений.

UPDATE

В дополнение к моему ответу выше, вы должны иметь в виду, что число вставок в секунду зависит от используемого вами жесткого диска. Я протестировал его на 3 разных ПК с разными жесткими дисками и получил огромные различия во времени. ПК1 (1 час 30 м), ПК2 (6 часов) ПК3 (14 часов), поэтому я начал задаваться вопросом, почему это так.

После двух недель исследований и проверки нескольких ресурсов: жесткого диска, памяти, кэша, я обнаружил, что некоторые настройки на жестком диске могут влиять на скорость ввода-вывода. Нажав на свойства желаемого выходного диска, вы увидите две опции на вкладке «Общие». Opt1: сжать этот диск, Opt2: разрешить индексировать содержимое файлов на этом диске.

Отключив эти две опции, все 3 ПК теперь требуют примерно одинакового времени для завершения (1 час и от 20 до 40 минут). Если вы столкнулись с медленной вставкой, проверьте, настроен ли ваш жесткий диск с этими параметрами. Это сэкономит вам много времени и головной боли, пытаясь найти решение

10 голосов
/ 12 июня 2017

Ответ на ваш вопрос заключается в том, что более новый sqlite3 улучшил производительность, используйте это.

Этот ответ Почему SQLAlchemy вставка с sqlite в 25 раз медленнее, чем использование sqlite3 напрямую? от SqlAlchemy Orm Автор имеет 100 000 вставок за 0,5 секунды, и я видел похожие результаты с python-sqlite и SqlAlchemy. Что заставляет меня верить, что производительность улучшилась с sqlite3

0 голосов
/ 12 марта 2019

Используйте ContentProvider для вставки массовых данных в дБ. Приведенный ниже метод используется для вставки больших объемов данных в базу данных. Это должно улучшить производительность INSERT в секунду SQLite.

private SQLiteDatabase database;
database = dbHelper.getWritableDatabase();

public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {

database.beginTransaction();

for (ContentValues value : values)
 db.insert("TABLE_NAME", null, value);

database.setTransactionSuccessful();
database.endTransaction();

}

Вызвать метод bulkInsert:

App.getAppContext().getContentResolver().bulkInsert(contentUriTable,
            contentValuesArray);

Ссылка: https://www.vogella.com/tutorials/AndroidSQLite/article.html проверьте раздел ContentProvider для более подробной информации

...