web-dev-qa-db-ja.com

マルチスレッドアプリケーションでSQLiteを使用する方法は?

SQLite をデータベースとして使用してアプリケーションを開発していますが、それを複数のスレッドで使用する方法を理解するのに少し苦労しています(残念ながら、他のStack Overflowの質問はまったく役に立ちませんでした)。

私のユースケース:データベースには1つのテーブルがあり、それを「A」と呼びましょう。これには、列の1つに基づいて異なる行グループがあります。テーブルAからコンテンツを読み取るアプリケーションの「メインスレッド」があります。さらに、ときどき、特定の行グループを更新することにしました。これを行うには、新しいスレッドを生成し、グループのすべての行を削除し、それらを再挿入します(これがアプリのコンテキストで行う唯一の方法です)。これは同時に異なるグループで発生する可能性があるため、2つ以上のスレッドがデータベースを更新しようとしている可能性があります。

私は各スレッドから異なるトランザクションを使用しています、つまりすべてのスレッドの更新サイクルの開始時に開始します。実際、各スレッドが実際に行うのは、「BEGIN」を呼び出し、データベースから「更新」する必要があるすべての行を削除し、新しい値で再度挿入します(これは、myのコンテキストで行う必要がある方法です)応用)。

今、私はこれを実装する方法を理解しようとしています。読んでみました(SQLiteサイト、Stack Overflowのその他の回答)が、すべての回答が見つかりませんでした。ここに私が疑問に思っていることがいくつかあります:

  1. 「open」を呼び出して、各スレッドから新しいsqlite構造を作成する必要がありますか?
  2. このすべてに特別なコードを追加する必要がありますか?それとも、異なるスレッドを生成し、行を更新するだけで十分ですか?(別のトランザクションを使用しているため)
  3. さまざまなロックの種類について話していることや、特定のAPIを呼び出すことで「SQLite busy」を受け取る可能性があるという事実を見ましたが、正直なところ、これらすべてを考慮する必要があるときに完全に説明された参照はありませんでした。する必要がありますか?

誰かが質問に答えたり、良いリソースの方向に私を向けることができるなら、私はとても感謝しています。

UPDATE 1:これまで読んだことから、データベースファイルに書き込むスレッドを2つ持つことはできないようですとにかく。

参照: http://www.sqlite.org/lockingv3.html セクション3.0:予約済みロックは、プロセスが将来のある時点でデータベースファイルへの書き込みを計画しているが、現在はファイルからの読み取りのみを行っていることを意味します。一度にアクティブにできるのは、1つのRESERVEDロックだけです。ただし、複数のSHAREDロックが1つのRESERVEDロックと共存できます。

これは、毎回行のグループを更新するために単一のスレッドからのみスポーンできることを意味しますか?つまり、行の一部を更新する必要があると判断したポーラースレッドがあり、それを実行するために新しいスレッドを作成しますが、一度に複数のスレッドを作成することはありませんか?私が作成した他のスレッドのように見えるので、とにかく最初のスレッドが終了するまでSQLITE_BUSYを取得します。

物事を正しく理解しましたか?

ところで、これまでの回答に感謝し、彼らは多くを助けてきました。

43
Edan Maor

このリンク をご覧ください。最も簡単な方法は、自分でロックを行い、スレッド間で接続を共有しないようにすることです。別の優れたリソースを見つけることができます here

  1. -DTHREADSAFE = 1を指定してSQLiteをコンパイルしていることを確認してください。

  2. 各スレッドがデータベースファイルを開き、独自のsqlite構造を保持していることを確認してください。

  3. 1つ以上のスレッドが同時にdbファイルにアクセスするときに衝突する可能性を処理するようにしてください。SQLITE_BUSYを適切に処理してください。

  4. データベースファイルを変更するコマンド(INSERT、UPDATE、DELETEなど)をトランザクション内に含めるようにしてください。

22
Kristian

