web-dev-qa-db-ja.com

インデックスが原因でレコード数が増えると、SQLiteの挿入速度が低下する

元の質問

バックグラウンド

SQLite 微調整が必​​要 で、挿入速度が50k挿入/秒程度になることはよく知られています。ここには、挿入速度が遅いことと、豊富なアドバイスとベンチマークに関する多くの質問があります。

SQLiteが大量のデータを処理できるという主張 もあり、50 GBを超えるレポートは正しい設定で問題を引き起こしません。

私はこれらの速度を達成するために、ここや他の場所でのアドバイスに従いましたが、35k-45kの挿入に満足しています。私が抱えている問題は、すべてのベンチマークが1m未満のレコードで高速の挿入速度を示すことです。私が見ているのは、挿入速度がテーブルサイズに反比例しているようだということです。

問題

私のユースケースでは、リンクテーブルに500mから1bのタプル([x_id, y_id, z_id])を数年(1m行/日)にわたって保存する必要があります。値はすべて1〜2,000,000の整数IDです。 z_idには単一のインデックスがあります。

パフォーマンスは最初の10m行(〜35,000挿入/秒)で優れていますが、テーブルが〜20m行になるまでにパフォーマンスが低下し始めます。現在、約100の挿入/秒が表示されています。

テーブルのサイズは特に大きくありません。 20m行の場合、ディスク上のサイズは約500MBです。

プロジェクトはPerlで書かれています。

質問

これはSQLiteの大きなテーブルの現実ですか、または10mを超える行を持つテーブルの maintain 高い挿入率の秘密はありますか?

可能な場合は回避したい既知の回避策

  • インデックスの削除、レコードの追加、およびインデックスの再作成:これは回避策としては問題ありませんが、更新中にDBを使用する必要がある場合は機能しません。 x 分/日の間、データベースに完全にアクセスできないようにすることはできません。
  • テーブルを小さなサブテーブル/ files に分割します:これは短期的には機能しますが、私はすでに実験しました。問題は、クエリを実行するときに履歴全体からデータを取得できる必要があることです。つまり、最終的に62のテーブルアタッチメント制限に達することになります。添付し、結果を一時テーブルに収集し、リクエストごとに何百回もデタッチするのは、多くの作業とオーバーヘッドのようですが、他に選択肢がない場合は試してみます。
  • Set SQLITE_FCNTL_CHUNK_SIZE :C(?!)がわからないので、これを成し遂げるためだけに学習したくない。ただし、Perlを使用してこのパラメーターを設定する方法はありません。

更新

ティムの提案 大量のデータセットを処理できるというSQLiteの主張にもかかわらず、インデックスが挿入時間をますます遅くしているということで、次の設定とベンチマーク比較を行いました。

  • 挿入された行: 1400万
  • コミットバッチサイズ: 50,000レコード
  • cache_sizeプラグマ: 10,000
  • page_sizeプラグマ: 4,096
  • temp_storeプラグマ: memory
  • journal_modeプラグマ: delete
  • synchronousプラグマ: off

私のプロジェクトでは、以下のベンチマーク結果のように、ファイルベースの一時テーブルが作成され、CSVデータをインポートするためのSQLiteの組み込みサポートが使用されます。次に、一時テーブルが受信データベースに接続され、50,000行のセットがinsert-selectステートメントで挿入されます。したがって、挿入時間は、ファイルからデータベース挿入時間ではなく、テーブルからテーブル挿入速度を反映します。 CSVのインポート時間を考慮すると、速度が25〜50%低下します(非常に大まかな見積もりで、CSVデータをインポートするのに時間がかかりません)。

明らかにインデックスがあると、テーブルサイズが大きくなると挿入速度が遅くなります。

Plot of SQLite insert speed and table size

上記のデータから、SQLiteが単に処理できないというアサーションではなく、 Timの答え に正しい答えを割り当てることができることは非常に明確です。明らかに can 大規模なデータセットを処理 if データセットのインデックス作成はユースケースの一部ではありません。ロギングシステムのバックエンドとしてそのためにSQLiteを使用してきましたが、しばらくの間、 not のインデックスを作成する必要があるため、私が経験した速度の低下に非常に驚きました。 。

