DSE GraphおよびGraph Analytics
DSE Graphでは、Sparkを使用してOLAPクエリーを実行することができます。
トランザクション負荷が高い際に、多数のローカル・グラフ探索をリアルタイムで実行できます。グラフの密度が高すぎる場合や、分岐要素(グラフの各レベルの接続ノード数)が多すぎる場合、OLTPクエリーに回答するメモリーおよび計算の要件は通常のアプリケーションのワークロードで許容可能な範囲を超えます。このような種類のクエリーは、詳細なクエリーと呼ばれます。
スキャン・クエリーは、グラフ全体またはグラフの大部分にタッチするクエリーです。このようなクエリーは通常、多数の頂点とエッジを探索します。たとえば、友達の友達を検索するソーシャル・ネットワーク・グラフに対するクエリーは、スキャン・クエリーです。
詳細なクエリーやスキャン・クエリーを使用するアプリケーションでは、OLAPクエリーを使用すると、パフォーマンスが向上します。
DSE Graphを使用したOLAPクエリーの実行
DSE Graphで作成されたすべてのグラフには、gremlin-consoleおよびDataStax Studioで使用可能なOLAP探索ソースa
が含まれています。この探索ソースでは、SparkGraphComputerを使用して、クエリーを分析し、基盤となるDSE Analyticsノードに対してクエリーを実行します。ノードは、OLAP探索ソースへのアクセスが可能なGraphおよびSparkで起動される必要があります。Spark Masterからコンソールを実行するか、Gremlin Consoleのyamlファイルのhosts
フィールドにSpark Masterを指定するかのいずれかにより、データ・センターのSpark Masterノードに接続する必要があります。1回限りまたは単一セッションOLAPクエリーの場合、database.a
をg
という別名にして、クエリーを作成します。たとえば、Gremlin Consoleの場合は、次のようになります。
:remote config alias g database.a
g.V().count()
グラフの他の部分に対して複数のクエリーを実行する場合は、graph.snapshot()
を使用して、グラフの各部分についてOLAP探索ソースを返します。たとえば、Gremlin Consoleの場合は、次のようになります。
categories = graph.snapshot().vertices('category1', 'category2').create()
スナップショットを作成するには、スナップショットが探索するすべての頂点を指定します。たとえば、次のクエリーは、Person
とAddress
の両方の頂点にタッチします。
def person = graph.snapshot().vertices('Person', 'Address').create() person.V().hasLabel('Person').out('HAS_ADDRESS').count()
create()
を呼び出す前にスナップショットに対してconf()
メソッドを使用し、TinkerPopのSparkGraphComputer構成オプションを設定します。たとえば、スナップショットのストレージ・レベルを明示的にMEMORY_ONLY
に設定する場合は、次のようになります。
graph.snapshot().vertices("vertexlabel_alice", "vertexlabel_bob").edges("edgelabel_carol").conf("gremlin.spark.persistStorageLevel", "MEMORY_ONLY").create()
GremlinからのSparkプロパティの設定
グラフ上でgraph.configuration.setPropertyメソッドを使用して、GremlinからSparkプロパティを設定することができます。
:remote config alias g database.a
g.graph.configuration.setProperty("property name", value)
デフォルトでは、Sparkアプリケーションはノードで使用可能なすべてのリソースを使用するため、他のSparkアプリケーションを実行することはできません。探索で使用する最大コア数とメモリー容量を設定して、OLAP探索を実行する前にアプリケーションのリソースを制限します。これは、コアの数やメモリーの容量が非常に多いサーバーでは特に重要です。
たとえば、この要求では、1つのコアと4 GBのメモリーをそれぞれ備えた10個のエグゼキューターが設定されます。
:remote config alias g example_graph.a ==>g=example_graph.a
g.graph.configuration.setProperty("spark.cores.max", 10) g.graph.configuration.setProperty("spark.executor.memory", "4g") g.graph.configuration.setProperty("spark.executor.cores", "1")
spark.cores.max
プロパティは、Sparkで使用される最大コア数を設定します。このプロパティをコアの総数よりも低く設定すると、クエリーが実行されるノードの数が制限されます。spark.executor.memory
プロパティは、各エグゼキューターで使用されるメモリー容量を設定します。spark.executor.cores
プロパティは、各エグゼキューターで使用されるコアの数を設定します。
GremlinからSparkプロパティを構成する前に、現在実行中のSparkコンテキストをSpark Web UIから強制終了します。これにより、現在実行中のすべてのGremlin OLAPクエリーが強制終了されます。Spark Web UIで、「Apache TinkerPop's Spark-Gremlin」というアプリケーションを見つけ、アプリケーションIDの横にあるkill
をクリックします。
OLAP探索では、実行中に多くの中間オブジェクトが作成されます。これらのオブジェクトはJVMのガーベージコレクションによって回収されるため、エグゼキューターを減らしてメモリーとCPUリソースを増やすとパフォーマンスが向上するグラフ以外のSparkジョブと比べて、メモリーとCPUリソースをそれぞれ減らした、大規模なエグゼキューターのプールを構成することをお勧めします。
ガーベージ・コレクションによる一時停止を減らし、OLAP探索のパフォーマンスを高めるために、コアが8個未満のエグゼキューターを割り当てることをお勧めします(ほとんどの場合、1個で機能します)。Sparkで使用可能なメモリーは、コア間で均等に分散させる必要があります。たとえば、3つのノードがあり、それぞれのノードで24個のコアと96 GBがSpark専用に割り当てられている場合、コアの総数は24 * 3 = 72個、メモリーの合計は96 GB * 3 = 188 GBになります。すべてのリソースを割り当てるには、72個の単一コアのエグゼキューターと4 GBのメモリーをそれぞれ要求する必要があります。
:remote config alias g example_graph.a ==>g=example_graph.a
g.graph.configuration.setProperty("spark.cores.max", 72)
g.graph.configuration.setProperty("spark.executor.memory", "4g") g.graph.configuration.setProperty("spark.executor.cores", "1")
一部のOLAPクエリーとほとんどのDseGraphFrame
クエリーでは、探索にSpark SQL結合を使用します。Sparkでは、マージ結合を実行するためのパーティションの数が事前定義されており、デフォルトでは200に設定されています。これにより、非常に大きなグラフ用に巨大なSparkパーティションを作成できますが、クエリー実行が遅くなります。
単一パーティションのサイズを小さくし、パフォーマンスを向上させるには、spark.sql.shuffle.partitions
プロパティを大きな数に設定することにより、シャッフル・パーティションの数を増やします。spark.sql.shuffle.partitions
を、Sparkクラスター・コアの数の2~4倍に設定することをお勧めします。したがって、200コア・クラスターの場合、spark.sql.shuffle.partitions
を400または800に設定します。
:remote config alias g example_graph.a ==>g=example_graph.a
g.graph.configuration.setProperty("spark.sql.shuffle.partitions", 500)
分析OLAPクエリーを使用する場合
大規模なグラフでは、通常、詳細なクエリーを実行する場合は、OLAPクエリーの方がパフォーマンスに優れています。ただし、OLTP読み込みの一環として詳細なクエリーを実行する意味があるのは、このクエリーがあまり実行されない場合に限られます。たとえば、オンライン支払いプロバイダーは、支払いを素早く処理するためにOLTPクエリーを好みますが、取引に不正がないか確かめるために詳細なクエリーの実行が必要になる場合があります。詳細なクエリーはOLTPワークロードとして非常に長い時間がかかる可能性がありますが、全体的に見て、アプリケーションのパフォーマンスはアプリケーションをOLTPクエリーとOLAPクエリーに分割するよりも速くなります。
グラフ全体を分析するレコメンデーション・エンジンや検索エンジンなどの長時間の実行されるプロセスや周期的なプロセスでは、OLAPクエリーが理想的なユース・ケースとなります。ただし、詳細なクエリーを呼び出したり、データベース全体をスキャンしたりする1回限りのデータ分析操作でも、OLAPクエリーとして実行する利点がある場合があります。OLTPクエリーとOLAPクエリーのパフォーマンスの違いの詳細については、「DSE Graph、OLTP、およびOLAP」を参照してください。
多数のエッジと頂点を削除する場合のベスト・プラクティス
グラフから多数のエッジや頂点を削除する際、自動で削除される前に非常に多くのトゥームストーンがデータベースに残っていることが原因で、それ以降のクエリーでエラー・メッセージが表示されることがあります。
そのようなエラーのログ・エントリーは以下のようになります。
ERROR [ReadStage-32] 2017-05-18 11:18:29,289 StorageProxy.java:2068 - Scanned over 100001 tombstones during query 'SELECT * FROM t33215.PhoneNumber_p WHERE token(community_id) > -7331398285705078207 AND token(community_id) <= -6858404847917653807 LIMIT 1000' (last scanned row partion key was ((216134144), 1250272)); query aborted
このようなエラーを回避するには、spark.cassandra.input.split.size_in_mb
プロパティをデフォルトの64 MBよりも小さいサイズに設定して、要求あたりのトゥームストーンの数を減らします。spark.cassandra.input.split.size_in_mb
プロパティは、CQLクエリーのたびにSpark Cassandra Connectorが要求するデータ・サイズの概算を設定します。
以下の例は、spark.cassandra.input.split.size_in_mb
プロパティを1 MBに設定し、グラフから電話番号の頂点をすべて削除する方法を示しています。
:remote config alias g example_graph.a
g.graph.configuration.setProperty("spark.cassandra.input.split.size_in_mb", "1") g.V().hasLabel("PhoneNumber").drop().iterate()
DSE認証とOLAPクエリー
DSE認証が有効になっている場合、Graph OLAPクエリーを送信したユーザーではなく、内部ユーザーdse_inproc_user
がアプリケーションを実行します。