マルチスレッドで使用するためにSQLliteを開始するときのいくつかの手順:

  1. Sqliteがマルチスレッドフラグでコンパイルされていることを確認してください。
  2. 各スレッドで接続を作成するには、sqliteファイルでopenを呼び出す必要があります。スレッド間で接続を共有しないでください。
  3. SQLiteには非常に保守的なスレッドモデルがあり、INSERT/UPDATE/DELETEを実行しようとしているトランザクションを開くことを含む書き込み操作を実行すると、他のスレッドはこの操作が完了するまでブロックされます。
  4. トランザクションを使用しない場合、トランザクションは暗黙的であるため、INSERT/DELETE/UPDATEを開始すると、sqliteは排他ロックの取得を試み、解放する前に操作を完了しようとします。
  5. BEGIN EXCLUSIVEステートメントを実行すると、そのトランザクションで操作を実行する前に排他ロックが取得されます。 COMMITまたはROLLBACKはロックを解除します。
  6. Sqlite3_step、sqlite3_prepare、およびその他の呼び出しは、SQLITE_BUSYまたはSQLITE_LOCKEDを返す場合があります。 SQLITE_BUSYは通常、sqliteがロックを取得する必要があることを意味します。 2つの戻り値の最大の違い:
    • SQLITE_LOCKED:sqlite3_stepステートメントからこれを取得する場合、ステートメントハンドルでsqlite3_resetを呼び出す必要があります。これはsqlite3_stepの最初の呼び出しでのみ取得する必要があるため、リセットが呼び出されると、実際にsqlite3_step呼び出しを「再試行」できます。他の操作では、SQLITE_BUSYと同じです
    • SQLITE_BUSY:sqlite3_resetを呼び出す必要はありません。ロックが解放されるのを少し待ってから操作を再試行してください。
28
Snazzer

私はこれが古いスレッドであり、応答が良いことを理解していますが、最近これを調べていて、いくつかの異なる実装の興味深い分析に出会いました。主に、接続の共有、メッセージの受け渡し、スレッドローカル接続、接続プーリングの長所と短所について説明します。こちらをご覧ください: http://dev.yorhel.nl/doc/sqlaccess

9
adechiaro

SQLite wikiからこのコードを確認してください

私はCで似たようなことをして、コードをアップロードしました here

役に立つことを願っています。

3
Macarse

SQLiteの最新バージョンでは、デフォルトでスレッドセーフが有効になっています。 _SQLITE_THREADSAFE_ コンパイルフラグは、マルチスレッド環境で安全に動作できるようにコードをSQLiteに含めるかどうかを制御します。 デフォルトの値は_SQLITE_THREADSAFE=1_です。 Serialized mode を意味します。このモードでは:

このモード(SQLiteがSQLITE_THREADSAFE = 1でコンパイルされている場合のデフォルト)では、SQLiteライブラリ自体がデータベース接続と準備済みステートメントへのアクセスをシリアル化し、アプリケーションが同じデータベース接続または異なるスレッドで同じ準備済みステートメントを自由に使用できるようにします同時に。

sqlite3_threadsafe()関数を使用して、Sqliteライブラリ_SQLITE_THREADSAFE_コンパイルフラグを確認します。

デフォルトのライブラリスレッドセーフ動作は、sqlite3_config()を介して変更できます。個々のデータベース接続のスレッドモードを調整するには、sqlite3_open_v2()で_SQLITE_OPEN_NOMUTEX_および_SQLITE_OPEN_FULLMUTEX_フラグを使用します。

1
Nikita

概要

SQLiteのトランザクションはシリアライズ可能です。

1つのデータベース接続で行われた変更は、コミット前の他のすべてのデータベース接続からは見えません。

クエリは、それらの変更がコミットされているかどうかにかかわらず、クエリの開始前に同じデータベース接続で完了したすべての変更を確認します。

クエリの実行開始後、クエリが完了する前に同じデータベース接続で変更が発生した場合、クエリがそれらの変更を表示するかどうかは未定義です。

クエリの実行開始後、クエリが完了する前に同じデータベース接続で変更が発生した場合、クエリは変更された行を複数回返すか、以前に削除された行を返すことがあります。

前の4つの項目の目的のために、同じ共有キャッシュを使用し、PRAGMA read_uncommittedを有効にする2つのデータベース接続は、個別のデータベース接続ではなく、同じデータベース接続と見なされます。


マルチスレッドアクセスに関する上記の情報に加えて、この最初の質問と先行書き込みログの導入以来多くのことが変更されているため、 分離に関するこのページ を調べる価値があります。 ([〜#〜] wal [〜#〜])。

データベースへの複数の接続を開くハイブリッドアプローチは、適切な同時実行性の保証を提供し、マルチスレッド書き込みトランザクションを許可するという利点と新しい接続を開く費用をトレードオフします。

0
Adrian_H