Elasticsearchでのデータ モデリングは、リレーショナル データベースを扱う場合ほど明白ではありません。データの正規化と SQL 結合に依存する従来のリレーショナル データベースとは異なり、Elasticsearch では関係を管理するための代替アプローチが必要です。
Elasticsearch でリレーションシップを管理するための一般的な回避策は 4 つあります。
アプリケーション側の結合
データの非正規化
ネストされたフィールドタイプとネストされたクエリ
親子関係
このブログでは、ネストされたフィールド タイプと親子関係を使用して関係を処理するデータ モデルを設計する方法について説明します。これら 2 つの手法のアーキテクチャ、パフォーマンスへの影響、および使用例について説明します。
Elasticsearch は、オブジェクトに他のオブジェクトを含めることができるネストされた構造をサポートしています。ネストされたフィールド タイプは、メイン ドキュメント内の JSON オブジェクトであり、独自の異なるフィールドとタイプを持つことができます。これらのネストされたオブジェクトは、ネストされたクエリを使用してのみアクセスできる、個別の非表示のドキュメントとして扱われます。
ネストされたフィールド タイプは、データの整合性、密接な結合、階層構造が重要な関係に適しています。これには、メイン エンティティが 1 つある 1 対 1 および 1 対多の関係が含まれます。たとえば、1 つのドキュメント内で人物とその複数の住所および電話番号を表す場合などです。
ネストされたフィールド タイプを使用すると、Elasticsearch はドキュメント全体、および親オブジェクトとネストされたオブジェクトを単一の Lucene ブロックとセグメントに保存します。これにより、関係がドキュメントに含まれるため、クエリ速度が速くなります。
コメント付きのブログ投稿の例を見てみましょう。コメントをブログ投稿の下にネストして、同じドキュメント内でまとめて簡単にクエリできるようにします。
{ "post_id": "1", "title": "Introduction to Elasticsearch Data Modeling", "content": "Exploring various data modeling options in Elasticsearch.", "comments": [ { "comment_id": "101", "text": "Great overview of data modeling!" }, { "comment_id": "102", "text": "Looking forward to more content." } ] }
ネストされたオブジェクトの関係の利点は次のとおりです。
更新の非効率性: ネストされたオブジェクトを含むドキュメントの任意の部分を更新、挿入、および削除するには、ドキュメント全体の再インデックスが必要です。特にドキュメントが大きい場合や更新が頻繁に行われる場合は、メモリを大量に消費する可能性があります。
大規模なネストされたフィールドでのクエリ パフォーマンス: 特に大規模なネストされたフィールドを持つドキュメントがある場合、パフォーマンスに影響が出る可能性があります。これは、検索要求によってドキュメント全体が取得されるためです。
複数レベルのネストは複雑になる場合があります。複数レベルのネストされた構造にわたってクエリを実行すると、やはり複雑になる可能性があります。これは、クエリにネストされたクエリ内に入れ子になったクエリが含まれる場合があり、コードが読みにくくなるためです。
親子マッピングでは、ドキュメントは親と子のタイプに編成されます。各子ドキュメントは親ドキュメントと直接関連付けられます。この関係は、親の ID と一致する子ドキュメントの特定のフィールド値を通じて確立されます。親子モデルは、親ドキュメントと子ドキュメントが独立して存在する分散型アプローチを採用しています。
親子結合は、エンティティ間の 1 対多または多対多の関係に適しています。企業と連絡先の関係を作成し、企業と連絡先、および特定の企業の連絡先を検索するアプリケーションを想像してください。
Elasticsearch は、どの親がどの子に接続されているかを追跡し、両方のエンティティを同じシャード上に配置することで、親子結合のパフォーマンスを高めます。結合操作をローカライズすることで、Elasticsearch はパフォーマンスのボトルネックとなる可能性のある広範なシャード間通信の必要性を回避します。
ブログ投稿とコメントの親子関係を例に挙げてみましょう。各ブログ投稿 (親) には、複数のコメント (子) を設定できます。親子関係を作成するには、次のようにデータをインデックス化します。
PUT my-index-000001 { "mappings": { "properties": { "post_id": { "type": "keyword" }, "post_id": { "type": "join", "relations": { "post": "comment" } } } } }
親ドキュメントは次のような投稿になります。
{ "post_id": "1", "title": "Introduction to Elasticsearch Data Modeling", "content": "Exploring various data modeling options in Elasticsearch." }
子ドキュメントは、親にリンクする post_id を含むコメントになります。
{ "comment_id": "101", "text": "Great overview of data modeling!", "post_id": "1" }
親子モデリングの利点は次のとおりです。
リレーショナル データ モデルに似ています: 親子関係では、親ドキュメントと子ドキュメントは別々であり、一意の親 ID によってリンクされます。この設定はリレーショナル データベース モデルに近いため、このような概念に精通しているユーザーにとってはより直感的です。
更新の効率: 子ドキュメントは、親ドキュメントや他の子ドキュメントに影響を与えることなく、追加、変更、または削除できます。これは、頻繁な更新が必要な多数の子ドキュメントを処理する場合に特に便利です。子ドキュメントを別の親に関連付けるプロセスは、新しい親が別のシャードにある可能性があるため、より複雑になることに注意してください。
異種の子に適しています: 子ドキュメントは個別に保存されるため、特にサイズが大きく異なる子ドキュメントが多数ある場合には、メモリとストレージの効率が向上する可能性があります。
親子関係の欠点は次のとおりです。
コストが高く、遅いクエリ: 別々のインデックス間でドキュメントを結合すると、クエリ実行中に計算作業が追加され、パフォーマンスに影響します。Elasticsearch によると、親子クエリはネストされたオブジェクトをクエリするよりも 5 ~ 10 倍遅くなることがあります。
マッピングのオーバーヘッド: 親子関係は、より多くのメモリとキャッシュ リソースを消費する可能性があります。Elasticsearch は親子関係のマップを維持しますが、これは特に大量のドキュメントがある場合に大きくなり、大量のメモリを消費する可能性があります。
シャード サイズの管理: 親ドキュメントと子ドキュメントの両方が同じシャードに存在するため、クラスター全体でデータ分散が不均一になる潜在的なリスクがあります。特に、多くの子を持つ親ドキュメントがある場合、一部のシャードが他のシャードよりも大幅に大きくなる可能性があります。これにより、Elasticsearch クラスターの管理とスケーリングが困難になる可能性があります。
再インデックスとクラスターのメンテナンス: データの再インデックスやシャーディング戦略の変更が必要な場合、親子関係によってこのプロセスが複雑になる可能性があります。このような操作中は、関係の整合性が維持されるようにする必要があります。シャードの再調整やノードのアップグレードなどの定期的なクラスター メンテナンス タスクは、より複雑になる可能性があります。これらのプロセス中に親子関係が中断されないように特別な注意を払う必要があります。
Elasticsearch を開発するElastic社は、親子関係に進む前に、アプリケーション側の結合、データの非正規化、および/またはネストされたオブジェクトを実行することを常に推奨しています。
以下の表は、ネストされたフィールド タイプとクエリおよび親子関係の特性を要約し、データ モデリングのアプローチを並べて比較したものです。
| ネストされたフィールドタイプとネストされたクエリ | 親子関係 |
---|---|---|
意味 | オブジェクトを別のオブジェクト内にネストします | 親文書と子文書をリンクします |
人間関係 | 1対1、1対多 | 1対多、多対多 |
クエリ速度 | データは同じブロックとセグメントに保存されるため、一般的に親子関係よりも高速です。 | 通常、クエリ時に親ドキュメントと子ドキュメントが結合されるため、ネストされたオブジェクトよりも 5 ~ 10 倍遅くなります。 |
クエリの柔軟性 | クエリの範囲がネストされた各オブジェクトの境界内に制限されるため、親子クエリよりも柔軟性が低くなります。 | 親ドキュメントまたは子ドキュメントを一緒にまたは個別にクエリできるため、クエリの柔軟性が向上します。 |
データの更新 | ネストされたオブジェクトを更新するには、ドキュメント全体の再インデックスが必要でした。 | すべてのドキュメントを再インデックスする必要がないため、子ドキュメントの更新が簡単になります。 |
管理 | すべてが1つのドキュメントに含まれているため、管理が簡単になります。 | 親ドキュメントと子ドキュメントの関係を個別にインデックス付けして維持する必要があるため、管理がより複雑になります。 |
ユースケース | 複数の階層レベルで複雑なデータを保存およびクエリする | 親が少なく子供が多い関係、例えば製品や製品レビューなど |
Elasticsearch はネストされたクエリや親子関係など、 SQL スタイルの結合に対するいくつかの回避策を提供していますが、これらのモデルは拡張性が低いことがわかっています。大規模なアプリケーションを設計する場合は、ネイティブ SQL 結合機能であるRocksetを使用した代替アプローチを検討するとよいでしょう。
Rockset は、深くネストされた JSON データを含むあらゆるデータに対する SQL 検索、集計、結合用に設計された検索および分析データベースです。データが Rockset にストリーミングされると、データベースのコア データ構造にエンコードされ、データの保存とインデックス作成に使用され、高速な取得が可能になります。Rockset は、SQL ベースのクエリ オプティマイザーを使用して、結合を含む高速クエリを可能にする方法でデータをインデックスします。そのため、SQL 結合をサポートするために事前のデータ モデリングは必要ありません。
Elasticsearch の課題の 1 つは、データが更新されたときに関係を効率的に維持する方法です。その理由の 1 つは、Elasticsearch が Apache Lucene 上に構築されており、不変のセグメントにデータを保存するため、すべてのドキュメントを再インデックスする必要があることです。Rockset は、Meta によってオープンソース化され、データ変更用に構築されたキー値ストアである RocksDB を使用して、ドキュメント全体を再インデックスする必要なく、フィールドレベルの更新を効率的にサポートできるようにします。
Elasticsearch の親子関係アプローチと Rockset のSQL クエリを比較してみましょう。
上記の親子関係の例では、2 つのドキュメント タイプを作成して、複数のコメントを含む投稿をモデル化しました。
投稿または親ドキュメントタイプ
コメントまたは子ドキュメントタイプ
親ドキュメントと子ドキュメントの関係を確立するために、一意の識別子である親 ID を使用しました。クエリ時には、Elasticsearch DSL を使用して特定の投稿のコメントを取得します。
Rockset では、投稿を含むデータは 1 つのコレクション (リレーショナル ワールドのテーブル) に保存され、コメントを含むデータは別のコレクションに保存されます。クエリの実行時には、SQL クエリを使用してデータを結合します。
以下に 2 つのアプローチを並べて示します。
POST /blog/posts/1 { "title": "Elasticsearch Modeling", "content": "A post about data modeling in Elasticsearch" } POST /blog/comments/2?parent=1 { "text": "Great post!" } POST /blog/comments/3?parent=1 { "text": "I learned a lot from this." }
タイトルとすべてのコメントで投稿を取得するには、次のようなクエリを作成する必要があります。
GET /posts/_search { "query": { "bool": { "must": [ { "match": { "title": "Exploring Elasticsearch Models" } } ] } }, "inner_hits": { "_source": ["text"], "name": "comments", "path": "comments" } }
このデータをクエリするには、単純な SQL クエリを記述するだけです。
SELECT p.title, p.content, c.text FROM posts p JOIN comments c ON p.post_id = c.post_id WHERE p.post_id = 1;
アプリケーションで結合する必要がある複数のデータ セットがある場合、Rockset は Elasticsearch よりも簡単でスケーラブルです。また、データの再構築、更新の管理、インデックスの再作成操作が不要なため、操作が簡素化されます。
このブログでは、ワークロードに最適なデータ モデリング アプローチを決定できるようにすることを目的として、Elasticsearch のネストされたフィールド タイプとネストされたクエリ、および親子関係の概要を説明しました。
ネストされたフィールド タイプとクエリは、1 つのドキュメント内で関係が維持される 1 対 1 または 1 対多の関係に役立ちます。これは、関係管理に対するよりシンプルでスケーラブルなアプローチであると考えられています。
親子関係モデルは、1 対多から多対多の関係に適していますが、特に関係を特定のシャードに含める必要があるため、複雑さが増します。
アプリケーションの主な要件の 1 つがリレーションシップのモデリングである場合は、Rockset を検討するとよいでしょう。Rockset はデータ モデリングを簡素化し、SQL 結合を使用してリレーションシップ管理に対してよりスケーラブルなアプローチを提供します。今すぐ 300 ドルのクレジットで無料トライアルを開始して、 Elasticsearch と Rockset のパフォーマンスを比較対照することができます。