結論

SQLite and を使用して大量のデータを保存したい場合は、 shards を使用してください。最終的に、MD5ハッシュの最初の3文字を使用して、zの一意の列を使用して、4,096個のデータベースの1つへの割り当てを決定しました。私の使用例は本来アーカイブであるため、スキーマは変更されず、クエリはシャードウォーキングを必要としません。非常に古いデータが削減され、最終的に破棄されるため、データベースサイズには制限があります。そのため、シャーディング、プラグマ設定、および de normalizationのこの組み合わせにより、 、上記のベンチマークに基づいて、少なくとも10k挿入/秒の挿入速度を維持します。

59
user918938

特定のz_idおよびそれにリンクされたx_idsおよびy_idsを見つけることが要件である場合(z_idsの範囲をすばやく選択するのとは異なり)、 -インデックス付きのハッシュテーブルネストされたリレーショナルデータベース。これにより、特定のz_idへの道を即座に見つけて、y_idsおよびx_idsを取得できます。インデックスが大きくなるにつれて、挿入時にパフォーマンスが低下します。クランピング(別名バケットコリジョン)を回避するために、z_idの桁に最大の重みを付けるキーハッシュアルゴリズムを選択します(最大の変動)。

追伸たとえば、bツリーを使用するデータベースは、線形ハッシュを使用するdbよりも最初は速く表示される場合がありますが、bツリーのパフォーマンスが低下し始めると、挿入パフォーマンスは線形ハッシュのレベルのままになります。

P.P.S. @ kawing-chiuの質問に答えるために:ここで関連するコア機能は、そのようなデータベースがいわゆる「スパース」テーブルに依存し、レコードの物理的な場所が入力としてレコードキーを受け取るハッシュアルゴリズムによって決定されることです。このアプローチでは、テーブル内のレコードの場所へのシーク直接が許可されますインデックスの仲介なし。インデックスをトラバースしたり、インデックスのバランスを取り直したりする必要はないため、テーブルの密度が高くなると挿入時間は一定になります。対照的に、bツリーでは、インデックスツリーが大きくなるにつれて挿入時間が短くなります。 OLTP多数の同時挿入を伴うアプリケーションは、このようなスパーステーブルアプローチの恩恵を受けることができます。レコードはテーブル全体に散在しています。スパーステーブルの「ツンドラ」に散在するレコードの欠点郵便番号など、共通の値を持つレコードの大きなセットの収集が遅くなる可能性があります。ハッシュスパーステーブルアプローチは、個々のレコードを挿入および取得し、networksを取得するように最適化されます。共通のフィールド値を持つレコードの大きなセットではなく、関連レコードの。

ネストされたリレーショナルデータベースは、タプルwithin行の列を許可するデータベースです。

13
Tim

すばらしい質問と非常に興味深いフォローアップ!

簡単に発言したいのですが、テーブルを小さなサブテーブル/ファイルに分割して後でアタッチすることは、アタッチされたデータベースの上限であるハード制限にすぐに達するため、オプションではないということです。これは完全に真実ですが、データを複数のテーブルに分割するという中途のオプションを検討したとは思いませんbut同じ単一のデータベース(ファイル)を使用し続けます。


私の提案が実際にパフォーマンスに影響を与えることを確認するために、非常に粗雑なベンチマークを行いました。

スキーマ:

CREATE TABLE IF NOT EXISTS "test_$i"
(
    "i" integer NOT NULL,
    "md5" text(32) NOT NULL
);

データ-200万行:

  • i = 1..2,000,000
  • md5 = iのmd5 16進ダイジェスト

各トランザクション= 50,000 INSERTs。


データベース:1;テーブル:1;インデックス:0

