web-dev-qa-db-ja.com

Python sqlite3と並行性

「スレッド」モジュールを使用するPythonプログラムがあります。1秒ごとに、プログラムはWebからデータを取得する新しいスレッドを開始し、このデータをハードドライブに保存します。これらの結果を保存するためにsqlite3を使用したいのですが、動作させることができません。問題は次の行にあるようです:

conn = sqlite3.connect("mydatabase.db")
  • このコード行を各スレッド内に配置すると、データベースファイルがロックされていることを示すOperationalErrorが表示されます。これは、別のスレッドがsqlite3接続を介してmydatabase.dbを開いてロックしていることを意味すると思います。
  • このコード行をメインプログラムに配置し、接続オブジェクト(conn)を各スレッドに渡すと、プログラミングエラーが発生します。スレッドで作成されたSQLiteオブジェクトは、同じスレッドでのみ使用できるということです。

以前は、すべての結果をCSVファイルに保存していましたが、これらのファイルロックの問題はありませんでした。これがsqliteで可能になることを願っています。何か案は?

78
RexE

コンシューマープロデューサーパターンを使用できます。たとえば、スレッド間で共有されるキューを作成できます。 Webからデータを取得する最初のスレッドは、このデータを共有キューに入れます。データベース接続を所有する別のスレッドは、キューからデータをデキューし、データベースに渡します。

39
Evgeny Lazin

一般的な考えに反して、sqlite3の新しいバージョンdoは、複数のスレッドからのアクセスをサポートします。

これは、オプションのキーワード引数check_same_threadを使用して有効にできます。

sqlite.connect(":memory:", check_same_thread=False)
168
Jeremiah Rose

以下は mail.python.org.pipermail.1239789 にあります

解決策を見つけました。なぜpythonドキュメンテーションにこのオプションに関する単語が1つもないので、接続関数に新しいキーワード引数を追加する必要があり、そこからカーソルを作成できるようになります。異なるスレッド。

sqlite.connect(":memory:", check_same_thread = False)

私にとって完璧に機能します。もちろん、今後はdbへの安全なマルチスレッドアクセスに注意する必要があります。とにかく助けようとするためのすべてのthx。

16
Robert Krolik

これにはスレッドを使用しないでください。これは、 twisted の簡単なタスクであり、とにかく大幅にさらに先に進むでしょう。

1つのスレッドのみを使用し、要求の完了によりイベントをトリガーして書き込みを実行します。

twistedはスケジューリング、コールバックなどを処理します。結果全体を文字列として渡すか、ストリームプロセッサを介して実行できます( Twitter APIfriendfeed API が両方とも起動します)結果がまだダウンロードされているため、発信者にイベントが送信されます)。

データで何をしているのかに応じて、完全な結果を完全にsqliteにダンプするか、クックしてダンプするか、読み取り中にクックして最後にダンプすることができます。

私はあなたがgithubで欲しいものに近い何かをする非常にシンプルなアプリケーションを持っています。 pfetch (並列フェッチ)と呼びます。スケジュールに従ってさまざまなページを取得し、結果をファイルにストリーミングし、オプションで各ページが正常に完了したらスクリプトを実行します。また、条件付きGETのような派手な機能も実行しますが、それでもあなたがしていることの良い基盤になるでしょう。

13
Dustin

multiprocessing に切り替えます。複数のCPUを使用することで、複数のコアを使用するだけでなく、はるかに優れた拡張性があり、インターフェイスはpython threading module。

