SQLite同時アクセス


177

SQLite3は、同じDBから読み書きする複数のプロセスによる同時アクセスを安全に処理しますか?それにプラットフォームの例外はありますか?


3
私は賞金目標について言及するのを忘れていました:ほとんどの回答は大丈夫だと言っています:「SQLiteは十分に高速です」、「SQLiteは並行性をうまく処理します」など正確に同時に到着します(理論的に非常にまれなケース)。1)エラーが発生してプログラムが中断されますか?または2)最初の書き込み操作が完了するまで、2番目の書き込み操作は待機しますか?または3)書き込み操作の1つが破棄されますか(データ損失!)?4)他に何かありますか?同時書き込みの制限を知ることは、多くの状況で役立ちます。
Basj

7
@Basjつまり、2)待機して数回再試行します(構成可能)、1)エラーをトリガーし、SQLITE_BUSY.3)SQLITE_BUSYエラーを処理するコールバックを登録できます。
2018年

回答:


112

これらの同時アクセスのほとんどが読み取り(SELECTなど)である場合、SQLiteはそれらを非常にうまく処理できます。ただし、同時に書き込みを開始すると、ロックの競合が問題になる可能性があります。SQLiteエンジン自体は非常に高速であり、競合を最小限に抑えるために多くの巧妙な最適化が行われているため、ファイルシステムの速度に大きく依存します。特にSQLite 3。

ほとんどのデスクトップ/ラップトップ/タブレット/電話アプリケーションでは、同時実行性が十分でないため、SQLiteは十分に高速です。(FirefoxはSQLiteをブックマーク、履歴などに幅広く使用しています)

サーバーアプリケーションの場合、ある日以前に、典型的なシナリオ(ブログ、フォーラムなど)では、1日あたり10万ページ未満のページビューであればSQLiteデータベースで完全に処理できると誰かが言っていました。実際、最新のディスクとプロセッサを使用すると、95%のWebサイトとWebサービスがSQLiteで正常に動作します。

本当に高速な読み取り/書き込みアクセスが必要な場合は、インメモリSQLiteデータベースを使用してください。RAMはディスクより数桁高速です。


16
OPは効率と速度についてではなく、同時アクセスについて質問します。Webサーバーはそれとは何の関係もありません。メモリデータベースについても同様です。
Jarekczek 2017

1
あなたはある程度正しいですが、効率/速度は役割を果たします。より高速なアクセスは、ロックの待機に費やされる時間が短くなることを意味し、それによりSQLiteの同時実行パフォーマンスの欠点が軽減されます。特に、書き込みが少なく高速である場合、DBはユーザーにとって同時実行性の問題をまったく持っていないように見えます。
Caboose

1
インメモリsqliteデータベースへの同時アクセスをどのように管理しますか?
P-Gn

42

はい、SQLiteは並行性を適切に処理しますが、パフォーマンスの観点からは最善ではありません。私が言うことができることから、それに例外はありません。詳細はSQLiteのサイトにあります:https : //www.sqlite.org/lockingv3.html

このステートメントは興味深いものです。「ページャーモジュールは、変更がすべて同時に発生するか、すべての変更が発生しないか、どれも発生しないこと、2つ以上のプロセスが同時に互換性のない方法でデータベースにアクセスしようとしないことを確認します」


2
ここでは、さまざまなプラットフォーム、つまりNFSファイルシステムとWindowsでの問題についてのコメントをいくつか示します(ただし、これはWindowsの古いバージョンにのみ関係する可能性があります...)
Nate

1
PHPのすべてのユーザーが使用するRAMにSQLite3データベースをロードすることは可能ですか?私はそれが手続きされた状態で何を推測しないよ
Anon343224user

1
@foxyfennec ..開始点ですが、SQLiteはこの使用例に最適なデータベースではない可能性があります。sqlite.org/inmemorydb.html
kingPuppy

37

はい、そうです。理由を理解しましょう

SQLiteはトランザクション対応です

SQLiteの単一トランザクション内のすべての変更は完全に発生するか、まったく発生しない

このようなACIDサポートと同時の読み取り/書き込みは、2つの方法で提供されます。いわゆるジャーナリング(「古い方法」と呼びます)または先読みログ(「新しい方法」と呼びます)を使用します。

ジャーナリング(旧式)

このモードでは、SQLiteはDATABASE-LEVEL ロックを使用します。これは理解すべき重要なポイントです。

つまり、何かを読み書きする必要があるときは常に、最初にENTIREデータベースファイルのロックを取得します。複数のリーダーが共存し、並行して何かを読むことができます

書き込み中は、排他ロックが取得され、他のロックは取得されませんプロセスが同時に読み取り/書き込みを行っていないため、書き込みは安全です。

これが理由です、ここで彼らはSQLiteの実装言っているシリアル化可能トランザクションを

トラブル

毎回データベース全体をロックする必要があり、誰もが書き込みの同時実行を処理するプロセスを待機するため、このような同時書き込み/読み取りのパフォーマンスはかなり低い

