元の質問
バックグラウンド
50k 挿入/秒のオーダーの挿入速度を達成するには、SQLiteを微調整する必要があることはよく知られています。ここには、挿入速度の遅さと豊富なアドバイスとベンチマークに関する多くの質問があります。
また、SQLite は大量のデータを処理できるという主張もあり、50 GB 以上のレポートでは適切な設定で問題は発生しません。
ここや他の場所のアドバイスに従ってこれらの速度を達成しましたが、35k-45k の挿入/秒に満足しています。私が抱えている問題は、すべてのベンチマークが 1m 未満のレコードでのみ高速な挿入速度を示していることです。私が見ているのは、挿入速度がテーブルサイズに反比例するように見えるということです。
問題
[x_id, y_id, z_id]
私のユース ケースでは、リンク テーブルに数年間 (100 万行/日) にわたって500m から 1b のタプル ( ) を格納する必要があります。値はすべて 1 ~ 2,000,000 の整数 ID です。に単一の索引がありますz_id
。
最初の 10m 行、最大 35k 挿入/秒のパフォーマンスは優れていますが、テーブルが最大 20m 行になるまでに、パフォーマンスが低下し始めます。現在、約 100 の挿入/秒が表示されています。
テーブルのサイズは特に大きくありません。20m 行の場合、ディスク上のサイズは約 500MB です。
プロジェクトは Perl で書かれています。
質問
これは SQLite の大きなテーブルの現実ですか、それとも 1,000 万行を超えるテーブルで高い挿入率を維持する秘訣はありますか?
可能であれば回避したい既知の回避策
- インデックスを削除し、レコードを追加して、インデックスを再作成します。これは回避策としては問題ありませんが、更新中に DB を引き続き使用する必要がある場合には機能しません。データベースをx分間/日完全にアクセス不能にすることはできません。
- テーブルをより小さなサブテーブル/ファイルに分割します。これは短期間で機能し、すでに実験しています。問題は、クエリを実行するときに履歴全体からデータを取得できるようにする必要があることです。これは、最終的に 62 個のテーブル添付ファイルの制限に達することを意味します。アタッチし、一時テーブルに結果を収集し、リクエストごとに何百回もデタッチすると、多くの作業とオーバーヘッドが発生するようですが、他に選択肢がない場合は試してみます。
- セット
SQLITE_FCNTL_CHUNK_SIZE
: 私は C (?!) を知らないので、これをやり遂げるためだけに C を学ばない方がいいと思います。ただし、Perl を使用してこのパラメーターを設定する方法がわかりません。
アップデート
大規模なデータセットを処理できるという SQLite の主張にもかかわらず、インデックスが挿入時間をますます遅くしているというティムの提案に従って、次の設定でベンチマーク比較を実行しました。
- 挿入された行: 1,400 万
- commit バッチ サイズ: 50,000 レコード
cache_size
プラグマ: 10,000page_size
プラグマ: 4,096temp_store
プラグマ:メモリjournal_mode
プラグマ:削除synchronous
プラグマ:オフ
私のプロジェクトでは、以下のベンチマーク結果のように、ファイルベースの一時テーブルが作成され、CSV データをインポートするための SQLite の組み込みサポートが使用されます。次に、一時テーブルが受信側データベースにアタッチされ、
insert-select
ステートメントを使用して 50,000 行のセットが挿入されます。したがって、挿入時間は、
ファイルからデータベースへの挿入時間ではなく、テーブルからテーブルへの挿入速度を反映しています。CSV のインポート時間を考慮すると、速度が 25 ~ 50% 低下します (非常に大まかな見積もりで、CSV データのインポートにそれほど時間はかかりません)。
明らかにインデックスがあると、テーブル サイズが大きくなるにつれて挿入速度が遅くなります。
上記のデータから、 SQLite では処理できないという主張ではなく、正しい答えをTim の答えに割り当てることができることは明らかです。そのデータセットのインデックス作成がユースケースの一部でない場合、明らかに大規模なデータセットを処理できます。しばらくの間、ロギング システムのバックエンドとして SQLite を使用してきましたが、これはインデックスを作成する必要がありません。
結論
SQLiteを使用して大量のデータを保存し、インデックスを付けたいと考えている場合は、シャードを使用することが解決策になる可能性があります。最終的に、MD5 ハッシュの最初の 3 文字を使用して一意の列をz
作成し、4,096 のデータベースの 1 つへの割り当てを決定することにしました。私のユースケースは本質的にアーカイブであるため、スキーマは変更されず、クエリでシャード ウォーキングが必要になることはありません。非常に古いデータは削減され、最終的には破棄されるため、データベースのサイズには制限があります。したがって、シャーディング、プラグマ設定、および一部の非正規化のこの組み合わせにより、上記のベンチマークに基づいて挿入速度を維持する適切なバランスが得られます。少なくとも 1 万回の挿入/秒。