7

Perl DBD::SQLite を使用しているときに、単一のトランザクションで 2 番目のクエリに対して「データベースがロックされています」というエラーを与える SQLite に関する既知の問題はありますか? シナリオ: Linux、Perl DBI、AutoCommit => 0、2 つのコード ブロックを含むサブルーチン (ブロックを使用して変数名をローカライズ)。最初のコード ブロックでは、select ステートメントの prepare() によってクエリ ハンドルが作成され、それが execute() され、ブロックが閉じられます。2 番目のコード ブロックでは、update ステートメントの準備によって別のクエリ ハンドルが作成され、頻繁に (30% の確率で) SQLite/DBI がこの段階でデータベース ロック エラーを返します。エラーは、execute() 中ではなく、prepare() 中に発生すると思います。

私の回避策は、最初のクエリの後にコミットすることです。(最初のクエリで終了を呼び出しても役に立ちませんでした)。エレガンスとパフォーマンスに関連するいくつかの理由から、コミットしないことを好みます。元のコードは、データベースとして Postgres を使用して、何年もの間問題なく動作していました。効果なしで sqlite_use_immediate_transaction を試しました。

他のすべての状況では、SQLite が非常にうまく機能することがわかったので、これは SQLite の問題ではなく、DBD ドライバーの見落としであると思われます。悲しいことに、私の現在のコードはスクリプトとモジュールの大きな山であるため、短い単一ファイルのテスト ケースはありません。

4

1 に答える 1

8

とにかくこれとは関係ありません:perldocからのトランザクションとデータベースのロック?DBD::SQLite

AutoCommitまたはbegin_workによるトランザクションは便利で便利ですが、厄介な「データベースがロックされています」というエラーが発生する場合があります。これは通常、誰かがトランザクションを開始し、他の人が(別のトランザクションで)データベースから読み取っているときにデータベースに書き込もうとしたときに発生します。驚かれるかもしれませんが、同時実行性を最大化するために通常の(延期された)トランザクションを開始しただけでは、SQLiteはデータベースをロックしません。書き込むステートメントを発行するときにロックを予約しますが、実際にcommitステートメントを使用して書き込もうとするまでは、他のユーザーがデータベースから読み取ることができます。ただし、データベースからの読み取りにも共有ロックが必要であり、予約した排他ロックを取得できないため、「データベースがロックされています」というエラーが発生し、他のユーザーが後で書き込もうとすると同じエラーが発生します。まだ保留中のロックがあるためです。この場合、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を参照してください。

于 2010-10-15T18:16:31.647 に答える