非常に大きなデータベースファイルを使用するsqliteのパフォーマンス特性は何ですか?[閉まっている]


325

sqliteは、サポートされている場合でも、非常に大きなデータベースファイルでは十分に機能しないことを知っています(1 GBを超えるファイルサイズが必要な場合は、エンタープライズrdbmsの使用を検討することをお勧めします。もう見つかりません。古いバージョンのsqliteに関連している可能性があります)。

しかし、私の目的のために、他の解決策を検討する前に、それが実際にどれほど悪いかを理解したいと思います。

私は2GBから数ギガバイトの範囲のsqliteデータファイルについて話しています。誰でもこれについて何か経験がありますか?ヒント/アイデアはありますか?


1
スレッド(スレッドあたりの接続)を使用すると、読み取るためだけに役立つかもしれない- stackoverflow.com/a/24029046/743263
malkia


23
2016年:SQLiteで問題なく動作する5 GBのデータベースがあります。まったく同じデータセットをPostgresにインストールしました。SQLiteは2.7ミリ秒、Postgresは2.5ミリ秒で複雑なクエリを実行しました。Reggreへのアクセスが簡単になり、インデックス機能が向上したため、結局Postgresを使いました。しかし、私はSQLiteに感銘を受け、それを使用することもできました。
Paulb 2017

回答:


246

そのため、非常に大きなファイルに対してsqliteを使用していくつかのテストを行い、いくつかの結論に達しました(少なくとも私の特定のアプリケーションについて)。

テストには、単一のテーブルまたは複数のテーブルのいずれかを含む単一のsqliteファイルが含まれます。各テーブルには約8列、ほとんどすべての整数、および4つのインデックスがありました。

アイデアは、sqliteファイルが約50GBになるまで十分なデータを挿入することでした。

シングルテーブル

テーブルを1つだけ含む複数の行をsqliteファイルに挿入しようとしました。ファイルが約7GBの場合(申し訳ありませんが、行数を特定することはできません)、挿入に時間がかかりすぎていました。すべてのデータを挿入するテストには24時間程度かかると推定していましたが、48時間後でも完了しませんでした。

これにより、単一の非常に大きなsqliteテーブルには挿入に関する問題があり、おそらく他の操作にも問題があると結論付けました。

テーブルが大きくなり、すべてのインデックスの挿入と更新に時間がかかるため、これは驚くべきことではないと思います。

複数のテーブル

次に、1日あたり1つのテーブルで、いくつかのテーブルにデータを時間で分割してみました。元の1つのテーブルのデータは、約700のテーブルに分割されました。

この設定では挿入に問題はありませんでした。毎日新しいテーブルが作成されるので、時間が経過してもそれほど長くはかかりませんでした。

真空の問題

i_like_caffeineで指摘されているように、VACUUMコマンドは、sqliteファイルが大きくなるほど問題になります。より多くの挿入/削除が行われると、ディスク上のファイルの断片化が悪化するため、目標は定期的にVACUUMを実行してファイルを最適化し、ファイル領域を回復することです。

ただし、ドキュメントで指摘されているように、データベースの完全なコピーはバキュームを行うために作成され、完了するまでに非常に長い時間がかかります。したがって、データベースが小さいほど、この操作は速く終了します。

結論

私の特定のアプリケーションでは、バキュームパフォーマンスと挿入/削除速度の両方を最大限に活用するために、データを複数のdbファイルに分割し、1日に1つにするでしょう。

これはクエリを複雑にしますが、私にとって、これだけのデータにインデックスを付けることができるのは価値のあるトレードオフです。もう1つの利点は、dbファイル全体を削除して、1日分のデータ(アプリケーションの一般的な操作)を削除できることです。

速度が問題になる時期を確認するには、おそらくファイルごとのテーブルサイズも監視する必要があります。

自動バキューム以外にインクリメンタルバキューム法がないように見えるのは残念です。私のバキュームの目標はファイルをデフラグすることです(ファイルスペースはそれほど重要ではありません)。これは、自動バキュームではできません。実際、ドキュメントには断片化が悪化する可能性があると記載されているため、定期的にファイルを完全に掃除する必要があります。


5
非常に役立つ情報。純粋な推測ですが、新しいバックアップAPIを使用してデータベースの断片化されていないバージョンを毎日作成し、VACUUMを実行する必要を回避できるかどうか疑問に思います。
eodonohoe 2009年

24
好奇心旺盛ですが、すべてのINSERTはトランザクションでしたか?
ポールルフェーブル

