Russian Qt Forum
Ноябрь 01, 2024, 05:29 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: скорость наполнения базы  (Прочитано 9932 раз)
Aso Renji
Гость
« : Июнь 04, 2012, 03:52 »

Есть таблица с двумя колонками типа INTEGER. Первая колонка - первичный индекс. В таблицу требуется внести миллион новых строк. Вопрос: как внести этот миллион за время сравнимое со временем запихивания миллиона записей в qMap и последующей выгрузкой qMap на диск? Тип используемой БД - любой, лишь бы базовый синтаксис SQL был. Прочие блага цивилизации вроде триггеров не интересуют.

Взял SQLite, сделал prepare для запроса, все равно через SQL работа идет раз в десять медленнее чем через qMap. Такая разница наводит на мысли о том, что я где-то накосячил. И собственно, тестовый код:

Код:
void test()
{
    const int rowNumber=1000000;

    //SQL test
    QSqlDatabase myBase=QSqlDatabase::addDatabase("QSQLITE");
    myBase.setDatabaseName("testBase");
    myBase.open();
    QSqlQuery query;
    query.exec("DROP TABLE testTable");
    query.exec("CREATE TABLE testTable(field1 INTEGER PRIMARY KEY,field2 INTEGER)");
    myBase.transaction();
    QSqlQuery insert;
    insert.prepare("INSERT INTO testTable (field1,field2) VALUES(?,?)");
    QTime time;
    time.start();
    for(int i=0;i<rowNumber;++i)
    {
        insert.addBindValue(QVariant(i));
        insert.addBindValue(QVariant(i));
        insert.exec();
    }
    myBase.commit();
    qDebug()<<"SQL time = "<<time.elapsed();
    myBase.close();

    //qMap test
    time.start();
    QFile F("testFile");
    F.open(QIODevice::WriteOnly);
    QDataStream stream(&F);
    QMap<int,int>testMap;
    for(int i=0;i<rowNumber;++i)
        testMap[i]=i;
    stream<<testMap;
    F.close();
    qDebug()<<"map time = "<<time.elapsed();
}
Записан
fuCtor
Гость
« Ответ #1 : Июнь 04, 2012, 06:22 »

Попробуй делать комимт порциями, загрузил например 1000 элементов, сохранил и так далее. Потенциально должно ускорить относительно одного общего.
Записан
alexis031182
Гость
« Ответ #2 : Июнь 04, 2012, 07:38 »

Транзакции и множественные VALUES
Записан
DmitryM
Гость
« Ответ #3 : Июнь 05, 2012, 08:13 »

Такая разница наводит на мысли о том, что я где-то накосячил.
Конечно накосячил, а именно в создание таблицы
Код
SQL
CREATE TABLE testTable(field1 INTEGER PRIMARY KEY,field2 INTEGER)
 
PRIMARY KEY накладывает следующие ограничения: UNIQUE NOT NULL.
Соответственно любая приличная СУБД проверяет то, что ты туда пихаешь.
Вообще имеет смысл рассмотреть key-value storage, но это уже NoSQL
Записан
xokc
Птица говорун
*****
Offline Offline

Сообщений: 976



Просмотр профиля
« Ответ #4 : Июнь 05, 2012, 22:33 »

1. Создавать индекс после вставки записей
2. Для вставки использовать BULK INSERT
Записан
DmitryM
Гость
« Ответ #5 : Июнь 06, 2012, 09:40 »

Автор сравнивает мягкое с теплым и удивляется Смеющийся
С чего prepare, addBindValue, exec будет отрабатывать за сравнимое время c оператором [] QMap'a?
Записан
xokc
Птица говорун
*****
Offline Offline

Сообщений: 976



Просмотр профиля
« Ответ #6 : Июнь 06, 2012, 19:41 »

Ну немного не так. Ему же надо наполнить QMap и записать его на диск. В некоторых случаях это вполне соизмеримо с BULK INSERT и последующем созданием первичного ключа.
Записан
Aso Renji
Гость
« Ответ #7 : Июнь 07, 2012, 08:25 »

Автор сравнивает мягкое с теплым и удивляется Смеющийся
С чего prepare, addBindValue, exec будет отрабатывать за сравнимое время c оператором [] QMap'a?
Время вставки в B-tree (в СУБД ведь его используют?), не должно существенно отличаться от времени вставки в balanced tree используемом во всяких там (q)map. По крайней мере, пока все нужные данные болтаются в кеше, а он у SQLite большой (подкурить руками пробовал, не помогает). Время выгрузки данных на диск тоже не должно существенно различаться. Объемы то те же самые. Вот если движок SQL при каждом INSERT гоняет парсер, тады ой. Но я непойму, зачем тогда prepare придуман.

И да, убирать PRIMARY KEY пробовал. Через каждые тысячу вставок начинать новую транзакцию пробовал. Все равно тормозит. Видимо, придется смотреть в сторону NoSQL решений.
Записан
Странник
Гость
« Ответ #8 : Июнь 07, 2012, 15:28 »

Цитировать
PRAGMA synchronous;
PRAGMA synchronous = 0 | OFF | 1 | NORMAL | 2 | FULL;

