18

アプリケーションで SQLite3 によるメモリ消費を削減する方法を探しています。

実行のたびに、次のスキーマを持つテーブルが作成されます。

(main TEXT NOT NULL PRIMARY KEY UNIQUE, count INTEGER DEFAULT 0)

その後、データベースは 1 秒あたり 5 万回の操作でいっぱいになります。書き込みのみ。

アイテムが既に存在する場合は、更新クエリを使用して「カウント」を更新します(これはUPSERTと呼ばれると思います)。これらは私のクエリです:

INSERT OR IGNORE INTO table (main) VALUES (@SEQ);
UPDATE tables SET count=count+1 WHERE main = @SEQ;

このように、トランザクションごとに 500 万回の操作で、DB に非常に高速に書き込むことができます。

この問題のためにディスク容量はあまり気にしませんが、RAM 容量が非常に限られています。したがって、あまり多くのメモリを浪費することはできません。

sqlite3_user_memory() は、実行中にメモリ消費量がほぼ 3GB に増加したことを通知します。sqlite3_soft_heap_limit64() で 2GB に制限すると、2GB に達するとデータベース操作のパフォーマンスがほぼゼロになります。

望ましいパフォーマンスを達成するには、キャッシュ サイズを 1M (ページ サイズがデフォルト) に上げる必要がありました。

メモリ消費を減らすにはどうすればよいですか?

4

4 に答える 4

16

1つの大きなトランザクションに集中している操作が多すぎることが原因で、メモリ消費量が多いようです。1Mの操作ごとのように、より小さなトランザクションをコミットしようとすると役立つ場合があります。トランザクションあたり500万回の操作は、メモリを大量に消費します。

ただし、動作速度とメモリ使用量のバランスを取ります。

小規模な取引が選択肢にない場合は、選択肢とPRAGMA shrink_memoryなる可能性があります。

sqlite3_status()withを使用しSQLITE_STATUS_MEMORY_USEDて、動的メモリ割り当てをトレースし、ボトルネックを特定します。

于 2013-03-09T00:24:57.633 に答える
9

私は...するだろう:

  • ステートメントを準備します(まだ行っていない場合)
  • トランザクションあたりの INSERT の量を減らします (10 秒 = 500,000 が適切に聞こえます)
  • PRAGMA locking_mode = EXCLUSIVE;できれば使う

また、(あなたが知っているかどうかはわかりませんが)PRAGMA cache_sizeMBではなくページです。PRAGMA cache_size * PRAGMA page_sizeasまたは SQLite >= 3.7.10 でターゲット メモリを定義してくださいPRAGMA cache_size = -kibibytes;。100 万に設定すると、1 GB または 2 GB になります。

しかし、INSERTでどのようcache_size に役立つのか興味があります...

違いが生じる場合は、ベンチマークを試すこともできますPRAGMA temp_store = FILE;

そしてもちろん、データベースが書き込まれていないときはいつでも:

  • PRAGMA shrink_memory;
  • VACUUM;

データベースで何をしているかに応じて、これらも役立つ場合があります。

  • PRAGMA auto_vacuum = 1|2;
  • PRAGMA secure_delete = ON;

次のプラグマを使用していくつかのテストを実行しました。

busy_timeout=0;
cache_size=8192;
encoding="UTF-8";
foreign_keys=ON;
journal_mode=WAL;
legacy_file_format=OFF;
synchronous=NORMAL;
temp_store=MEMORY;

テスト #1:

INSERT OR IGNORE INTO test (time) VALUES (?);
UPDATE test SET count = count + 1 WHERE time = ?;

1 秒あたり最大 109,000 の更新。

テスト #2:

REPLACE INTO test (time, count) VALUES
(?, coalesce((SELECT count FROM test WHERE time = ? LIMIT 1) + 1, 1));

毎秒最大 120,000 の更新でピークに達しました。


私も試しPRAGMA temp_store = FILE;てみましたが、更新は毎秒約1〜2k低下しました。


1 回のトランザクションで 7M の更新が行われる場合、journal_mode=WALは他のすべてよりも遅くなります。


データベースに 35,839,987 レコードを入力したところ、65521 回の更新のバッチごとにセットアップに 4 秒近くかかっていますが、16 MB のメモリ消費にも達していません。


わかりました、ここに別のものがあります:

INTEGER PRIMARY KEY 列のインデックス (実行しないでください)

INTEGER PRIMARY KEY で列を作成すると、SQLite はこの列をテーブル構造のキー (インデックス) として使用します。これは、この列の非表示のインデックスです (SQLite_Master テーブルには表示されないため)。列に別のインデックスを追加する必要はなく、使用されることはありません。さらに、INSERT、DELETE、および UPDATE 操作が遅くなります。

PK を NOT NULL + UNIQUE として定義しているようです。PK は暗黙的に UNIQUE です。

于 2013-05-12T20:47:08.043 に答える
4

1 つのトランザクションのすべての操作がテーブル全体に分散され、テーブルのすべてのページにアクセスする必要があると仮定すると、ワーキング セットのサイズは次のようになります。

  • テーブルのデータ用に約 1 GB に加えて
  • main列のインデックス用に約 1 GB 、プラス
  • トランザクションで変更されたすべてのテーブルのページ (おそらくすべて) の元のデータに対して約 1 GB。

count列を別のテーブルに移動することで、各操作で変更されるデータの量を減らすことができます。

CREATE TABLE main_lookup(main TEXT NOT NULL UNIQUE, rowid INTEGER PRIMARY KEY);
CREATE TABLE counters(rowid INTEGER PRIMARY KEY, count INTEGER DEFAULT 0);

次に、各操作について:

SELECT rowid FROM main_lookup WHERE main = @SEQ;
if not exists:
    INSERT INTO main_lookup(main) VALUES(@SEQ);
    --read the inserted rowid
    INSERT INTO counters VALUES(@rowid, 0);
UPDATE counters SET count=count+1 WHERE rowid = @rowid;

C では、挿入されたものはsqlite3_last_insert_rowidrowidで読み取られます。

セパレートSELECTandを実行すると、 ;INSERTより遅くはありません。INSERT OR IGNOREどちらの場合でも、SQLite は同じ作業を行います。

この最適化は、ほとんどの操作が既存のカウンターを更新する場合にのみ役立ちます。

于 2013-03-06T21:13:24.130 に答える
2

ブレーンストーミングの精神で、私はあえて答えます。私はこの仲間のようなテストを行っていません:

SQLite の 1 秒あたりの INSERT パフォーマンスを改善しますか?

私の仮説は、テキストの主キーのインデックスは、2 つの整数列のいくつかのインデックス (ハッシュ テーブルをシミュレートするために必要なもの) よりも RAM を集中的に使用する可能性があるというものです。

編集:実際には、これには主キーさえ必要ありません:

      create table foo( slot integer, myval text, occurrences int);
      create index ix_foo on foo(slot);  // not a unique index

整数の主キー (またはスロットの一意でないインデックス) では、テキスト値が既にファイルにあるかどうかを簡単に判断する方法がありません。したがって、その要件に対処するために、私が別の投稿者に提案したことを実装して、ハッシュ化されたキーをシミュレートしてみてください。

何百万ものエントリに対するSQLiteの最適化?

ハッシュキー関数を使用すると、テキスト値が存在する場合に格納される場所を特定できます。

http://www.cs.princeton.edu/courses/archive/fall08/cos521/hash.pdf http://www.fearme.com/misc/alg/node28.html http://cs.mwsu.edu/ ~griffin/courses/2133/downloads/Spring11/p677-pearson.pdf

于 2013-03-15T11:41:48.597 に答える