こんにちは、プロダクト開発本部の小浦方です。 db tech showcase 2021 2日目の AL12 のセッションである「Amazon Neptune~グラフ構造とクエリの比較~Best PracticesとBad Pattern」のレポートをお届けします。
スピーカー名:アマゾン ウェブ サービス ジャパン合同会社 五十嵐 建平 様
セッション概要:
本セッションでは、AWSが提供するグラフデータベース「Amazon Neptune」の実際のデモを通して、グラフの設計とクエリについて良い例/悪い例を挙げ、性能等を比較しながらBest Practicesについて考察します。
以下のアジェンダで、グラフDBをどのように設計すればよいのか、どうやって性能を最適化すればいいのか、といったことを例を交えてご解説いただきました。
アジェンダ
- グラフDBのデータモデリングプロセス
- グラフDBのクエリ種別とクエリ言語
- グラフDB内部のデータ表現とクエリにおける探索戦略
グラフDBのデータモデリングプロセス
よい使いどころを見つけられれば、グラフDBを使うことで他の方法に比べて計算量を大きく削減することができます。例えば「多対多」の関連が大量にあるようなケースなどは、グラフDBに向いています。ドメインモデルが複雑になる上データセットが巨大であり、スキーマ・構造は固定できず、多くの異なるエンティティがあって更にそれらが様々なパターンで接続され、接続・関連が大量にある中で関連へのクエリをしたい、といったような複雑な状況が現実にしばしば存在します。
もしRDBでそういった状況を表現しようとすると、中間テーブルを含めてたくさんのテーブルをJOINすることになって膨大な計算リソースが必要となってしまいますが、グラフDBを使うことでその困難を解決できる可能性があります。グラフDBが利用できる分野としては、ソーシャルネットワーク、レコメンデーション、ナレッジグラフ、不正検知、製薬・衣料、ネットワーク・IT運用などが挙げられます。
データソースからデータモデルを最初に作ろうとするのは、グラフDBを利用する場合では悪手です。
複雑なドメインモデルで、スキーマも固定できず、エンティティが様々なパターンで接続されているような場合、(RDBMSを設計するときのようにデータソースから「正しい」データモデルが定まらず、)自由に無数のパターンが想定されるからです。最初にクエリ・アプリケーションを想定してデザインするのが、ベストプラクティスになります。
まず最初にアプリケーションの目的を考えます。そうすると、データに対して問いたいことが分かるので、エンティティ・属性・関連、及びそれらに対するクエリが設計できます。必要なデータやモデルが見えてきたところで、ようやくデータソースからの変換を考え、最後にアプリケーションを実装するという順序がよいです。
グラフDBのクエリ種別とクエリ言語
グラフDBへのクエリの種別は、「Global(OLAP)」と「Local(OLTP)」の2つに分けられます。
「Global」は、グラフ全体を読み込んだ上で全体のバランスや構造を考慮する処理で、例えば、欠落しているエンティティやエッジの推測、Graph Neural Network、クラスタリング、PageRankなどが当てはまります。グラフ全体を分析するプロットフォームとしては、Spark GraphX、Deep Grapf Libraryなどがあります。
「Local」は、グラフの一部を対象にルールベースで探索やマッチングをするような処理になります。具体的には、起点ノードを取得し、起点ノードの周辺を探索し、境界パターンによりマッチングするような流れになります。Amazon Neptuneは、グラフの一部(Local)を探索するためのプラットフォームです。
グラフ構造の表現は、「プロパティグラフ」と「Resource Description Framework(RDF)」とがあります。シンプルに表現してLocalで処理をしたい場合は、プロパティグラフを採用することになります。プロパティグラフに対するクエリ言語は、トラバーサル(探索)に特化した「Gremlin」と、SQLに近いかたちで表現できる「openCypher」とがあります。このセッションでは探索に特化したGremlinの話が多くなります。
プロパティグラフは、「頂点(ノード)」、「エッジ(ノードとノードを繋ぐ)」、「プロパティ(ノードやエッジの属性情報)」、「ラベル(ノードをグループ化)」の4要素で構成されます。プロパティグラフは、通常はアプリケーションに特化したモデルであり、データ共有を目的としては設計されません。
Resource Description Framework(RDF)は、トリプル=主語(Subject)+ 述語(Predicate)+目的語(Object)という形でグラフ構造を表現します。RDFは、グローバルURIや共有辞書が自然に定義されるので、既存の Linked Open Dataとの統合や、グラフデータの共有が容易です。
グラフDB内部のデータ表現とクエリにおける戦略
Amazon Neptune内部では、プロパティグラフ・RDFのどちらも同じグラフデータモデルを使用しており、インターフェイスや実行計画だけ異なるような実装になっています。
Graph Localにおける探索戦略
- 起点ノードを取得
- 起点ノードの周辺を探索
- 境界パターンによりマッチ
起点ノードを効率よく得るためには、索引(インデックス)を作成する必要があります。Amazon Neptuneでは、多くの状況に対応できる必要最小限のインデックスが自動的に作成されるのでインデックス設計は不要です。また、ノードに付与された長いテキストを全文検索して起点ノードを取得したいというケースもよくありますが、そのようなケースでは「全文検索をAmazon OpenSearch Serviceと連携する」という解決策があります。
起点ノードの周辺を探索する際は、リンクされた情報を辿るだけで計算量は辿った量に比例するだけになり、結合処理が存在しないのでRDBMSと比べて劇的に軽く柔軟性も高いです。隣接探索時のストレージI/Oを緩和するために、リードレプリカを使うという方策があります。
境界パターンによりマッチする際は、どこで探索を止めるかが問題になります。辿るノードの中にエッジ数が巨大なノード(モンスターノード)が含まれると、探索ノード・エッジ数が直ぐに天文学的な数字になってしまいます。「探索時に幅優先と深さ優先を使い分ける」、「ノードやエッジを意味や時系列で分割・分類することでそもそもモンスターノードを発生させないようにする」といった対策があります。
Amazon Neptuneをチューニングする際は、Neptune notebookで実行計画と統計情報を得ることができます。何が最適化できなかったを出力することも可能です。また、結果が同じクエリでも、起点ノードの違いでインデックスアクセス数が大きく異なって実行時間に大きく差が出ることがあり、そういった点に注目することも実行計画を活用する上でポイントとなります。
まとめ
- グラフDBのデータモデリングプロセス
- クエリから考えましょう
- グラフDBのクエリ種別とクエリ言語
- Graph Globalと Graph Localを同列に扱わないようにして、適切な言語を選択しましょう
- グラフDB内部のデータ表現とクエリにおける戦略
- どのようなグラフが探索されるのかイメージしましょう
- Amazon Neptuneの様々な機能が役立ちます
聴講した感想
個人的に以前からグラフDBに興味はありましたが、(既に用意されているようなサンプルデータではない)実際に現場で出会うデータや課題に対してうまく使うためにはどうしたらよいのか分からず、なかなか手を出せずにいました。このセッションのおかげで、グラフDBを実戦で使う際に、どのようなことを考えて、どのようなステップを踏めばよいのか、具体的にイメージができるようになりました。
グラフDBのデータモデリングは「アプリケーション・クエリから考えるべし」という話がありましたが、ということは、グラフデータからのどのような情報をどのように引き出せるかを色々知っておくことが重要になります。私は統計・機械学習技術の研究開発をしていることが多く、データから価値ある情報を引き出す方法を日頃から考えているので、グラフデータに対する統計・機械学習の応用についてもより興味が出てきました。
db tech showcase2021のアーカイブ動画・資料を12月末まで公開しております! 閲覧はこちらからご覧ください。https://eventregist.com/e/TvkwJoawl5Dy