ElasticSearchインデックスとMySQLインデックス

ElasticSearchインデックスとMySQLインデックス
はじめに
この間、私は製品の検索機能を維持していましたが、管理コンソールでelasticsearchの効率的なクエリ効率を見るたびに、彼がどのようにそれを行っているのか興味がありました。

ElasticSearchインデックスとMySQLインデックス
これは、MySQLをローカルで使用してプライマリキーを介してクエリを実行するよりもさらに高速です。

このため、ElasticSearchインデックスとMySQLインデックス検索に関連する情報を見つけました

ElasticSearchインデックスとMySQLインデックス
この種の質問に対する答えはインターネット上にたくさんあります。おおよその意味は次のとおりです。

  • ESはLuceneをベースにしたフルテキスト検索エンジンで、データをセグメント化してインデックスを保存します。大量のインデックスデータの管理に優れています。MySQLと比較して、データや関連するクエリを頻繁に更新するのは得意ではありません。
    あまり徹底的ではなく、分析に関連する原則もありませんが、インデックスが繰り返し言及されているので、インデックスの観点から両者の違いを比較します。

MySQLインデックス
MySQLで始まります。インデックスという用語は誰もが知っている必要があります。通常、一部のクエリシナリオに存在します。これは、時間のスペースの一般的なケースです。

以下では、例としてInnodbエンジンを使用しています。
一般的なデータ構造
は、MySQLインデックスを自分で設計することを前提としています。オプションは何ですか。

ハッシュテーブル
最初に考えるべきことは、クエリと書き込みのための非常に一般的で効率的なデータ構造であるハッシュテーブルです。Javaに対応するのはHashMapです。

ElasticSearchインデックスとMySQLインデックス
このデータ構造をあまり導入する必要はありません。書き込み効率は非常にO(1)です。たとえば、id = 3のデータをクエリする場合は、3をハッシュして、この配列で対応するものを見つける必要があります。場所は大丈夫です。

ただし、1≤id≤6などの間隔データを照会する場合、ハッシュテーブルは十分に満たすことができません。無秩序であるため、すべてのデータをトラバースして、どのデータがこの間隔に属するかを知る必要があります。


ElasticSearchインデックスとMySQLインデックス
コンポーネント検索はまた、O(LOGN)データを効率的に見つけることができます。

同時に、データも順序付けされているため、当然、間隔クエリをサポートできます。順序付けられた配列はインデックス作成に適しているようです。

当然のことながら、別の大きな問題があります。id= 2.5のデータを挿入すると、後続のすべてのデータを同時に1ビット移動する必要があり、書き込み効率が非常に低くなります。

平衡バイナリツリー
の順序付き配列の書き込み効率は高くないので、効率的な書き込みを見てみましょう。バイナリツリーは簡単に考えられます。ここでは、平衡バイナリツリーを例として取り上げます。

バランスの取れたバイナリツリーの特性により、次のようになります。

左側のノードは親ノードよりも小さく、右側のノードは親ノードよりも大きくなっています。

したがって、id = 11のデータをクエリする場合、最終的にデータを見つけるために10-> 12-> 11をクエリするだけで済みます。時間の複雑さはO(logn)であり、同様に、データを書き込むときはO(logn)です。

ただし、それでも間隔範囲検索は十分にサポートされていません。5≤id≤20のデータをクエリする必要があるとすると
、順序付けされた配列のクエリ効率は非常に高くなります。id = 4のデータをクエリする場合は、必要なだけです。バイナリElasticSearchインデックスとMySQLインデックス
検索では、データO(logn)を効率的に見つけることもできます。

同時に、データも順序付けされているため、当然、間隔クエリをサポートできます。順序付けられた配列はインデックス作成に適しているようです。

当然のことながら、別の大きな問題があります。id= 2.5のデータを挿入すると、後続のすべてのデータを同時に1ビット移動する必要があり、書き込み効率が非常に低くなります。

平衡バイナリツリー
の順序付き配列の書き込み効率は高くないので、効率的な書き込みを見てみましょう。バイナリツリーは簡単に考えられます。ここでは、平衡バイナリツリーを例として取り上げます。

ElasticSearchインデックスとMySQLインデックス
バランスの取れたバイナリツリーの特性により、次のようになります。

左側のノードは親ノードよりも小さく、右側のノードは親ノードよりも大きくなっています。

したがって、id = 11のデータをクエリする場合、最終的にデータを見つけるために10-> 12-> 11をクエリするだけで済みます。時間の複雑さはO(logn)であり、同様に、データを書き込むときはO(logn)です。

ただし、それでも間隔検索は十分にサポートされていません。5≤id≤20のデータをクエリする場合、最初に10ノードの左側のサブツリーをクエリし、次に10ノードの右側のサブツリーをクエリして最後にすべてのデータをクエリする必要があります。

このようなクエリの効率は高くありません。

ジャンプテーブル
は、上記のハッシュテーブル、順序付き配列、バイナリツリーほど一般的ではない場合がありますが、実際には、Redisで設定された並べ替えはジャンプテーブルによって実装されます。