Query or change the setting of the "synchronous" flag. The first (query) form will return the synchronous setting as an integer. When synchronous is FULL (2), the SQLite database engine will use the xSync method of the VFS to ensure that all content is safely written to the disk surface prior to continuing. This ensures that an operating system crash or power failure will not corrupt the database. FULL synchronous is very safe, but it is also slower. When synchronous is NORMAL (1), the SQLite database engine will still sync at the most critical moments, but less often than in FULL mode. There is a very small (though non-zero) chance that a power failure at just the wrong time could corrupt the database in NORMAL mode. But in practice, you are more likely to suffer a catastrophic disk failure or some other unrecoverable hardware fault. With synchronous OFF (0), SQLite continues without syncing as soon as it has handed data off to the operating system. If the application running SQLite crashes, the data will be safe, but the database might become corrupted if the operating system crashes or the computer loses power before that data has been written to the disk surface. On the other hand, some operations are as much as 50 or more times faster with synchronous OFF.

In WAL mode when synchronous is NORMAL (1), the WAL file is synchronized before each checkpoint and the database file is synchronized after each completed checkpoint and the WAL file header is synchronized when a WAL file begins to be reused after a checkpoint, but no sync operations occur during most transactions. With synchronous=FULL in WAL mode, an additional sync operation of the WAL file happens after each transaction commit. The extra WAL sync following each transaction help ensure that transactions are durable across a power loss, but they do not aid in preserving consistency. If durability is not a concern, then synchronous=NORMAL is normally all one needs in WAL mode.

The default setting is synchronous=FULL.

See also the fullfsync and checkpoint_fullfsync pragmas.
Записан
Aso Renji
Гость
« Ответ #9 : Июнь 08, 2012, 03:27 »

PRAGMA synchronous;
При использовании транзакций не дает никакого эффекта. Если транзакции убрать эффект от наличия прагмы весьма ощутим, но добавление записей тормозит еще больше чем при транзакциях.
Записан
Alex_C
Гость
« Ответ #10 : Июнь 08, 2012, 20:09 »

Да ребят)) Ну вы блин даете)))
Такую фигню пишите - без обид.
Делал программу, в которой было очень принципиально время занесения в БД.. Долго в этом разбирался.
Теперь по сути: Вы хотите занести данные в ... куда угодно, но не в БД - конечно это быстрее, потому как при внесении данных в БД создаются ключи, а если БД еще и OLAP - еще и связи - медленно. Есть варианты как занести быстро - но они платформозависимы. Ну как то так)))
Записан
Aso Renji
Гость
« Ответ #11 : Июнь 08, 2012, 21:22 »

Теперь по сути: Вы хотите занести данные в ... куда угодно, но не в БД - конечно это быстрее, потому как при внесении данных в БД создаются ключи
А при использовании qMap они, конечно, не создаются, потому что разработчик - идиот. Я вас очень удивлю, но и std::map, и qMap, за вычетом некоторых тонкостей работают по тем же принципам что таблица БД с кластерным индексом. Разница между B-tree и balanced tree не принципиальна.
Записан
twp
Гость
« Ответ #12 : Июнь 09, 2012, 23:20 »

Время вставки в B-tree (в СУБД ведь его используют?), не должно существенно отличаться от времени вставки в balanced tree используемом во всяких там (q)map. По крайней мере, пока все нужные данные болтаются в кеше, а он у SQLite большой (подкурить руками пробовал, не помогает). Время выгрузки данных на диск тоже не должно существенно различаться. Объемы то те же самые. Вот если движок SQL при каждом INSERT гоняет парсер, тады ой. Но я непойму, зачем тогда prepare придуман.

И да, убирать PRIMARY KEY пробовал. Через каждые тысячу вставок начинать новую транзакцию пробовал. Все равно тормозит. Видимо, придется смотреть в сторону NoSQL решений.
QMap не является деревом.
Цитата: Assistant
The QMap class is a template class that provides a skip-list-based dictionary.
prepare скорее всего не поддерживается в SQLite, его поддерживают СУБД типа Oracle. Это можно проверить через
Код
C++ (Qt)
QSqlDriver::hasFeature(QSqlDriver::PreparedQueries)
 
Аналогично можно проверить поддержку пакетных операций QSqlQuery::execBatch() и транзакций
Код
C++ (Qt)
QSqlDriver::BatchOperations
QSqlDriver::Transactions
 
Не думаю что SQLite их поддерживает
В итоге скорее всего самый быстрый способ для SQLite (как уже было предложено), это создать таблицу с одним полем, и после вставки всех данных добавить уникальный ключ.
Записан
Aso Renji
Гость
« Ответ #13 : Июнь 10, 2012, 03:08 »

QMap не является деревом.
Действительно, ступил. Впрочем, даже после замены qMap на точно древовидный std::map map все равно работает раз в пять быстрее БД.
prepare скорее всего не поддерживается в SQLite, его поддерживают СУБД типа Oracle.
По документации поддерживается и prepare (http://www.sqlite.org/c3ref/prepare.html), и транзакции (http://sqlite.org/lang_transaction.html). И по hasFeature все есть. А вот BatchOperations действительно, нет. Правда, при попытке прикрутить MySQL (тоже тормозит), Qt говорит что этого BatchOperations и там нет. Он вообще где либо есть?
Записан
twp
Гость
« Ответ #14 : Июнь 11, 2012, 13:32 »

я чего-то думал что SQLite менее продвинута. Ну по идее оракл и мелкософтовская субд поддерживает batch опреации. Ну еще как вариант можно попробовать использовать кастом драйвер к базе данных. Хотя я не думаю что можно добиться совместимых по скорости результатов с QMap. Все таки СУБД поддерживает целостность данных и проверяет типы данных которые добавляются в базу.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.159 секунд. Запросов: 22.