ロールバック/停止

データベースファイルに何かを書き込む前に、SQLiteはまず変更するチャンクを一時ファイルに保存します。データベースファイルへの書き込み中にクラッシュが発生した場合、この一時ファイルが取得され、変更が元に戻されます。

先行書き込みロギングまたはWAL(新しい方法)

この場合、すべての書き込みは一時ファイルに追加されます(先書きログ)にこのファイルは定期的に元のデータベースとマージされます。SQLiteが何かを検索するとき、最初にこの一時ファイルをチェックし、何も見つからない場合はメインデータベースファイルを続行します。

その結果、リーダーはライターと競合せず、パフォーマンスはオールドウェイと比較してはるかに優れています。

注意事項

SQliteは基盤となるファイルシステムのロック機能に大きく依存しているため、注意して使用する必要があります。詳細はこちら

また、特にジャーナルモードでデータベースがロックされてエラーが発生する可能性が高いため、このエラーを考慮してアプリを設計する必要があります。


34

WAL(ログ先行書き込み)モードについて言及した人はいません。トランザクションが適切に編成され、WALモードがオンになっていることを確認してください。更新が行われている間、人々が物事を読んでいる間、データベースをロックしておく必要はありません。

唯一の問題は、ある時点でWALをメインデータベースに再統合する必要があることです。これは、データベースへの最後の接続が閉じたときに行われます。非常にビジーなサイトでは、すべての接続が閉じるまでに数秒かかる場合がありますが、1日あたり10万ヒットは問題になりません。


興味深いですが、ネットワーク上でデータベースにアクセスするシナリオではなく、単一のマシンでのみ機能します。
Bobík

ライターが待機するデフォルトのタイムアウトは5秒であり、その後database is lockedエラーがライターによって発生することは言及する価値がある
mirhossein

16

2019年には、2つの新しい同時書き込みオプションがまだリリースされていませんが、別々のブランチで利用できます。

「PRAGMA journal_mode = wal2」

通常の "wal"モードに対するこのジャーナルモードの利点は、ライターが1つのwalファイルへの書き込みを続行し、他のファイルがチェックポイントされていることです。

BEGIN CONCURRENT-詳細なドキュメントへのリンク

BEGIN CONCURRENTの強化により、データベースが「wal」または「wal2」モードの場合、複数のライターが書き込みトランザクションを同時に処理できますが、システムはまだCOMMITコマンドをシリアル化しています。

"BEGIN CONCURRENT"で書き込みトランザクションが開かれると、データベースの実際のロックはCOMMITが実行されるまで延期されます。つまり、BEGIN CONCURRENTで開始されたトランザクションはいくつでも同時に進行できます。システムは、オプティミスティックページレベルロックを使用して、競合する並行トランザクションがコミットされるのを防ぎます。

一緒に、それらはbegin-concurrent-wal2またはそれぞれ別の独自のブランチに存在します。


1
これらの機能がリリースバージョンに組み込まれる時期はわかりますか?彼らは本当に私にとって重宝するでしょう。
Peter Moore

2
わからない 枝から簡単に構築できます。.NETの場合、低レベルのインターフェースとWAL2 +同時開始+ FTS5を備えたライブラリがあります:github.com/Spreads/Spreads.SQLite
VB

ああ、確かにありがとう。安定性についてもっと気になります。SQLiteはリリースに関しては非常に優れていますが、実稼働コードでブランチを使用することがどれほど危険かはわかりません。
Peter Moore

2
このスレッドgithub.com/Expensify/Bedrock/issues/65および一般的なBedrockを参照してください。彼らはそれをプロダクションで使用し、それをプッシュしましたbegin concurrent
VB

13

SQLiteには、データベースレベルでリーダー/ライターロックがあります。複数の接続(異なるプロセスが所有している可能性があります)は、同じデータベースから同時にデータを読み取ることができますが、データベースに書き込むことができるのは1つだけです。

SQLiteは無制限の数の同時リーダーをサポートしますが、同時に使用できるライターは1つだけです。多くの場合、これは問題ではありません。ライターがキューに入りました。各アプリケーションは、データベースの作業をすばやく実行して続行します。ロックは数十ミリ秒以上続きません。しかし、より多くの同時実行性を必要とするアプリケーションがいくつかあり、それらのアプリケーションは別のソリューションを探す必要があるかもしれません。- SQLiteの@ SQLite.orgのための適切な使用方法

読み取り/書き込みロックは、独立したトランザクション処理を可能にし、データベースレベルで排他ロックと共有ロックを使用して実装されます。

接続がデータベースに対して書き込み操作を実行する前に、排他ロックを取得する必要があります。排他ロックが取得されると、他の接続からの読み取り操作と書き込み操作の両方が、ロックが再び解放されるまでブロックされます。

同時書き込みの場合の実装の詳細

SQLiteには、最大同時実行性を保証するために、書き込み操作中にデータベースをできるだけ遅くロックするのに役立つロックテーブルがあります。

