質問

現在 SQLite3 を使用して Rails サイトを実行しています。

およそ 500 リクエストごとに 1 回、

ActiveRecord::StatementInvalid (SQLite3::BusyException:データベースはロックされています:...

コードへの影響を最小限に抑えてこれを修正する方法は何ですか?

DB をソース管理に保存できるため、バックアップが自然になり、変更を非常に迅速にプッシュできるため、現在 SQLLite を使用しています。ただし、実際には同時アクセス向けに設定されていないのは明らかです。明日の朝には MySQL に移行します。

役に立ちましたか?

解決

デフォルトでは、データベースがビジーでロックされている場合、sqlite はブロックされたビジー エラーを即座に返します。諦める前に、しばらく待って試し続けるように依頼することもできます。これで通常は問題が解決します。ただし、データベースにアクセスするスレッドが数千個もある場合を除き、sqlite は不適切であると私も同意します。

    // set SQLite to wait and retry for up to 100ms if database locked
    sqlite3_busy_timeout( db, 100 );

他のヒント

これは Rails サイトであるとおっしゃいました。Rails では、database.yml 構成ファイルで SQLite の再試行タイムアウトを設定できます。

production:
  adapter: sqlite3
  database: db/mysite_prod.sqlite3
  timeout: 10000

タイムアウト値はミリ秒単位で指定します。10 秒または 15 秒に増やすと、ログに表示される BusyException の数が減少します。

ただし、これは一時的な解決策にすぎません。サイトに真の同時実行性が必要な場合は、別の DB エンジンに移行する必要があります。

これらはすべて真実ですが、おそらく次のような質問には答えていません。Rails アプリが本番環境で時折 SQLite3::BusyException を発生させるのはなぜですか?

@シャルマネス:実稼働ホスティング環境はどのようなものですか?共有ホスト上にありますか?sqlite データベースを含むディレクトリは NFS 共有上にありますか?(おそらく共有ホスト上です)。

この問題は、NFS 共有によるファイル ロックの現象と SQLite の同時実行性の欠如に関係している可能性があります。

記録のために。Rails 2.3.8 を使用したあるアプリケーションで、Rifkin Habsburg が提案した「タイムアウト」オプションを Rails が無視していることがわかりました。

さらに調査した結果、Rails 開発に関連する可能性のあるバグが見つかりました。 http://dev.rubyonrails.org/ticket/8811. 。さらに調査した結果、 ソリューション (Rails 2.3.8 でテスト済み):

この ActiveRecord ファイルを編集します。activerecord-2.3.8/lib/active_record/connection_adapters/sqlite_adapter.rb

これを置き換えます:

  def begin_db_transaction #:nodoc:
    catch_schema_changes { @connection.transaction }
  end

  def begin_db_transaction #:nodoc:
    catch_schema_changes { @connection.transaction(:immediate) }
  end

以上です!パフォーマンスの低下は見られず、アプリは中断することなくさらに多くのリクエストをサポートするようになりました (タイムアウトを待機します)。スクライトいいですね!

bundle exec rake db:reset

私にとってはうまくいきました。リセットされ、保留中の移行が表示されます。

ソース: このリンク

- Open the database
db = sqlite3.open("filename")

-- Ten attempts are made to proceed, if the database is locked
function my_busy_handler(attempts_made)
  if attempts_made < 10 then
    return true
  else
    return false
  end
end

-- Set the new busy handler
db:set_busy_handler(my_busy_handler)

-- Use the database
db:exec(...)

Sqlite では、現在のプロセスが終了するまで他のプロセスを待機させることができます。

複数のプロセスが Sqlite DB にアクセスしようとしている可能性があることがわかっている場合は、次の行を使用して接続します。

conn = sqlite3.connect('ファイル名', 隔離レベル = '排他的')

Python Sqlite ドキュメントによると、次のようになります。

connect(または)connect()通話への孤立_ levelパラメーターを介して、または接続の分離_levelプロパティを介して、どのような種類の開始ステートメントを制御できますか(またはまったくありません)。

rake db:merge でも同様の問題がありました。問題は、作業ディレクトリが SMB 共有上にあったことでした。フォルダーをローカルマシンにコピーすることで問題を修正しました。

この問題がある場合でも、 タイムアウトを増やしても何も変わりません, 、トランザクションに関して別の同時実行性の問題が発生する可能性があります。要約すると次のようになります。

  1. トランザクションを開始します(トランザクションを取得します) 共有 ロック)
  2. DB からいくつかのデータを読み取ります (まだ使用しています 共有 ロック)
  3. その間、別のプロセスがトランザクションを開始し、データを書き込みます ( 予約済み ロック)。
  4. 次に、書き込もうとすると、今度は、 予約済み ロック
  5. SQLite が SQLITE_BUSY 例外を発生させる すぐに (タイムアウトとは無関係に)以前の読み取りは、データを取得できるまでに正確ではなくなる可能性があるため、 予約済み ロック。

