iPhone用のsqlite3データベースのサイズを縮小するにはどうすればよいですか?
-
03-07-2019 - |
質問
編集:すべての回答に感謝します。これまでに最適化を適用した後の結果は次のとおりです。
- 文字のソートと実行長エンコードへの切り替え-新しいDBサイズ42M
- ブール値のインデックスの削除-新しいDBサイズ33M
本当に素晴らしい部分は、これがiphoneコードの変更を必要としないことです
sqlite形式(読み取り専用)で保持されている大きな辞書を持つiphoneアプリケーションがあります。現在非常に大きいDBファイルのサイズを小さくするためのアイデアを探しています。
sqlite DBのエントリ数と結果のサイズは次のとおりです。
franks-macbook:DictionaryMaker frank$ ls -lh dictionary.db
-rw-r--r-- 1 frank staff 59M 8 Oct 23:08 dictionary.db
franks-macbook:DictionaryMaker frank$ wc -l dictionary.txt
453154 dictionary.txt
...エントリあたり平均約135バイト。
ここに私のDBスキーマがあります:
create table words (word text primary key, sowpods boolean, twl boolean, signature text)
create index sowpods_idx on words(sowpods)
create index twl_idx on words(twl)
create index signature_idx on words(signature)
サンプルデータを次に示します。
photoengrave|1|1|10002011000001210101010000
photoengraved|1|1|10012011000001210101010000
photoengraver|1|1|10002011000001210201010000
photoengravers|1|1|10002011000001210211010000
photoengraves|1|1|10002011000001210111010000
photoengraving|1|1|10001021100002210101010000
最後のフィールドは、アナグラム検索の文字頻度を表します(各位置は0〜9の範囲です)。 2つのブール値はサブ辞書を表します。
次のようなクエリを実行する必要があります。
select signature from words where word = 'foo'
select word from words where signature = '10001021100002210101010000' order by word asc
select word from words where word like 'foo' order by word asc
select word from words where word = 'foo' and (sowpods='1' or twl='1')
1つのアイデアは、文字の頻度をより効率的にエンコードすることです。それらをblobとしてバイナリエンコードします(ゼロが多いため、おそらくRLEを使用しますか?)。これをどのように達成するのが最善か、またはサイズを縮小する他のアイデアはありますか?ルビーでデータベースを構築し、目的Cで電話でそれを読んでいます。
DBの統計情報を取得する方法はありますか?
解決
署名フィールドのすべてのユースケースについて明確ではありませんが、代わりに単語のアルファベット順バージョンを保存することが有益であると思われます。
他のヒント
「真空」を入力してみましたか?コマンドを使用して、回収するのを忘れたDBに余分なスペースがないことを確認しますか?
sowpodとtwlのインデックスを削除します-これらはおそらくクエリ時間の助けにはならず、間違いなく多くのスペースを必要とします。
SQLiteダウンロードページから sqlite3_analyzer を使用して、データベースの統計を取得できます。 。
完全に異なるアプローチとして、包括的なのではなく、ブルームフィルターを使用してみてください。データベース。基本的に、ブルームフィルターは一連のハッシュ関数で構成され、各ハッシュ関数はビットフィールドに関連付けられています。正当な単語ごとに、各ハッシュ関数が評価され、対応するビットフィールドの対応するビットが設定されます。欠点としては、理論的には誤検知が発生する可能性がありますが、十分なハッシュを使用して誤検知を最小限に抑えるか、実際に排除することができます。プラス面はスペースを大幅に節約します。
SQLiteの作成者は、データベース圧縮(および暗号化)を含むバージョンのSQLiteを販売しています。これは完璧でしょう。
最善の策は圧縮を使用することです。残念ながら、SQLiteは現時点ではネイティブにサポートしていません。幸いなことに、誰かが圧縮拡張機能を開発するのに時間をかけました。 p>
それ以外の場合は、データを主に圧縮形式で保存し、その場で解凍することをお勧めします。
テキストフィールドとして、 signature
は現在、エントリごとに少なくとも26 * 8バイト(208バイト)を使用していますが、ビットフィールドにデータをパックする場合は、おそらく1文字あたり3ビット(1文字あたりの最大頻度を7に減らします)。つまり、署名全体を26 * 3ビット= 78ビット= 10バイトでパックできるということです。文字ごとに4ビットを使用した場合でも(文字ごとに最大15回)、104ビット(13バイト)しか使用しません。
編集:もう少し考えてみると、バイナリ演算が簡単になるので、文字ごとに4ビット(3ではなく)を使用する方が良いと思います。
EDIT2: SQLiteデータ型のドキュメントを読むと、 「署名」を作成することができますINTEGER型とSQLite型の26列にわたるフィールドスパンは正しいことを行い、値を格納するために必要なビットだけを使用します。
データベースにそのような約45万語があることを正しく認識していますか?
iPhoneについては何の手掛かりもありませんが、sqlitemについても深刻ではありませんが、sqliteがファイルをすぐにgzとして保存する方法を許可しない限り(多分、すでに内部でできていますか?両方のインデックスを使用していなくても、エントリごとに約135 bと言います)、テーブルアプローチから離れて、「手動で」保存します。 ディクショナリアプローチの圧縮で、残りをオンザフライおよびメモリでビルドします。それはあなたのタイプのデータで非常にうまく機能するはずです。
Wait ...その署名を使用して、全文検索または認識ミスタイプを許可していますか? sqliteの全文検索はそのフィールドを廃止しませんか?
前述のとおり、「署名」を保存します。より効率的にすることは良いアイデアのようです。
ただし、単語に何らかのルックアップテーブルを使用することで、大量のスペースを節約できるようにも見えます-ルートワードを取得してから「er」、「ed」、「quote」を追加しているように見えるため; es"など、なぜ別のルックアップテーブルからのルートワードを参照する数値IDの列と、ベースワードに追加される一般的な単語の接尾辞のテーブルを参照する数値IDの別の列がないのか。
単一のルートワードで複数のエントリの署名の短縮版を保存するトリックがある場合は、保存された署名のサイズを小さくするためにそれらを使用することもできます(どのアルゴリズムがそれらの値を生成するかわからない)
これは、「単語」を持っているので、私にとっても非常に理にかなっているようです。列を主キーとして使用しますが、インデックスを作成しません。テーブルの主IDである別の数値列を作成するだけです。
mhmm ... iPhone ...永続的なデータ接続はありませんか? これは、webapplication / webserviceがぴったりとジャンプできる場所だと思います。 ほとんどのビジネスロジックをWebサーバーに移動し(FTSと大量のメモリを備えた実際のSQLを使用します)、その情報をデバイス上のクライアントにオンラインで取得します。
他の箇所で述べたように、ブール列のインデックスは失われます。インデックスはテーブルスキャンよりも遅くなり(使用される場合)、不必要にスペースを使用します。
単語に単純な圧縮を適用することを検討します。ハフマンコーディングはかなり良いですこの種のもののために。また、私は署名を見ます:列を文字の頻度順にソートし、暗示される可能性のある末尾のゼロを保存しません。それらもハフマンでエンコードできると思います。
もちろん、エンコードされた文字列がSQLiteを混乱させないことを常に前提としています。