android sqlite «база данных заблокирована» ошибки несмотря на использование поставщика контента и последовательный доступ к базе данных - PullRequest
10 голосов
/ 31 октября 2011

У меня есть приложение (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 ]

7 голосов
/ 15 января 2012

SQLite гарантирует последовательный доступ из нескольких потоков, если вы используете одно соединение с базой данных.Как и где вы открываете и закрываете соединение с базой данных?

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

0 голосов
/ 19 января 2012

Относительно

На самом ли деле в Android и более крупных базах данных SQLite нормально, что вы периодически получаете блокировки базы данных?

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

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

Также хорошо помнить, что, размещая вашу БД позади провайдера контента, она по-прежнему может быть доступна нескольким потокам одновременно. Чтобы обеспечить доступ к БД только по одному потоку за раз, вы можете поместить синхронизированные конструкции в БД внутри вашего провайдера контента. Очевидно, что если вы выполняете много длинных операций записи / чтения в БД, блокировка таким образом абсолютно разрушит ваше приложение. Размещение всего кода вашей БД внутри провайдера контента также даст вам единую точку отладки, которая поможет вам выяснить, обращаются ли к БД несколько потоков.

0 голосов
/ 15 января 2012

Я бы проверил, есть ли у какого-либо действия, которое вызывает БД или другое действие, которое вызывает БД, только один экземпляр.Иначе, в некотором смысле, он может заблокироваться.

...