9
はい、挿入はトランザクションごとに10000メッセージのバッチで行われました。
Snazzer、2009年

6
どのファイルシステムを使用しましたか?ext {2,3,4}の場合、data =設定は何でしたか、ジャーナリングは有効でしたか?ioパターンの他に、sqliteがディスクにフラッシュする方法が重要になる場合があります。
東武

5
私は主にWindowsでテストしていたので、Linuxでの動作についてコメントすることはできません。
スナッツァ

169

プラットフォームでは50 GB以上のDBSを使用しています。文句はありません。すべてが正しく行われていることを確認してください。定義済みステートメントを使用していますか?* SQLITE 3.7.3

  1. 取引
  2. 事前の発言
  3. これらの設定を適用します(DBを作成した直後)

    PRAGMA main.page_size = 4096;
    PRAGMA main.cache_size=10000;
    PRAGMA main.locking_mode=EXCLUSIVE;
    PRAGMA main.synchronous=NORMAL;
    PRAGMA main.journal_mode=WAL;
    PRAGMA main.cache_size=5000;

これが他の人に役立つことを願って、ここでうまく機能します


22
最近160GBの範囲のdbsでテストされ、同様にうまく機能します。
スナッツァ

10
またPRAGMA main.temp_store = MEMORY;
Vikrant Chaudhary、2011年

40
@アレックス、なぜ2つのPRAGMA main.cache_size = 5000;があるのですか?
ジャック

23
これらの最適化を盲目的に適用するだけではありません。特に、synchronous = NORMALはクラッシュセーフではありません。つまり、適切なタイミングでプロセスがクラッシュすると、ディスク障害がない場合でもデータベースが破損する可能性があります。sqlite.org/pragma.html#pragma_synchronous
mpm

22
@アレックスあなたはそれらの値とそれらとデフォルトの値の違いを説明できますか?
4m1nh4j1 2014

65

最大3.5GBのSQLiteデータベースを作成しましたが、目立ったパフォーマンスの問題はありません。私が正しく覚えていれば、SQLite2にはいくつかの下限があった可能性があると思いますが、SQLite3にはそのような問題はないと思います。

SQLiteの制限ページによると、各データベースページの最大サイズは32Kです。また、データベースの最大ページ数は1024 ^ 3です。したがって、私の計算では、最大サイズとして32テラバイトになります。SQLiteに到達する前に、ファイルシステムの限界に到達すると思います。


3
実行している操作に応じて、8G sqliteデータベースで3000行を削除しようとすると、フレンチプレスの素敵なポットを醸造するのに十分な時間がかかりますlol
benjaminz

4
@benjaminz、あなたはそれを間違っているに違いない。1つのトランザクションで3k行の削除をラップする場合、それはほぼ瞬時に行われるはずです。私自身もこの間違いがありました。1万行を1つずつ削除するのに30分かかりました。しかし、すべての削除ステートメントを1つのトランザクションにまとめると、5秒かかりました。
mvp

55

挿入に48時間を超える時間がかかった理由の多くは、インデックスが原因です。それは信じられないほど高速です:

1-すべてのインデックスを削除2-すべての挿入を実行3-インデックスを再度作成


23
それはよく知られています...しかし、実行時間の長いプロセスでは、インデックスを再構築するために定期的にインデックスを削除することはありません。これは、sqlite dbを最初から再構築する必要がある場合、すべての挿入が完了した後にインデックスが作成されるアプローチです。
Snazzer、

24
@Snazzerは、同様の状況で「アキュムレータ」テーブルを使用しました。1日に1回、蓄積された行をアキュムレータテーブルからメインテーブルに1つのトランザクション内で移動します。必要に応じて、ビューは両方のテーブルを単一のテーブルとして表示することを担当しました。
CAFxX 2012年

4
別のオプションは、インデックスを保持することですが、データを挿入する前に、インデックス順にデータを事前にソートします。
Steven Kryskalla 2014

1
@StevenKryskallaインデックスを削除して再作成するのと比べてどうですか?あなたが知っている、ベンチマークのあるリンクはありますか?
mcmillab

1
@mcmillabこれは何年も前のことなので、すべての詳細やベンチマーク統計を覚えていませんが、直感的に考えると、N個のランダムに並べられた要素をインデックスに挿入するとO(NlogN)時間かかり、N個のソートされた要素を挿入するとO(N )時間。
Steven Kryskalla

34

