Pull to refresh

Comments 34

>> В мире SQLite более “простые” операции всегда должны идти левее более “сложных”.
Спасибо. Это небольшое, но очень важное знание на будущее. Там, где теория совсем расходится с конкретной реализацией. А за всем и не уследишь.
Я думаю было бы полезнее, если бы автор привел больше примеров оптимизации взаимодействия с SQLite. Не жадничайте опытом — больше примеров!
Я вот, например, знаю про команду VACUUM, которая тоже бывает полезна при работе с SQLite.
Мне совсем не сложно, но остальные примеры более относятся ко всем базам данных, нежели исключительно к SQLite. Вот, например, два:

1) При большой количестве инсертов (когда речь идёт на тысячи и десятки тысяч) используйте «BEGIN TRANSACTION» и «COMMIT». Оптимальную периодичность открытия-закрытия транзакции трудно посчитать, но мы эмпирически вывели число 100, т.е. как-то так:

if(!(insert_counter % 100))
{
sqlite3_exec(db, "COMMIT", NULL, NULL, &errmsg);
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &errmsg);
}


2) Как ни смешно это звучит, но большинство непроизводительного кода часто бывает по той причине, что для выполнения одной операции использовалось несколько обращений к базе.

Например, нужно вывести картинку машины заданного цвета. Первый запрос ищет машину, второй — её ID в таблице цветов, и третий непосредственно ссылку на PNG-картинку. Объединив все запросы с один, мы повышаем производительность в десятки раз.

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

Но опять же, это относится ко всем базам данных.
Это правило, кстати, хорошо работает и в языках программирования при ряде OR условий.
Да, я в курсе + есть шанс вывалиться из короткозамкнутого условия по простой проверке, не выполняя сложных операций.
работать с SQLite напрямую бывает еще удобно когда одно и тоже приложение пишется под две и более платформы (андроид и iOS например). тогда можно использовать одни и теже запросы к базе.

еще SQLite не работает корректно с юникод строками (в частности, русскими символами) и тогда его приходится патчить ICU расширением.
Спасибо, теперь понятно почему оно меня так сегодня ругало при записи в базу.
все запросы перевожу в utf8 — всё корректно.
А у меня помню были проблемы с немецким языком. Только, к сожалению, не помню как решили, было больше двух лет назад…
добавление выполняется без проблем.
у нас были некорректно выполнялся select like. регистронезависимый поиск не работал с русским языком. тогда и пришлось патчить
Кстати, как и что оптимизировать в SQLite становится понятно, покопавшись у него «внутрях», благо код очень компактный и понятный. Например, я, в свое время, узнал, что SQL-запрос компилируется в обыкновенную процедурную программу, которая потом исполняется на простенькой виртуальной машине. А изучение работы механизма пейджинга тоже дает пару идей, где можно искать проблемы с производительностью.
Вообще основания для использования FmDb вместо Core Data, которые привел автор, ясно дают понять, что автор практически не знаком с принципами работы и методами работы с Core Data с большими данными.
Часто, простота освоения и использования Core Data останавливают многих на более глубокое изучение этой технологии. Как следсвите, при возникновении не очень тривиальных задач,
эти многие начинают искать «костыли» или альтернативные пути, вместо того чтоб более детально ознакомится с CoreData.

UFO landed and left these words here
Полностью поддерживаю! Первое время сам удивлялся и почему c CoreData все так тормозит, причем даже на незначительных объемах данных. Как только разобрался все встало на свои места.
Может и не знаком так хорошо как Вы, но в любом случае, согласитесь, CoreData будет работать медленнее по определению ORM. Не в разы, не на порядки, но медленнее.

К тому же, как правильно заметил ara89 выше, есть ещё один сильный довод — кросс-платформенность.
В некоторых случаях не медленее, а даже быстрее, благодаря fault'ам. И намного безопасней и экономичней с точки зрения управления памятью, можно держать объекты из разных мест, благодаря тому-же faulting и uniquing. Например вы сделали два запроса к пересекающимся данным из разных мест — держите двойное раходование памяти, в коре-дата естественно этого нет, и более того, часть данных уже загуржена и доступна (которая находится в пересечении), остальные же можно догрузить лениво, если нужно.
Конечно это можно и напрямую сделать, но получится намного сложнее, да и в итоге получится тот-же Core Data. Уж в очень экзотических случаях можно использовать гибридный подход: выполнять SQL запросы к хранилищу Core Data.

какая-же кросс-платформенность, если будет использоваться тот-же FmDb. Кросс-платформенность в данном случае относительная, да и смысл ее, если писать нативное приложение.
Я бы сказал что в некоторых случаях для обеспечения одной модели для мака и iOS лучше использовать Core Data для обеспечения той-же кросс-платформенности.
>> Уж в очень экзотических случаях можно использовать гибридный подход: выполнять SQL запросы к хранилищу Core Data.

