android sqlite "база данных заблокирована" ошибки несмотря на использование поставщика контента и последовательный доступ к базе данных
У меня есть приложение (Android 2.2 Google API Level 8), в котором есть несколько действий, извлекающих данные из контент-провайдера (ВЫБЕРИТЕ только доступ к базе данных). Он также имеет службу с централизованной блокировкой очереди задач, принимающей любые задачи записи в базу данных; Операции могут запускать сервисный запрос (как намерение), который помещает задачу в очередь блокировки для последовательного извлечения одним потоком и выполнения. База данных составляет около 4 МБ.
Существует один помощник базы данных, который служба использует для вызова методов для взаимодействия с базой данных, включая запись в нее; Все записи SQL выполняются в помощнике по базе данных.
- Все записи базы данных окружены транзакцией.
- Все чтения базы данных закрывают курсор в конце метода.
- Ни одно из действий не имеет дескриптора объекта базы данных, они могут общаться только через поставщика контента или службы.
- Любые запускаемые с помощью AlarmManager задачи, например "Операции", используют сервис только для добавления соответствующей задачи в очередь.
- Служба является единственным классом, который имеет дескриптор для помощника базы данных.
- Все записи в базу данных выполняются только через задачу, помещенную в очередь; Я тщательно проверил, что выполнение задачи является последовательным, хорошо понимая, что необходимо избегать одновременных записей в базу данных SQLite.
Во время выполнения задач я постоянно получаю одну или две ошибки "база данных заблокирована" при попытке записи в базу данных, вызванную выполнением задачи "начать транзакцию".
При попытке отследить источник блокировки я обнаружил, что использование dbhelper.inTransaction(), dbhelper.isLockedByThisThread(), dbhelper.isLockedByOtherThread() не помогло, так как они не указывали бы на неожиданную блокировку базы данных.
Я обнаружил, что раннее обнаружение блокировки сработало в том, чтобы создать метод с beginTransaction() и setTransactionSuccessful без какого-либо реального кода записи SQL, в блоке try catch, который бы регистрировал проблему - всегда вызывается методом beginTransaction().
Я поместил эту ловушку блокировки базы данных по обе стороны от каждого из методов задачи очереди блокировки в ожидании / надежде на то, что я найду единственного преступника, который оставит базу данных в заблокированном состоянии после завершения. Я не мог найти последовательного виновника. После детализации от начала вызова задачи до записи в базу данных я обнаружил, что блокировка базы данных может происходить, по-видимому, совершенно неожиданно, не будучи заблокированной ранее выполненной задачей (Все эти задачи выполняются последовательно в одном единственном потоке).
Посмотрев на опыт других людей с проблемами блокировки базы данных, я попытался закрыть соединение с базой данных сразу после завершения транзакции по всем задачам, но это не помогло, если что-то получало больше случаев блокировки базы данных. Попробовал добавить сон между каждым выполнением задачи; не был полностью протестирован, но, как правило, обнаружил, что задержка в 3 секунды и более, по-видимому, останавливает появление блокировок базы данных. Пробовал отключать диспетчер тревожных заданий - без разницы.
У меня сложилось впечатление, что какая-то форма обслуживания, внешняя по отношению к моему приложению, периодически закрывает и блокирует базу данных - возможно, задерживает запись журналов. Очевидно, я менее чем заинтересован в настройке задержки обработки задачи, поэтому я рассматриваю вопрос об использовании очереди повторных попыток блокировки базы данных, чтобы при необходимости повторить попытку записи в базу данных; многие предпочитают решать, но у меня заканчиваются идеи.
Кто-нибудь может вспомнить какой-то принцип или промах, который я пропустил?
На самом ли деле в Android и более крупных базах данных SQLite нормально, что вы иногда получаете блокировки базы данных?
Спасибо
3 ответа
SQLite гарантирует последовательный доступ из нескольких потоков, если вы используете одно соединение с базой данных. Как и где вы открываете и закрываете соединение с базой данных?
Я обычно рекомендую открыть базу данных один раз при запуске и никогда не закрывать ее. Закрытие не имеет смысла, так как транзакционная природа SQLite означает, что записи в любом случае сбрасываются в постоянное хранилище как можно скорее.
Я бы проверил, есть ли у какого-либо действия, которое вызывает БД или другое действие, которое вызывает БД, только один экземпляр. Иначе, в некотором смысле, он может заблокироваться.
Что касается
На самом ли деле в Android и более крупных базах данных SQLite нормально, что вы иногда получаете блокировки базы данных?
Нет, это определенно не нормально для случайных блокировок базы данных. Читая свой рассказ, вы говорите, что у вас есть и служба, и поставщик контента, извлекающий информацию из базы данных, поэтому возможно, что вы блокируете базу данных между двумя доступами.
Что я обычно делаю, так это проверяю, что я обрабатываю весь доступ к базе данных через провайдера контента. Имея единую точку входа в базу данных, вы можете гарантировать, что каждый программный компонент использует одну и ту же логику для доступа к БД. Возможно ли, чтобы ваш сервис имел доступ к БД через контент-провайдера?
Также хорошо помнить, что, размещая вашу БД позади провайдера контента, она по-прежнему может быть доступна нескольким потокам одновременно. Чтобы обеспечить доступ к БД только по одному потоку за раз, вы можете поместить синхронизированные конструкции в БД внутри вашего контент-провайдера. Очевидно, что если вы выполняете много длинных операций записи / чтения в БД, блокировка таким образом абсолютно разрушит ваше приложение. Размещение всего кода вашей БД внутри провайдера контента также даст вам единую точку отладки, которая поможет вам выяснить, обращаются ли к БД несколько потоков.