ここでは、次のジャンプテーブルで実装されるデータ構造の利点を簡単に紹介します。

順序付きリンクリストのクエリ効率でさえ、バイナリ検索に配列添え字を使用できないため、高くないことは誰もが知っています。したがって、時間の複雑さはo(n)です。

ただし、次の図に示すように、リンクリストを巧妙に最適化して、偽装したバイナリ検索を実現することもできます。

ElasticSearchインデックスとMySQLインデックス
ボトムデータのプライマリインデックスとセカンダリインデックスを抽出できます。データ量に応じて、Nレベルのインデックスを抽出できます。

クエリを実行するときは、ここでインデックスを使用して、偽装したバイナリ検索を実現できます。

id = 13のデータをクエリする場合、データをクエリするために4つのノード1—> 7—> 10—> 13をトラバースするだけで済みます。数値が大きいほど、効率の向上が明らかになります。

同時に、インターバルクエリもサポートされています。現在の単一ノードのクエリと同様に、開始ノードをクエリしてから、ターゲットノードまで逆方向にトラバース(リンクリストが順序付けられている)するだけで、データの全範囲をクエリできます。

同時に、実際のデータをインデックスに保存せず、ポインタのみを保存するため、最下層のリンクリストが占めるスペースはごくわずかです。

バランスの取れたバイナリツリーの最適化
しかし、実際には、MySQLのInnodbはスキップテーブルを使用せず、B +ツリーと呼ばれるデータ構造を使用します。

このタイプのデータ構造は、需要シナリオに従って実際のプロジェクトの基本データ構造から進化したものであるため、このタイプのデータ構造は、大学の教師が基本データ構造としてよく話すため、バイナリツリーのようなものではありません。

たとえば、ここでのB +ツリーは、バランスの取れたバイナリツリーから進化したと見なすことができます。

先ほど、バイナリツリーの間隔クエリの効率は高くなく、この点に合わせて最適化できることを説明しました。

ElasticSearchインデックスとMySQLインデックス
元のバイナリツリーに基づいて最適化した後:すべての非リーフはデータを格納しませんが、リーフノードのインデックスとして機能し、すべてのデータはリーフノードに格納されます。

このようにして、すべてのリーフノードのデータが整然と保存され、間隔クエリを適切にサポートできます。

最初に開始ノードの位置を照会してから、リーフノードを逆方向にトラバースするだけで済みます。

データ量が多い場合は、インデックスファイルをメモリに保存できないことは明らかです。速度は非常に速いですが、リソースの消費量は少なくないため、MySQLはインデックスファイルをディスクに直接保存します。

この点は、後述のelasticsearchインデックスとは少し異なります。

インデックスはディスクに保存されるため、ディスクIOを可能な限り削減する必要があります(ディスクIOとメモリの効率は1桁ではありません)

上の図からわかるように、データをクエリするには少なくとも4つのIOを実行する必要があります。明らかに、IOの数はツリーの高さと密接に関連しています。ツリーの高さが低いほど、IOの数が少なくなり、パフォーマンスが向上します。いいです。

どうすれば木の高さを下げることができますか?
ElasticSearchインデックスとMySQLインデックス
バイナリツリーをトライノミアルツリーに変更して、ツリーの高さを大幅に下げることができます。これにより、データをクエリするときのIOの数が自然に減少し、クエリの効率が大幅に向上します。

これが実際にはB +ツリーの起源です。

インデックスを使用するためのいくつかの提案
は、上の図のB +ツリーを理解することで、日常業務の細部を実際に最適化できます。たとえば、順番に増やす方がよいのはなぜですか。

書き込むプライマリキーデータが故障しているとすると、後で書き込むデータのIDが前に書き込んだIDよりも小さい可能性があるため、B +ツリーインデックスを維持するときに書き込まれたデータを移動する必要があります。

データが増分で書き込まれる場合、そのような考慮事項はなく、毎回順番に書き込むだけで済みます。

そのため、データベースのプライマリキーを可能な限り増加傾向にする必要があります。最も合理的な方法は、サブテーブルを考慮せずにプライマリキーを増やすことです。

全体として、考え方はジャンプテーブルに似ていますが、使用シナリオに関連する調整が行われます(たとえば、すべてのデータがリーフノードに格納されます)。

ESインデックス
MySQLは終了しました。次に、Elasticsearchがインデックスをどのように使用するかを見てみましょう。


ESのフォワードインデックスは、インバーテッドインデックスと呼ばれるデータ構造を使用します。インバーテッドインデックスについて正式に説明する前に、フォワードインデックスの反対について説明しましょう。
ElasticSearchインデックスとMySQLインデックス

上の図は例です。doc_idを介して特定のオブジェクトをクエリする方法は、正のインデックスを使用して呼び出されます。これは、ハッシュテーブルとしても理解できます。

本質は、キーによって値を見つけることです。

たとえば、doc_id = 4を使用すると、データ名= jetty wang、age = 20をすばやく見つけることができます。