0..50000 records inserted in 1.87 seconds
50000..100000 records inserted in 1.92 seconds
100000..150000 records inserted in 1.97 seconds
150000..200000 records inserted in 1.99 seconds
200000..250000 records inserted in 2.19 seconds
250000..300000 records inserted in 1.94 seconds
300000..350000 records inserted in 1.94 seconds
350000..400000 records inserted in 1.94 seconds
400000..450000 records inserted in 1.94 seconds
450000..500000 records inserted in 2.50 seconds
500000..550000 records inserted in 1.94 seconds
550000..600000 records inserted in 1.94 seconds
600000..650000 records inserted in 1.93 seconds
650000..700000 records inserted in 1.94 seconds
700000..750000 records inserted in 1.94 seconds
750000..800000 records inserted in 1.94 seconds
800000..850000 records inserted in 1.93 seconds
850000..900000 records inserted in 1.95 seconds
900000..950000 records inserted in 1.94 seconds
950000..1000000 records inserted in 1.94 seconds
1000000..1050000 records inserted in 1.95 seconds
1050000..1100000 records inserted in 1.95 seconds
1100000..1150000 records inserted in 1.95 seconds
1150000..1200000 records inserted in 1.95 seconds
1200000..1250000 records inserted in 1.96 seconds
1250000..1300000 records inserted in 1.98 seconds
1300000..1350000 records inserted in 1.95 seconds
1350000..1400000 records inserted in 1.95 seconds
1400000..1450000 records inserted in 1.95 seconds
1450000..1500000 records inserted in 1.95 seconds
1500000..1550000 records inserted in 1.95 seconds
1550000..1600000 records inserted in 1.95 seconds
1600000..1650000 records inserted in 1.95 seconds
1650000..1700000 records inserted in 1.96 seconds
1700000..1750000 records inserted in 1.95 seconds
1750000..1800000 records inserted in 1.95 seconds
1800000..1850000 records inserted in 1.94 seconds
1850000..1900000 records inserted in 1.95 seconds
1900000..1950000 records inserted in 1.95 seconds
1950000..2000000 records inserted in 1.95 seconds

データベースファイルサイズ:89.2 MiB。