初期状態はUNLOCKEDであり、この状態では、接続はまだデータベースにアクセスしていません。プロセスがデータベースに接続され、トランザクションがBEGINで開始された場合でも、接続はまだUNLOCKED状態です。

UNLOCKED状態の後、次の状態はSHARED状態です。データベースからデータを読み取ることができる(書き込むことはできない)ためには、SHAREDロックを取得して、接続が最初にSHARED状態に入る必要があります。複数の接続がSHAREDロックを同時に取得および維持できるため、複数の接続が同じデータベースから同時にデータを読み取ることができます。ただし、1つのSHAREDロックのみが解放されないままである限り、どの接続もデータベースへの書き込みを正常に完了できません。

接続がデータベースへの書き込みを希望する場合、最初にRESERVEDロックを取得する必要があります。

一度にアクティブにできるのは1つのRESERVEDロックだけですが、複数のSHAREDロックは1つのRESERVEDロックと共存できます。RESERVEDがPENDINGと異なるのは、RESERVEDロックがあるときに新しいSHAREDロックを取得できる点です。- ファイルロックと同時実行でSQLiteのバージョン3 @ SQLite.org

接続がRESERVEDロックを取得すると、データベース変更操作の処理を開始できますが、これらの変更は実際にディスクに書き込まれるのではなく、バッファーでのみ実行できます。読み出しコンテンツに加えられた変更は、メモリバッファに保存されます。接続が変更(またはトランザクション)を送信する場合、RESERVEDロックをEXCLUSIVEロックにアップグレードする必要があります。ロックを取得するには、まずロックを持ち上げて保留中のロックにする必要があります。

PENDINGロックは、ロックを保持しているプロセスができるだけ早くデータベースに書き込むことを望んでおり、現在のすべてのSHAREDロックがクリアされるのを待っているため、排他ロックを取得できることを意味します。PENDINGロックがアクティブな場合、データベースに対する新しいSHAREDロックは許可されませんが、既存のSHAREDロックは続行できます。

データベースファイルに書き込むには、排他ロックが必要です。ファイルで許可される排他ロックは1つだけで、他のどのロックも、排他ロックと共存することはできません。同時実行性を最大化するために、SQLiteはEXCLUSIVEロックが保持される時間を最小化するように機能します。- ファイルロックと同時実行でSQLiteのバージョン3 @ SQLite.org

したがって、SQLiteがサポートしていないという理由だけで、同じDBに書き込む複数のプロセスによる同時アクセスをSQLiteが安全に処理すると言えるかもしれません。あなたは得るでしょうSQLITE_BUSYSQLITE_LOCKED、それが再試行制限を打つときに、第2の作家のため。


ありがとうございました。2人のライターによるコードの例は、それがどのように機能するかを理解するのに非常に優れています。
Basj 2018年

1
@Basjつまり、sqliteはデータベースファイルに読み取り/書き込みロックを持っています。これは、ファイルへの同時書き込みと同じです。WALを使用すると、同時書き込みはできませんが、WALは書き込みを高速化でき、読み取りと書き込みを同時に実行できます。WALは、WAL_READ_LOCK、WAL_WRITE_LOCKのような新しいロックを導入します。
18年

2
キューを使用して、複数のスレッドがキューにフィードし、1つのスレッドだけがキュー内のSQLステートメントを使用してDBに書き込むことができますか?この
ガブリエル

7

このスレッドは古いですが、sqliteで実行したテストの結果を共有するのは良いことだと思います。EXCLUSIVEロックとタイムアウトに設定されたトランザクション内で、ステートメントSELECTとUPDATE sqlコマンドを実行するPythonプログラムの2つのインスタンスを実行しました(異なるプロセスは同じプログラム)。ロックを取得するのに10秒かかり、結果は苛立たしいものでした。すべてのインスタンスは10000ステップループで実行されました。

  • 排他ロックでデータベースに接続する
  • 1つの行を選択してカウンターを読み取ります
  • 1ずつ増加するカウンターに等しい新しい値で行を更新する
  • dbへの接続を閉じる

sqliteがトランザクションに排他ロックを許可した場合でも、実際に実行されたサイクルの合計数は20 000以下ではありませんでした(両方のプロセスでカウントされた単一カウンターの合計反復数)。Pythonプログラムはほとんど単一の例外をスローしませんでした(20回の実行の選択中に1回だけ)。テスト時のsqliteリビジョンは3.6.20で、Python v3.3 CentOS 6.5でした。私の意見では、この種の仕事のためのより信頼できる製品を見つけるか、sqliteへの書き込みを単一の一意のプロセス/スレッドに制限する方が良いです。


5
ここで説明されているように、Pythonでロックを取得するには、いくつかの魔法の言葉を言う必要があるようです。stackoverflow.com/ a / 12848059/1048959 これは、python sqliteのドキュメントwith conで十分だと信じ込ませているにもかかわらずです。
Dan Stahlke、2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.