反転インデックス
名前にliが含まれているデータをクエリしたい場合はどうすればよいですか?この方法で効率的にクエリを実行するにはどうすればよいですか?

明らかに、上記のフォワードインデックスだけでは効果がありません。名前にliが含まれているかどうかを判断するためにすべてのデータをトラバースすることしかできません。これは、非常に非効率的です。

しかし、インデックス構造を再構築すると、次のようになります。
ElasticSearchインデックスとMySQLインデックス

名前にliを含むデータを照会する場合は、このインデックス構造を使用して投稿リストに含まれるデータを照会し、次にマッピング方法を使用して最終データを照会するだけで済みます。

このインデックス構造は、実際には逆インデックスです。

用語辞書
しかし、以前の経験と組み合わせて、このインデックス構造でliを効率的にクエリする方法は、用語を整然と配置する限り、バイナリツリー検索ツリーデータ構造を使用してo(logn)の下のデータをクエリできます。

テキストを独立した用語に分割するプロセスは、実際には、単語のセグメンテーションと呼ばれることがよくあります。

すべての用語を組み合わせたものが用語辞書であり、単語辞書とも呼ばれます。

英語の単語のセグメンテーションは比較的単純です。単語を分割するには、テキストをスペースと句読点で区切るだけです。中国語は比較的複雑ですが、それをサポートする多くのオープンソースツールがあります(この記事の焦点では​​ないため、単語のセグメンテーションに関心のある人は自分で検索できます)。
テキストの量が多いと、単語のセグメンテーション後に多くの用語が発生します。このような逆インデックスデータ構造をメモリに保存する場合は間違いなく十分ではありませんが、MySQLのようにディスクに保存する場合は効率がそれほど高くありません。

用語インデックス

したがって、妥協方法を選択できます。用語辞書全体をメモリに配置することはできないため、用語辞書のインデックスを作成してメモリに配置できます。

このようにして、用語辞書を効率的に照会し、最後に用語辞書を介して投稿リストを照会できます。

MySQLのB +ツリーと比較すると、ディスクIOも数倍削減されます。

ElasticSearchインデックスとMySQLインデックス
この用語インデックスは、このようなTrieツリーを使用して保存できます。これは、辞書ツリーと呼ばれることがよくあります。

辞書ツリーの詳細については、こちらを確認してください。

ElasticSearchインデックスとMySQLインデックス
jで始まる用語で検索する場合、最初のステップは、メモリ内の用語インデックスを介して、用語辞書ファイルのどこにjで始まる用語があるかを見つけることです(この位置はファイルポインタである可能性があります。間隔範囲)。

この位置間隔のすべての用語を取り出した直後に、順序がソートされているため、バイナリ検索によって特定の位置をすばやく見つけることができます。このようにして、投稿リストを照会できます。

最後に、ターゲットデータは、投稿リストの場所情報を介して元のファイルから取得できます。

より多くの最適化
もちろん、ElasticSearchは多くのターゲットを絞った最適化も行っています。2つのフィールドを検索する場合、ビットマップを使用して最適化できます。

たとえば、name = liとage = 18のデータをクエリする必要があります。このとき、これら2つのフィールドを使用して、投稿リストからそれぞれの結果を取得する必要があります。
ElasticSearchインデックスとMySQLインデックス
最も簡単な方法は、2つのセットを別々にトラバースして重複データを取り出すことですが、これは明らかに非効率的です。

現時点では、ストレージにビットマップ方式を使用(およびストレージスペースを節約)すると同時に、固有のビットと計算を使用して結果を取得できます。


[1, 3, 5]       ⇒ 10101

[1, 2, 4, 5] ⇒ 11011

結果は、2つのバイナリ配列を追加することで取得できます。


10001 ⇒ [1, 5]

結局、投稿リストは[1、5]として解かれますが、これは当然効率がはるかに高くなります。

同じクエリ要件はMySQLで特別に最適化されていませんが、2番目のフィールドは、少量のデータを含むデータが除外された後にフィルタリングされ、効率は当然ESほど高くありません。

もちろん、投稿リストは最新バージョンのESでも圧縮されます。特定の圧縮ルールは公式ドキュメントに記載されていますが、ここでは紹介しません。

まとめ
最後に、次のように要約します。

ElasticSearchインデックスとMySQLインデックス
上記の内容から、最終的には基本的なデータ構造で構成された複雑な製品であっても、さまざまなアプリケーションシナリオに合わせて最適化されることがわかります。したがって、データ構造とアルゴリズムの基礎を築いた後、新しいテクノロジーやミドルウェアを検討してください。すぐに始めて、最適化の方向性さえ知っているだけです。

最後に、パイを描きます。ES反転インデックスのアイデアに基づいてスタンドアロンの検索エンジンを構築しようとします。自分で書くだけで、理解を深めることができます。

より良い読書体験については、こちらをご覧ください:https//www.notion.so/ElasticSearch-VS-MySQL-54bddcc092c64c26b2127f1fb9772a23

あなたの好きなものと共有はあなたの最大のサポートです

おすすめ

転載: blog.51cto.com/15049794/2562888