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


32

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

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

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

主な問題は、パターン文字列を指定して、一致するアイテムをすばやく見つける方法だと思います。DBMSの機能に依存することはできますか、それともインメモリインデックスを自分で構築する必要がありますか?何か案は?

編集

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

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


2
200万レコード* 500バイト= 1 GBのデータ。どちらの方法でも、検索する大量のデータです-Xの各値は一意である可能性が高いのですか、それともXの値が同じである多くのレコードがありますか?

1
それはまた、迅速な検索のためにキャッシュとしてメモリに保存しようとする大量のデータになります。これは、ユーザーセッションごとに1GB以上に相当します。
maple_shaft

以前のコメントでは、Webアプリケーションを想定しています。これはWebアプリケーションですか?
maple_shaft

これはデスクトップアプリケーションです。レコード内の値は必ずしも一意ではありません。また、完全一致ではなく部分文字列を検索しています。
ジョルジオ

@maple_shaft:最近アクセスしたレコードのみをキャッシュします。クエリ文字列を変更してもレコードが一致する場合、それはまだキャッシュにあります。
ジョルジオ

回答:


20

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

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

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

  1. 部分文字列の一致テキストBLOBが単一の文字列または単語(空白なし)であり、その中の任意の部分文字列を検索する必要がある場合。このような場合、すべてのファイルを解析して、一致する可能な限り最良のファイルを見つける必要があります。1つは、ボイヤームーアアルゴリズムのようなアルゴリズムを使用します。詳細はこちらこちらをご覧ください。これはgrepと同等です-grepは内部で同様のものを使用するためです。ただし、戻る前に少なくとも100以上のgrep(最悪の場合は200万)を作成することができます。

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

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


2
これは興味深い概念ですが、開発者がデータベースエンジンよりも高速かつ効率的に1 GBのテキストデータを簡単に検索できるとは考えられません。あなたよりもはるかに賢い人々と私は、クエリオプティマイザーでそれを実現するために労力を費やしてきました。
maple_shaft

4
@maple_shaft私が与えた例は、RDBMSデータベースエンジンではありません。呼び出したい場合、それらは「検索エンジン」に似ています。インデックス(またはハッシュテーブル)からリストを選択することと、クエリが実行されるたびに1GBのデータを繰り返し検索することとの間には、概念上の大きな違いがあります。だから私が提案しているのは、マイナーな微調整ではありません。
ディパンメタ

これは面白いアイデアのように思えますが、どのように機能するのでしょうか。私は2 000 000個を超えるファイルを持ち、それぞれのサイズは約0.5キロバイトです。または、ファイルごとに複数のレコードを持つことを提案していますか?データベースとの違いは何ですか?
ジョルジオ

これは、たとえば、SQLフルテキストインデックスよりもパフォーマンスが必ずしも良いとは思いません。
カークブロードハースト

@Giorgio-はい、それが全文検索エンジンの動作方法です。ここでの主な違いは、事前にインデックス付けされたページとメモリ内検索です(クエリが実行されるたびに)。
ディパンメタ

21

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


1
私の意見では、RDBMSのフルテキストオプションは、「非構造化非関連データの山を検索する」ために設計されていないことを行うための回避策です。検索エンジンを構築する場合は、RDBMSを使用しないでください。それは小さなデータセットで動作するかもしれませんが、あらゆる種類のスケーリングを欠きます。構造化されていないデータの山を検索することは釘ではないので、ハンマーを使用しないでください。ジョブに適したツールを使用します。
ピーターB

8

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


1
はい!私は木構造について考えていて、自分に似たようなものがあることを思い出しましたが、トライを使用したことがないのでトライを覚えていませんでした。ストレージ要件について:20000ヒットでテーブルを作成するのは意味がないので、最初のNエントリ(たとえばN = 100)のみを取得する必要があることに注意してください。したがって、トライの各ノードは最大でN個のエントリを指します。また、高速アクセスが必要であることを忘れていましたが、データは一度しか読み込まれないため、高速更新は必要ありません。並べ替えられたインデックスでのトライのアイデアは本当にうまくいくかもしれません!
ジョルジオ

1
良い答えが、トライをマッチングするための素晴らしいですが、あなたが注意としてスタートあなたの言葉のをしかし、任意の部分文字列に一致する場合はすぐに複雑で、非常に大規模な取得します...
カーク・ブロードハースト

最初の実験として、検索する必要のある文字列に表示されるすべてのサブ文字列のセットを作成しようとしました。これらの文字列は、正しく理解できれば、トライのパスに対応します。長さ6のサブストリングでメモリ不足の例外(JVMの256Mのヒープ)が発生しました。したがって、何か間違ったことをしない限り、この解決策は実行できないと思います。
ジョルジオ

5

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

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

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

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


maple_shaftと@Wyatt Barnett:提案をありがとう。読書をして、さまざまな解決策を試してみる必要があります。すべてのデータベースが完全なインデックス作成をサポートしているわけではありませんが、MySQL(現在使用中)はサポートしています(dev.mysql.com/doc/refman/5.5/en/fulltext-search.html)。いくつかのテストを行ってから、ここで報告します。
ジョルジオ

2

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

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


1
述べた問題は、とにかくRDMSに適しているとは思えません。
ピーターB

1

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


3
反対票は?
twigg

1

Apache Luceneなどに類似したすべてのソリューションの基礎となる技術である「逆索引」という用語を提示した回答がなかったことは、やや奇妙です。

逆索引は、単語から文書へのマッピング(「レコードレベルの逆索引」)、または文書内の正確な単語位置(「単語レベルの逆索引」)です。

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

そのため、(単語、ファイル、場所)タプルを含むインデックスについて考えてください。例えば( "inverted"、 "foo.txt"、123)がある場合は、( "index"、 "foo.txt"、124)がインデックスの一部であるかどうかをチェックして、完全なフレーズ「inverted index」を検索します。 。

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.