これを修正する 1 つの方法は、 active_record を取得するための sqlite アダプター 予約済み をパディングしてトランザクションの開始時に直接ロックします。 :immediate ドライバーのオプションです。これによりパフォーマンスは少し低下しますが、少なくともすべてのトランザクションはタイムアウトに従い、次々に発生します。これを使用してこれを行う方法は次のとおりです prepend (Ruby 2.0+) これを初期化子に入れます:

module SqliteTransactionFix
  def begin_db_transaction
    log('begin immediate transaction', nil) { @connection.transaction(:immediate) }
  end
end

module ActiveRecord
  module ConnectionAdapters
    class SQLiteAdapter < AbstractAdapter
      prepend SqliteTransactionFix
    end
  end
end

詳細はこちらをご覧ください: https://rails.lighthouseapp.com/projects/8994/tickets/5941-sqlite3busyExceptions-are-raised-immediately-in-some-cases-despite-setting-sqlite3_busy_timeout

ほとんどの回答は生の Ruby ではなく Rails に関するものであり、OP は Rails について IS を質問しますが、それは問題ありません。:)

したがって、生の Ruby ユーザーがこの問題を抱えていて、yml 設定を使用していない場合に備えて、この解決策をここに残しておきたいと思います。

接続をインスタンス化した後、次のように設定できます。

db = SQLite3::Database.new "#{path_to_your_db}/your_file.db"
db.busy_timeout=(15000) # in ms, meaning it will retry for 15 seconds before it raises an exception.
#This can be any number you want. Default value is 0.

ロックが発生したときにアクセスされているテーブルは何ですか?

長時間実行されるトランザクションはありますか?

ロックが発生したときにどのリクエストがまだ処理されていたかわかりますか?

ああ、先週の私の存在の悩みです。Sqlite3 は、何らかのプロセスが実行されると db ファイルをロックします。 書きます データベースに。IE 任意の UPDATE/INSERT タイプのクエリ (何らかの理由で count(*) も選択します)。ただし、複数の読み取りは問題なく処理されます。

そこで私はついにイライラして、データベース呼び出しの周囲に独自のスレッド ロック コードを作成しました。アプリケーションがデータベースに書き込むスレッドを常に 1 つだけにすることで、数千のスレッドまで拡張することができました。

そして、はい、それは地獄のように遅いです。しかし、十分に高速でもあり、 正しい, 、これは素晴らしいプロパティです。

sqlite3 Ruby 拡張機能でデッドロックを見つけたので、ここで修正します。試してみて、問題が解決するかどうかを確認してください。

    https://github.com/dxj19831029/sqlite3-ruby

プルリクエストを開いたのですが、もう応答がありません。

とにかく、sqlite3 自体で説明されているように、何らかのビジー例外が予想されます。

注意してください この条件では: スクライト忙しい

    The presence of a busy handler does not guarantee that it will be invoked when there is 
    lock contention. If SQLite determines that invoking the busy handler could result in a 
    deadlock, it will go ahead and return SQLITE_BUSY or SQLITE_IOERR_BLOCKED instead of 
    invoking the busy handler. Consider a scenario where one process is holding a read lock 
    that it is trying to promote to a reserved lock and a second process is holding a reserved 
    lock that it is trying to promote to an exclusive lock. The first process cannot proceed 
    because it is blocked by the second and the second process cannot proceed because it is 
    blocked by the first. If both processes invoke the busy handlers, neither will make any 
    progress. Therefore, SQLite returns SQLITE_BUSY for the first process, hoping that this 
    will induce the first process to release its read lock and allow the second process to 
    proceed.

この条件を満たした場合、タイムアウトは無効になります。これを避けるには、begin/commit 内に select を置かないでください。または、開始/コミットに排他ロックを使用します。

お役に立てれば。:)

これは多くの場合、同じデータベースにアクセスする複数のプロセスの連続した障害です。RubyMine で「1 つのインスタンスのみを許可する」フラグが設定されていない場合

以下を実行してみてください。役立つかもしれません。

ActiveRecord::Base.connection.execute("BEGIN TRANSACTION; END;") 

から: ルビィ:SQLite3::BusyException:データベースがロックされています:

これにより、システムを妨げているトランザクションが解消される可能性があります

これはトランザクションがタイムアウトしたときに発生すると思います。本当に「本物の」データベースを使用する必要があります。Drizzle や MySQL のようなもの。前の 2 つのオプションよりも SQLite を好む理由はありますか?

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top