または、ALiが提案したように、単に SQLAlchemyのスレッドプーリングメカニズム を使用します。それはあなたのためにすべてを自動的に処理し、それらのいくつかを引用するだけで多くの追加機能を備えています:

  1. SQLAlchemyには、SQLite、Postgres、MySQL、Oracle、MS-SQL、Firebird、MaxDB、MS Access、Sybase、およびInformixの方言が含まれています。 IBMはDB2ドライバーもリリースしました。したがって、SQLiteから移行することにした場合、アプリケーションを書き直す必要はありません。
  2. SQLAlchemyのオブジェクトリレーショナルマッパー(ORM)の中心部分である作業単位システムは、保留中の作成/挿入/更新/削除操作をキューに編成し、それらをすべて1つのバッチでフラッシュします。これを達成するために、外部キー制約を尊重するために、キュー内のすべての変更されたアイテムのトポロジカル「依存性ソート」を実行し、冗長ステートメントをグループ化してさらにバッチ処理できる場合があります。これにより、最大限の効率とトランザクションの安全性が得られ、デッドロックの可能性が最小限に抑えられます。
12
nosklo

または、私のように怠け者の場合は、 SQLAlchemy を使用できます。それはあなたのためにスレッディングを処理します、( スレッドローカル、およびいくつかの接続プーリングを使用して )、そしてそれをする方法は 設定可能 です。

追加のボーナスとして、同時アプリケーションにSqliteを使用することが災害になると気づいた場合、MySQLやPostgresなどを使用するためにコードを変更する必要はありません。ただ切り替えることができます。

7
Ali Afshar

このエラーの原因となるマルチスレッドで同じカーソルを使用しないで同じスレッドで同じカーソルを使用するには、データベースに対してevery transactionの後にsession.close()を使用する必要があります。

2
Hazem Khaled

私はEvgenyの答えが好きです-通常、キューはスレッド間通信を実装する最良の方法です。完全を期すために、他のオプションを次に示します。

  • 生成されたスレッドが使用を終了したら、DB接続を閉じます。これはOperationalErrorを修正しますが、パフォーマンスのオーバーヘッドにより、このような接続のオープンとクローズは一般的にNo-Noです。
  • 子スレッドを使用しないでください。 1秒間に1回のタスクが適度に軽量であれば、フェッチとストアを実行し、適切なタイミングまでスリープすることで問題を回避できます。これは、フェッチおよびストア操作に1秒以上かかる可能性があるため望ましくありません。また、マルチスレッドアプローチでは、リソースの多重化のメリットが失われます。
0
James Brady

threading.Lock() を使用します

0
Alexandr

Scrapy は私の質問に対する潜在的な答えのようです。そのホームページは私の正確なタスクを説明しています。 (コードの安定性はまだわかりませんが。)

0
RexE

プログラムの並行性を設計する必要があります。 SQLiteには明確な制限があり、それらに従う必要があります。 [〜#〜] faq [〜#〜] (次の質問も参照)を参照してください。

0
iny

上記の回答のいずれにもベンチマークが見つからなかったため、すべてをベンチマークするテストを作成しました。

3つのアプローチを試しました

  1. SQLiteデータベースからの順次読み取りと書き込み
  2. ThreadPoolExecutorを使用して読み取り/書き込みを行う
  3. ProcessPoolExecutorを使用して読み取り/書き込みを行う

ベンチマークからの結果と要点は次のとおりです。

  1. 順次読み取り/順次書き込みが最適に機能する
  2. 並列処理する必要がある場合は、ProcessPoolExecutorを使用して並列に読み取ります
  3. ThreadPoolExecutorまたはProcessPoolExecutorを使用して書き込みを実行しないでください。データベースロックエラーが発生し、チャンクの挿入を再試行する必要があります。

My SO answer [〜#〜] here [〜#〜] で、ベンチマークのコードと完全なソリューションを見つけることができます。

0
PirateApp

Y_serial Pythonデータ永続化のためのモジュール: http://yserial.sourceforge.net

単一のSQLiteデータベースを取り巻くデッドロックの問題を処理します。並行性への要求が重くなった場合、多くのデータベースのクラスFarmを簡単に設定して、確率的な時間で負荷を分散させることができます。

これがあなたのプロジェクトに役立つことを願っています...それは10分で実装するのに十分簡単であるべきです。

0
code43