Комментарии 33
благодарю за 3-ую часть.
Отдельное спасибо за присоединение базы данных в памяти, этого-то мне и не хватало. :)
Спасибо за всю статью! :)
Пожалуйста, не кидайте в меня гнилыми помидорами, а, напишите, что же все-таки, конкретно нужно сделать, чтобы БЫСТРО вставить 100500 записей в таблицу (желательно с примером кода)?
Если база пустая (не жалко потерять):
PRAGMA synchronous = 0;
PRAGMA journal_mode = OFF;
BEGIN;
INSERT INTO XXX ...;
INSERT INTO XXX ...;
… 10000 вставок…
COMMIT;
BEGIN;
INSERT INTO XXX ...;
INSERT INTO XXX ...;
… 10000 вставок…
COMMIT;
… пока все не вставится…
PRAGMA synchronous = FULL;
PRAGMA journal_mode = DELETE;
PRAGMA synchronous = 0;
PRAGMA journal_mode = OFF;
BEGIN;
INSERT INTO XXX ...;
INSERT INTO XXX ...;
… 10000 вставок…
COMMIT;
BEGIN;
INSERT INTO XXX ...;
INSERT INTO XXX ...;
… 10000 вставок…
COMMIT;
… пока все не вставится…
PRAGMA synchronous = FULL;
PRAGMA journal_mode = DELETE;
Добавил в конец статьи ссылки про оптимизацию вставок в SQLite
0. Прибить имеющиеся индексы
1. Заворачиваешь порции Insert-ов по несколько тысяч в транзакции
2. Разделяешь создание запросов и их исполнение по разным thread, ибо первое — CPU, а второе IO bound.
3. Ждешь окончания транзакций и перестраиваешь индексы.
Пример кода ( под iPhone + немного страшен из-за некоторых попыток оптимизации )
github.com/dodikk/CsvToSqlite
Дерзай.
1. Заворачиваешь порции Insert-ов по несколько тысяч в транзакции
2. Разделяешь создание запросов и их исполнение по разным thread, ибо первое — CPU, а второе IO bound.
3. Ждешь окончания транзакций и перестраиваешь индексы.
Пример кода ( под iPhone + немного страшен из-за некоторых попыток оптимизации )
github.com/dodikk/CsvToSqlite
Дерзай.
>>Увы, не существует возможности открыть два соединения к одной и той же БД в памяти.
А по ссылке www.sqlite.org/inmemorydb.html в разделе «In-memory Databases And Shared Cache» написанно, что можно.
А по ссылке www.sqlite.org/inmemorydb.html в разделе «In-memory Databases And Shared Cache» написанно, что можно.
Верно! Спасибо за информацию)
Не могу исправить в статье — получаю ошибку AJAX.
Увы, не существует возможности открыть два соединения к одной и той же БД в памяти.
UPD: Уже, оказывается, можно открыть два соединения к одной БД в памяти.
Не могу исправить в статье — получаю ошибку AJAX.
UPD: Уже, оказывается, можно открыть два соединения к одной БД в памяти.
rc = sqlite3_open("file:memdb1?mode=memory&cache=shared", &db);
ATTACH DATABASE 'file:memdb1?mode=memory&cache=shared' AS aux1;
Три раза в статье повторено, что нужно использовать только UTF-8. А можно пояснить — почему так?
a) на текущий момент парсер только UTF-8 (не исключает факта, что в будущем появится парсер UTF-16);
b) UTF-8, как правило, быстрее (меньше данных пишется на диск);
c) база в памяти занимает меньше места.
b) UTF-8, как правило, быстрее (меньше данных пишется на диск);
c) база в памяти занимает меньше места.
Спасибо. Только не совсем понятен первый пункт — какой именно парсер? В статьях упоминания про него не нашел.
Много лет использую в SQLite кодировку windows-1251 вместо UTF-8. Никаких проблем с парсером это не создаёт. Но вот операции LIKE работают неправильно (не понимают регистр русских символов, конечно) и FTS-раширения тоже работают неправильно. Не стал переделывать collations, а вместо этого в FTS-индексах привожу всё к lowercase, и это снимает проблему.
Это означает, что целостность гарантирована при аварии приложения (поскольку ОС продолжает работать), но не при аварии ОС или отключении питания.
Режим синхронизации NORMAL (или 1) гарантирует целостность при авариях ОС и почти при всех отключениях питания. Существует ненулевой шанс, что при потере питания в самый неподходящий момент база испортится. Это некий средний, компромисный режим по производительности и надежности.
Режим FULL гарантирует целостность всегда и везде и при любых авариях. Но работает, разумеется, медленнее, поскольку в определенных местах делаются паузы ожидания. И это режим по умолчанию.
Блаженны верующие…
Режим FULL отличается от NORMAL только на макоси, где для FULL вызывается fnctl(fd, F_FULLFSYNC, ...) вместо fsync(). Для линукса всегда будет fdatasync, для винды — FlushFileBuffers. И ничего они не могут гарантировать, если диск или дисковый контроллер обманет. Хорошо (а, главное, честно) написано тут (ага, постгрес).
Учитывая вышесказанное, мне не очень понятно, что за смешные 50 транзакций в секунду. Постгрес выдает ~1000 (OpenVZ, Core2Duo E8400, HDD 7200RPM)
Цифра взята отсюда: www.sqlite.org/faq.html#q19
Также сами разработчики утверждают, что в режиме FULL делаются дополнительные lock. Я не проверял этот факт. Разумеется, фраза про целостность всегда и везде содержит некоторую иронию. Мне казалось, что это заметно.
Также сами разработчики утверждают, что в режиме FULL делаются дополнительные lock. Я не проверял этот факт. Разумеется, фраза про целостность всегда и везде содержит некоторую иронию. Мне казалось, что это заметно.
Опять же сошлюсь на свой (и тысяч клиентов нашего софта!) многолетний опыт использования SQLite на серверах: SQLite все-таки изредка не справляется с сохранением целостности БД при сбоях питания или зависаниях железа (перегрев). Независимо от настроек синхронизации и режима журнала. Притом, что у нас он используется на Windows, где от NTFS ожидается дополнительная помощь в этом деле… Но увы. Не зря в SQLite есть backup API :)
Все-таки, мне кажется, вы ошибаетесь насчет отличия FULL от NORMAL.
Комментарий из кода sqlite3.c:
Если поискать в коде места с «pPager->fullSync», то можно увидеть, что есть дополнительные операции в режиме FULL.
Комментарий из кода sqlite3.c:
/*
** CAPI3REF: Synchronization Type Flags
**
** When SQLite invokes the xSync() method of an
** [sqlite3_io_methods] object it uses a combination of
** these integer values as the second argument.
**
** When the SQLITE_SYNC_DATAONLY flag is used, it means that the
** sync operation only needs to flush data to mass storage. Inode
** information need not be flushed. If the lower four bits of the flag
** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics.
** If the lower four bits equal SQLITE_SYNC_FULL, that means
** to use Mac OS X style fullsync instead of fsync().
**
** Do not confuse the SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags
** with the [PRAGMA synchronous]=NORMAL and [PRAGMA synchronous]=FULL
** settings. The [synchronous pragma] determines when calls to the
** xSync VFS method occur and applies uniformly across all platforms.
** The SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags determine how
** energetic or rigorous or forceful the sync operations are and
** only make a difference on Mac OSX for the default SQLite code.
** (Third-party VFS implementations might also make the distinction
** between SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL, but among the
** operating systems natively supported by SQLite, only Mac OSX
** cares about the difference.)
*/
#define SQLITE_SYNC_NORMAL 0x00002
#define SQLITE_SYNC_FULL 0x00003
#define SQLITE_SYNC_DATAONLY 0x00010
Если поискать в коде места с «pPager->fullSync», то можно увидеть, что есть дополнительные операции в режиме FULL.
Нда, пожалуй, ошибаюсь (назовут все одинаково, сиди потом, грепай...), хотя это и не отменяет соображений насчет надежности fsync.
FULL от NORMAL отличается одной дополнительной синхронизацией журнала и выравниванием на следующий сектор при записи имени журнала. Т.е. оверхед относительно небольшой, и теоретически NORMAL и FULL тормозят примерно одинаково. Тогда, имхо, основная задержка возникает при записи самих данных (в постгресе синхронизируется только WAL, данные пишутся отдельным процессом в бекграунде).
В таком случае, в WAL режиме работать должно на порядок быстрее.
FULL от NORMAL отличается одной дополнительной синхронизацией журнала и выравниванием на следующий сектор при записи имени журнала. Т.е. оверхед относительно небольшой, и теоретически NORMAL и FULL тормозят примерно одинаково. Тогда, имхо, основная задержка возникает при записи самих данных (в постгресе синхронизируется только WAL, данные пишутся отдельным процессом в бекграунде).
В таком случае, в WAL режиме работать должно на порядок быстрее.
Ну, насчет «на порядок» не уверен) Но быстрее, да.
Да, WAL — оптимальный вариант, когда нужна приемлемая производительность без особого дрожания за целостность. Хотя в реализации WAL-режима в SQLite вплоть до текущих версий есть какая-то ошибка, из-за которой журнал иногда растёт больше заданного лимита (в многопоточном режиме использования). При этом всё продолжает работать без ошибок, но постепенно всё медленнее и медленнее, пока не переоткроешь БД в эксклюзивном режиме, тогда WAL записывается в основной файл, и всё нормализуется.
В столбец NONE значения заносятся «как есть» (этот тип используется по умолчанию, если не задан другой)Поверил, но оказалось не совсем так, если 64-битное число хранить в таком поле, то оно портится (INTEGER тоже не подходит), приходится использовать поле TEXT.
Вероятно тип NONE преобразуется автоматически к TEXT или INTEGER, в зависимости от содержимого.
Большое спасибо за пояснение WAL-режима человеческим языком, мне реально пригодилось при выяснении мелких нюансов для Delphi + FireDAC с 16+ одновременно пишущих thread-ов.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
SQLite — замечательная встраиваемая БД (часть 3)