Вот этого, на сколько я знаю, не получится сделать, если речь идет не об использовании предикатов.
это как одно другому то мешать может? у sql свои предикаты, а у core data свои. Они даже при желании не пересекаются.
Имел ввиду особый формат хранения данных CoreData в SQLLite базе. Технически запрос конечно можно выполнить.
>> Обращение к SQLite базе из двух потоков одновременно неизбежно вызовет краш

На самом деле это не так. При открытии базы возможно использовать флаг SQLITE_OPEN_FULLMUTEX, при условии, что данная функциональность не была отключена на этапе компиляции.
www.sqlite.org/c3ref/open.html
Спасибо, ценное замечание! Почитал документацию — оказывается, SQLite может работать в трёх режимах

Но всё же на stackoverflow более популярны советы по NSOperationQueue и @synchronized, т.е. организовать строго последовательный доступ средствами языка, а не параллельный — базой, т.к. человек пишет, что при FULLMUTEX и большой количестве параллельных инсертов у него пропадают записи.
Не знание CoreData на должном уровне, вовсе не говорит о том что этой очередной костыль системы, что выходит из вашей статьи. Ваши выводы не объективны и однобоки, и несут лишь синтетическую нагрузку. Не буду повторяться, скажу лишь что я полностью согласен с «rule» комментарии которого выше.

На мой взгляд, отказ от использования CoreData может быть обусловлен только нехваткой времени на его изучение.
Ааа, знакомый тролль :) А я милого узнаю по походке…
В свое время начали использовать fmdb, тысячу раз пожалели, проблем с ним набролись достаточно.
Ваша статья вызывает недоумение.

Sqlite замечательно использует индексы. Отсутствие хранимых процедур не влияет на их работу.

Условие LIKE '%foo%' не индексируется ни в одной БД, включая Oracle. Поэтому оно не может и не должно влиять на подбор индекса.

Работать из двух потоков можно, используя два соединения к БД. Использовать одно соединение можно при условии, то sqlite работает в режиме сериализации.

Кэширование данных можно выполнять по-разному. Можно и БД в памяти использовать.

Вообщем, не очень понятно, что хотели сказать.
Столько времени прошло… но отвечу

— SQLite нормально использует индексы, я с этим не спорю;
— с какого перепуга LIKE вообще может индексироваться, это бред, и я такого не писал. Я писал о том, что в SQLite нет оптимизатора запросов, в отличие от Oracle и SQL Server, поэтому нужно думать, в каком порядке ставить условия;
— в настройках по умолчанию SQLite однопоточна, мьютексы и тем более Serialized ведут к очень существенному провису пефоманса.
— про какое кеширование идет речь? Если вам нужно просто закешировать данные, так понятно что тут вообще плоскопараллельно, как и что делать. В какой памяти хранить, если речь шла о сотнях мегабайт базы?

В общем, непонятно что вы хотели сказать.
Не обижайтесь, расшифрую подробнее.

Изначально делается посылка, что sqlite это «недобаза» из-за отсутствия встроенного языка, прав доступа, и пр. Этот вывод расширяется на «плохую» работу оптимизатора запросов. В sqlite безусловно имеется оптимизатор запросов, не такой мощный как в Oracle, но вполне приличный. В каждой новой версии sqlite он улучшается.

Поэтому, вначале надо понять какой версией sqlite пользуетесь вы. Затем увидеть схему БД, напишите какие индексы есть по указанной таблице. Если вы действительно наблюдаете такую разницу по времени, то, возможно, наткнулись на какой-то баг и следует отправить баг-репорт.

Оператор LIKE может быть использован в индексе при записи операнда в виде 'ABC%'.

Насчет однопоточности: не знаю каким образом встроена sqlite на вашей платформе, но обычно всегда можно открыть N независимых соединений к БД. Блокировка БД будет возникать только при обновлении (т.е. sqlite работает по принципу «многие читают-один пишет») и ее можно обойти. Короче, просто открывайте дополнительные соединения к БД.

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

Спасибо за развернутый ответ, изменил свое мнение о вашей карме :)

Про первое отвечу — я люблю SQLite, но оптимизатора запросов там либо нет, либо он слишком простой. В любом случае, лучше знать эту особенность чем не знать.

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

Ну и про БД в памяти — БД занимала около 700 мегабайт (энтерпрайз приложение), ее невозможно было хранить в памяти.

Но все равно отдельное уважение за развернутый комментарий!
Работа оптимизатора описана здесь.

Если под iOS нельзя читать двоим это странно. Зачем такое ограничение…
Only those users with full accounts are able to leave comments. Log in, please.