Почему SQLite дает «базу данных заблокирована» для второго запроса в транзакции при использовании Perl DBD :: SQLite? - PullRequest
6 голосов
/ 15 октября 2010

Известна ли проблема, связанная с тем, что SQLite выдает ошибку «база данных заблокирована» для второго запроса в одной транзакции при использовании Perl DBD :: SQLite? Сценарий: Linux, Perl DBI, AutoCommit => 0, подпрограмма с двумя блоками кода (использование блоков для локализации имен переменных). В первом блоке кода дескриптор запроса создается методом prepare () в операторе select, он выполняется () и блок закрывается. Второй блок кода, другой дескриптор запроса, создается подготовкой к оператору обновления, и часто (30% времени) SQLite / DBI выдает ошибку блокировки базы данных на этом этапе. Я думаю, что ошибка происходит во время prepare (), а не во время execute ().

Моя работа заключается в том, чтобы коммитить после первого запроса. (Вызов финиша по первому запросу не помог). Я предпочитаю не совершать по нескольким причинам, связанным с элегантностью и производительностью. Исходный код прекрасно работал в течение многих лет с Postgres в качестве базы данных. Я попытался sqlite_use_immediate_transaction без эффекта.

Во всех других ситуациях я обнаружил, что SQLite работает очень хорошо, поэтому я подозреваю, что это упущение в драйвере DBD, а не проблема с SQLite. К сожалению, мой текущий код представляет собой большую кучу скриптов и модулей, поэтому у меня нет короткого теста для одного файла.

1 Ответ

7 голосов
/ 15 октября 2010

Это никак не связано с этим: Транзакция и блокировка базы данных из DBD::SQLite perldoc?

Транзакция AutoCommit или begin_work хороша и удобна, но иногда вы можете получить досадную ошибку «база данных заблокирована». Обычно это происходит, когда кто-то начинает транзакцию и пытается выполнить запись в базу данных, пока другой человек читает из базы данных (в другой транзакции). Вы можете быть удивлены, но SQLite не блокирует базу данных, когда вы просто начинаете обычную (отложенную) транзакцию, чтобы максимизировать параллелизм. Он резервирует блокировку при выдаче инструкции на запись, но до тех пор, пока вы на самом деле не попытаетесь написать с помощью оператора фиксации, он позволяет другим людям читать из базы данных. Однако для чтения из базы данных также требуется общая блокировка, и это не позволяет предоставить вам эксклюзивную блокировку, которую вы зарезервировали, поэтому вы получаете ошибку «база данных заблокирована», и другие люди получат такую ​​же ошибку, если они попытаются выполнить запись позже, так как у вас все еще есть ожидающая блокировка. busy_timeout в этом случае не помогает.

Чтобы избежать этого, установите тип транзакции явно. Вы можете выполнить немедленную немедленную транзакцию (или начать исключительную транзакцию) для каждой транзакции или установить для атрибута дескриптора базы данных sqlite_use_immediate_transaction значение true (начиная с 1.30_02), чтобы всегда использовать немедленную транзакцию (даже если вы просто используете begin_work или выключаете AutoCommit.) .

my $dbh = DBI->connect("dbi:SQLite::memory:", "", "", {
  sqlite_use_immediate_transaction => 1,
});

Обратите внимание, что это работает только тогда, когда все соединения используют одну и ту же (неотложенную) транзакцию. Подробнее о блокировке см. http://sqlite.org/lockingv3.html.

...