web-dev-qa-db-ja.com

データベース上の文字列/レコードの非常に大きなリストをすばやく検索する方法

次の問題があります。200万を超えるレコードを含むデータベースがあります。各レコードには文字列フィールドXがあり、フィールドXに特定の文字列が含まれているレコードのリストを表示したいと思います。各レコードのサイズは約500バイトです。

具体的に言うと、アプリケーションのGUIにテキストフィールドがあり、そこに文字列を入力できます。テキストフィールドの上に、テキストフィールドの文字列と一致する(最初のN、たとえば100)レコードを表示するテーブルがあります。テキストフィールドに文字を1つ入力または削除すると、テーブルのコンテンツをその場で更新する必要があります。

適切なインデックス構造および/またはキャッシングを使用してこれを行う効率的な方法はあるのでしょうか。上記で説明したように、クエリに一致する最初のNアイテムのみを表示します。したがって、Nが十分に小さい場合、データベースから一致するアイテムをロードすることは大きな問題ではありません。さらに、アイテムをメインメモリにキャッシュすると、検索が速くなります。

主な問題は、パターン文字列が与えられた場合、一致するアイテムをすばやく見つける方法だと思います。一部のDBMS機能に依存できますか、それとも自分でメモリ内インデックスを作成する必要がありますか?何か案は?

[〜#〜]編集[〜#〜]

最初の実験を実行しました。レコードをさまざまなテキストファイルに分割し(ファイルあたり最大200レコード)、ファイルをさまざまなディレクトリに配置しました(1つのデータフィールドの内容を使用してディレクトリツリーを決定しました)。約40000個のディレクトリに約50000個のファイルができます。次に、Luceneを実行してファイルのインデックスを作成しました。 Luceneデモプログラムで文字列を検索するのは非常に高速です。分割とインデックス作成には数分かかりました。これは、クエリを実行する静的データセットであるため、これで十分です。

次のステップは、Luceneをメインプログラムに統合し、Luceneから返されたヒットを使用して、関連するレコードをメインメモリにロードすることです。

33
Giorgio

データをDB内に配置する代わりに、それらを一連のドキュメント(テキストファイル)として個別に保持し、リンク(パス/ URLなど)をDB内に保持できます。

SQLクエリの設計は、部分文字列検索と検索の両方で非常に遅くなるため、これは不可欠です。

今、あなたの問題は、文字列のセットを含むテキストファイルを検索する必要があるとして定式化されています。ここには2つの可能性があります。

  1. サブストリングの一致テキストBLOBが単一の文字列またはWord(空白なし)であり、その中の任意のサブストリングを検索する必要がある場合。このような場合、一致する可能性のある最良のファイルを見つけるために、すべてのファイルを解析する必要があります。 1つはBoyer Moorアルゴリズムのようなアルゴリズムを使用します。詳細は this および this を参照してください。これはgrepと同じです。grepは内部で同様のものを使用するためです。ただし、戻る前に、少なくとも100以上のgrep(最悪の場合は200万)を作成することができます。

  2. インデックス付き検索。ここでは、テキストに単語のセットが含まれており、検索は固定された単語の長さに制限されていると想定しています。この場合、ドキュメントは、出現する可能性のあるすべての単語に索引付けされます。これはしばしば「全文検索」と呼ばれます。これを行うアルゴリズムは多数あり、直接使用できるオープンソースプロジェクトも多数あります。それらの多くは、以下のようにワイルドカード検索、おおよその検索などもサポートしています:
    a。 Apache Lucene: http://lucene.Apache.org/Java/docs/index.html
    b。 OpenFTS: http://openfts.sourceforge.net/
    c。 Sphinx http://sphinxsearch.com/

クエリとして「固定単語」が必要な場合、2つのアプローチは非常に高速で効果的です。

20
Dipan Mehta

あなたが探している技術は全文索引付けです。ほとんどのRDBMSには、ここで機能する組み込みの機能がいくつかあります。より洗練されたものにしたい場合や、メモリで実行したい場合は、Luceneなどを使用できます。

21
Wyatt Barnett

トライ を検討しましたか?基本的には、共通のプレフィックスを使用してツリーを構築するため、同じ文字で始まるすべての単語は同じノードの子です。部分文字列のマッチングをサポートする場合は、何らかの permuted index を生成し、そこからトライを作成する必要があります。ただし、ストレージ要件が大幅に悪化する可能性があります。

8
TMN

Wyatt Barnettの回答に加えて、適切な列でフルテキストインデックスを使用するRDBMSソリューションが機能することを追加したいと思いますが、以前にフェッチしたレコードのローカルキャッシュを利用する場合は、これらのキャッシュされたレコードを利用する計画が必要です。あなたの利点に。

1つのオプションは、明示的にクエリから取得したくないこれらのレコードの一意の識別子を収集して、それらをおそらくNOT INまたはNOT EXISTSに含めることです。

ただし、NOT INまたはNOT EXISTSの使用は安くはない傾向があり、使用しているデータベースエンジンによっては、クエリのパフォーマンスやクエリプランに悪影響を与える可能性があります。最後のクエリで説明プランを実行して、影響を受ける列のすべてのインデックスが使用されていることを確認します。

また、2つの方法のパフォーマンスを比較してどちらが速いかを確認しても問題はありません。ローカルキャッシュを維持し、クエリからそれらを明示的にフィルタリングすると、すべてのレコードをフェッチするきめ細かく調整されたクエリよりもパフォーマンスが低下する可能性があることに驚くかもしれません。

5
maple_shaft

見逃した場合に備えて。 DB内でサポートされているテキスト検索の代わりにデータベースにLuceneを使用する場合、DBに変更を加えるときは非常に注意する必要があります。 DBと外部リソース(Lucene)の両方に変更を加える必要がある場合、どのようにアトミック性があることを確認しますか?はい、できますが、多くの作業が必要になります。

つまり、Luceneをデータスキーマに入れると、DBトランザクションのサポートが失われます。

2
InformedA

答えがどれも "inverted index" という用語を提示しなかったのは少し奇妙です。これは、Apache Luceneやその他に類似したすべてのソリューションの基礎となるテクノロジーです。

逆インデックスは、単語からドキュメントへのマッピング(「レコードレベルの逆インデックス」)、またはドキュメント内の正確なWordの場所(「ワードレベルの逆インデックス」)です。

ANDとOR論理演算を実装するのは簡単です。正確なWordの場所がある場合、隣接する単語を検索して、フレーズ検索を可能にすることができます。

したがって、(Word、ファイル、場所)タプルを含むインデックスについて考えます。あなたが持っているとき( "inverted"、 "foo.txt"、123)次に、( "index"、 "foo.txt"、124)が完全なフレーズ "inverted index"を検索するためのインデックスの一部であるかどうかを確認します。

全文検索エンジンを最初から再実装することはお勧めしませんが、Apache Luceneなどのテクノロジーがどのように機能するかを知っておくと役立ちます。

したがって、私の推奨は、逆索引がどのように機能するかを学び、Apache Luceneなどのそれらを使用するテクノロジーを選択することです。そうすれば、少なくとも何ができるのか、何ができないのかをしっかり理解できます。

1
juhist

スフィンクスを検討したことがありますか? http://sphinxsearch.com サードパーティのツールを使用できる場合、これはあなたが達成しようとしていることに理想的であり、私が個人的に持っているどのRDBMSよりも全文検索ではるかに効率的です中古。

1
twigg