データベース:1;テーブル:1;インデックス:1(md5

0..50000 records inserted in 2.90 seconds
50000..100000 records inserted in 11.64 seconds
100000..150000 records inserted in 10.85 seconds
150000..200000 records inserted in 10.62 seconds
200000..250000 records inserted in 11.28 seconds
250000..300000 records inserted in 12.09 seconds
300000..350000 records inserted in 10.60 seconds
350000..400000 records inserted in 12.25 seconds
400000..450000 records inserted in 13.83 seconds
450000..500000 records inserted in 14.48 seconds
500000..550000 records inserted in 11.08 seconds
550000..600000 records inserted in 10.72 seconds
600000..650000 records inserted in 14.99 seconds
650000..700000 records inserted in 10.85 seconds
700000..750000 records inserted in 11.25 seconds
750000..800000 records inserted in 17.68 seconds
800000..850000 records inserted in 14.44 seconds
850000..900000 records inserted in 19.46 seconds
900000..950000 records inserted in 16.41 seconds
950000..1000000 records inserted in 22.41 seconds
1000000..1050000 records inserted in 24.68 seconds
1050000..1100000 records inserted in 28.12 seconds
1100000..1150000 records inserted in 26.85 seconds
1150000..1200000 records inserted in 28.57 seconds
1200000..1250000 records inserted in 29.17 seconds
1250000..1300000 records inserted in 36.99 seconds
1300000..1350000 records inserted in 30.66 seconds
1350000..1400000 records inserted in 32.06 seconds
1400000..1450000 records inserted in 33.14 seconds
1450000..1500000 records inserted in 47.74 seconds
1500000..1550000 records inserted in 34.51 seconds
1550000..1600000 records inserted in 39.16 seconds
1600000..1650000 records inserted in 37.69 seconds
1650000..1700000 records inserted in 37.82 seconds
1700000..1750000 records inserted in 41.43 seconds
1750000..1800000 records inserted in 49.58 seconds
1800000..1850000 records inserted in 44.08 seconds
1850000..1900000 records inserted in 57.17 seconds
1900000..1950000 records inserted in 50.04 seconds
1950000..2000000 records inserted in 42.15 seconds

データベースファイルサイズ:181.1 MiB。


データベース:1;テーブル:20(100,000レコードにつき1);インデックス:1(md5

0..50000 records inserted in 2.91 seconds
50000..100000 records inserted in 10.30 seconds
100000..150000 records inserted in 10.85 seconds
150000..200000 records inserted in 10.45 seconds
200000..250000 records inserted in 10.11 seconds
250000..300000 records inserted in 11.04 seconds
300000..350000 records inserted in 10.25 seconds
350000..400000 records inserted in 10.36 seconds
400000..450000 records inserted in 11.48 seconds
450000..500000 records inserted in 10.97 seconds
500000..550000 records inserted in 10.86 seconds
550000..600000 records inserted in 10.35 seconds
600000..650000 records inserted in 10.77 seconds
650000..700000 records inserted in 10.62 seconds
700000..750000 records inserted in 10.57 seconds
750000..800000 records inserted in 11.13 seconds
800000..850000 records inserted in 10.44 seconds
850000..900000 records inserted in 10.40 seconds
900000..950000 records inserted in 10.70 seconds
950000..1000000 records inserted in 10.53 seconds
1000000..1050000 records inserted in 10.98 seconds
1050000..1100000 records inserted in 11.56 seconds
1100000..1150000 records inserted in 10.66 seconds
1150000..1200000 records inserted in 10.38 seconds
1200000..1250000 records inserted in 10.24 seconds
1250000..1300000 records inserted in 10.80 seconds
1300000..1350000 records inserted in 10.85 seconds
1350000..1400000 records inserted in 10.46 seconds
1400000..1450000 records inserted in 10.25 seconds
1450000..1500000 records inserted in 10.98 seconds
1500000..1550000 records inserted in 10.15 seconds
1550000..1600000 records inserted in 11.81 seconds
1600000..1650000 records inserted in 10.80 seconds
1650000..1700000 records inserted in 11.06 seconds
1700000..1750000 records inserted in 10.24 seconds
1750000..1800000 records inserted in 10.57 seconds
1800000..1850000 records inserted in 11.54 seconds
1850000..1900000 records inserted in 10.80 seconds
1900000..1950000 records inserted in 11.07 seconds
1950000..2000000 records inserted in 13.27 seconds

データベースファイルサイズ:180.1 MiB。


ご覧のとおり、データを複数のテーブルに分割した場合、挿入速度はほぼ一定のままです。

7
Alix Axel

残念ながら、これはSQLiteの大きなテーブルの制限だと思います。 設計されていない 大規模または大容量のデータセットを操作するためです。私はそれがプロジェクトの複雑さを劇的に増大させるかもしれないと理解していますが、あなたはおそらくあなたのニーズに適したより洗練されたデータベースソリューションを研究する方が良いでしょう。

リンクしたものすべてから、テーブルサイズとアクセス速度は直接的なトレードオフのようです。両方は持てません。

2
Michael Pratt

私のプロジェクトでは、異なる列にインデックスが付けられているため、データベースを分割できませんでした。挿入を高速化するために、作成時にデータベースを/ dev/shm(= linux ramdisk)に配置し、ローカルディスクにコピーしました。これは、1回限りの書き込みが可能なデータベースでのみ有効であることは明らかです。

1
Max

インデックスのハッシュ値の衝突により挿入速度が遅くなると思われます。

1つのテーブルに多くの行がある場合、インデックス列のハッシュ値の衝突がより頻繁に発生します。別のハッシュ値を取得するには、Sqliteエンジンがハッシュ値を2、3回、場合によっては4回計算する必要があることを意味します。

したがって、これはテーブルに多くの行がある場合のSQLite挿入の遅延の根本的な原因だと思います。

この点は、シャードを使用するとこの問題を回避できる理由を説明できます。ここで私の主張を確認または否定するためのSQLiteドメインの本当の専門家は誰ですか?

0
Clock ZHONG