通常の推奨に加えて:

  1. 一括挿入のインデックスを削除します。
  2. 大規模なトランザクションでのバッチ挿入/更新。
  3. バッファキャッシュを調整する/ジャーナルを無効にする/ PRAGMAを無効にする。
  4. 64ビットマシンを使用します(大量のキャッシュ™を使用できるようにするため)。
  5. [2014年7月追加] 複数のSQLクエリを実行する代わりに、共通テーブル式(CTE)を使用してください!SQLiteリリース3.8.3が必要です。

SQLite3の使用経験から次のことを学びました。

  1. 挿入速度を最大にするために、列制約のあるスキーマは使用しないでください。(必要に応じて後でテーブルを変更する ALTER TABLEで制約を追加することはできません)。
  2. スキーマを最適化して、必要なものを保存します。これは、データベースに挿入する前に、テーブルを分解したり、データを圧縮/変換したりすることを意味する場合があります。良い例は、IPアドレスを(長い)整数として格納することです。
  3. dbファイルごとに1つのテーブル-ロックの競合を最小限に抑えるため。(単一の接続オブジェクトが必要な場合は、ATTACH DATABASEを使用してください。
  4. SQLiteは同じ列にさまざまな種類のデータを格納できます(動的型付け)。

質問/コメント歓迎。;-)


1
「dbファイルごとに1つのテーブル」からどの程度の影響がありますか?面白そう。テーブルにテーブルが3つしかなく、ゼロから構築されている場合、それは重要だと思いますか?
Martin Velez、

4
@マーティンはそれを言うのが嫌いですが、答えはそれが依存するということです。アイデアは、データを管理可能なサイズに分割することです。私の使用例では、さまざまなホストからデータを収集し、事後のデータについてレポートを作成しているため、このアプローチはうまく機能しました。他の人が提案した日付/時間によるパーティションは、私が想像する長い期間にわたるデータに対してはうまく機能するはずです。
Lester Cheung

3
@レスターチャン:2番目の#1について:今日まで、SQLite3は、テーブルの作成後、ALTER TABLEを使用した制約の追加をサポートしていないというドキュメントと個人的な経験からの私の理解です。既存のテーブル行に制約を追加または削除する唯一の方法は、目的の特性を持つ新しいテーブルを作成してすべての行をコピーすることです。これは、制約付きで1回挿入するよりもはるかに遅い可能性があります。
Mumbleskates

3
@Widdershinsあなたは絶対的に正しいです-SQLiteのALTER TABLEでは制約を追加できません。私は何を吸っていたのか分かりません-答えを更新します-ありがとう。
Lester Cheung、2016年

これらの提案はどれも、膨大なSQLite dbファイルの使用とは関係ありません。この回答が送信されてから質問は編集されましたか?
A. Rager、

9

sqliteスケーリングに関する主な不満は次のとおりです。

  1. 単一プロセス書き込み。
  2. ミラーリングなし。
  3. 複製なし。

9

7GBのSQLiteデータベースがあります。内部結合を使用して特定のクエリを実行するには2.6秒かかります。これを高速化するために、インデックスを追加してみました。追加したインデックスに応じて、クエリが0.1秒に低下したり、7秒​​に上昇したりしました。私の場合の問題は、列が非常に重複している場合にインデックスを追加すると、パフォーマンスが低下することでした:


9
重複が多い列でパフォーマンスが低下するのはなぜですか(深刻な質問)?
マーティンベレス

6
カーディナリティの低い列は、インデックスに困難です:stackoverflow.com/questions/2113181/...
Metrixの

9

SQLiteのドキュメントには、データベースファイルの実際のサイズ制限は数十GBであるという記述がありました。これは主に、トランザクションを開始するたびにSQLiteが「ダーティページのビットマップを割り当てる」必要があるためです。したがって、データベースの各MBに256バイトのRAMが必要でした。50 GBのDBファイルに挿入するには、大きな(2 ^ 8)*(2 ^ 10)= 2 ^ 18 = 256 MBのRAMが必要です。

しかし、SQLiteの最近のバージョンでは、これは不要になりました。詳細はこちら


25
私は、このアウトを指すように持っていることを非常に残念だけど、2^18実際には唯一の256 Kである
ガブリエル・シュライバー

7
@GabrielSchreiberそれと、50GBが(2 ^ 10)MBではないという事実、それはたった1GBです。したがって、50 GBのデータベースの場合、12.5 MBのメモリが必要です。(2 ^ 8)*(2 ^ 10)* 50
elipoultorak

8

私は、vacuumコマンドを使用するときに、大きなsqliteファイルで問題を経験しました。

まだauto_vacuum機能を試していません。データを頻繁に更新および削除することが予想される場合、これは一見の価値があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.