使用 explain 和 profile 調校 Gremlin 查詢 - Amazon Neptune

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

使用 explainprofile 調校 Gremlin 查詢

您通常可以在 Amazon Neptune 中調整 Gremlin 查詢,以使用從 Neptune 說明設定檔 取得的報告中可用的資訊來取得更好的效能APIs。這樣做有助於了解 Neptune 如何處理 Girmlin 周遊。

重要

3.4.11 TinkerPop 版中進行了變更,以提高查詢處理方式的正確性,但目前有時可能會嚴重影響查詢效能。

例如,此類查詢的執行速度可能會明顯變慢:

g.V().hasLabel('airport'). order(). by(out().count(),desc). limit(10). out()

限制步驟之後的頂點現在會以非最佳方式擷取,因為 TinkerPop 3.4.11 變更。若要避免這種情況,您可以在 order().by() 之後的任何點新增 barrier() 步驟來修改查詢。例如:

g.V().hasLabel('airport'). order(). by(out().count(),desc). limit(10). barrier(). out()

TinkerPop 3.4.11 已在 Neptune 引擎 1.0.5.0 版中啟用。

了解 Neptune 中處理的 Gremlin 周遊

將 Grimlin 周遊傳送至 Neptune 時,有三個主要程序可將周遊轉換為基礎執行計畫,供引擎執行。這些是剖析、轉換和最佳化:

這 3 個程序可將 Girmlin 查詢轉換為執行計畫。

周遊剖析程序

處理周遊的第一步是將其剖析為通用語言。在 Neptune TinkerPop API中,該常用語言是屬於 的一組 TinkerPop 步驟。這些步驟的每一個都代表周遊內的計算單位。

您可以將 Girmlin 周遊傳送至 Neptune 做為字串或位元碼。REST 端點和 Java 用戶端驅動程式submit()方法會以字串形式傳送周遊,如本範例所示:

client.submit("g.V()")

使用 Gremlin 語言變體 (GLV) 的應用程式和語言驅動程式會以位元組程式碼傳送周遊。

周遊轉換程序

處理周遊的第二個步驟是將其 TinkerPop 步驟轉換為一組已轉換和未轉換的 Neptune 步驟。Apache TinkerPop Gremlin 查詢語言中的大多數步驟都會轉換為 Neptune 特定的步驟,這些步驟已最佳化為在基礎 Neptune 引擎上執行。在周遊中遇到沒有 Neptune 對等 TinkerPop 項的步驟時, TinkerPop 查詢引擎會處理該步驟和周遊中的所有後續步驟。

如需在哪些情況下可轉換哪些步驟的詳細資訊,請參閱 Gremlin 步驟支援

周遊最佳化程序

周遊處理中的最後一步是透過最佳化工具執行一系列已轉換和未轉換的步驟,以嘗試確定最佳執行計畫。此最佳化的輸出是 Neptune 引擎所處理的執行計畫。

使用 Neptune Gremlin explain API 調整查詢

Neptune 說明與 Gremlin explain() 步驟API不同。它會傳回 Neptune 引擎在執行查詢時將處理的最終執行計畫。因為它不會執行任何處理,所以不論使用哪些參數,都會傳回相同的計畫,而且其輸出不包含任何有關實際執行的統計資料。

考慮以下簡單的周遊,尋找安克拉治的所有機場頂點:

g.V().has('code','ANC')

您可以透過 Neptune 以兩種方式執行此周遊explainAPI。第一個方法是REST呼叫說明端點,如下所示:

curl -X POST https://your-neptune-endpoint:port/gremlin/explain -d '{"gremlin":"g.V().has('code','ANC')"}'

第二種方法是搭配 explain 參數使用 Neptune 工作台的 %%gremlin 儲存格魔法。這會將儲存格內文中包含的周遊傳遞至 Neptune,explainAPI然後在執行儲存格時顯示產生的輸出:

%%gremlin explain g.V().has('code','ANC')

產生的explainAPI輸出說明 Neptune 的周遊執行計畫。如您在下圖中所見,該計畫包含了處理管道中 3 個步驟中的每個步驟:

說明簡易 Gremlin 周遊的API輸出。

透過查看未轉換的步驟來調校周遊

在 Neptune explainAPI輸出中要尋找的首要任務之一,就是未轉換為 Neptune 原生步驟的 Gremlin 步驟。在查詢計畫中,當遇到無法轉換為 Neptune 原生步驟的步驟時,它和計畫中的所有後續步驟都會由 Gramlin 伺服器處理。

在上面的範例中,已轉換周遊中的所有步驟。讓我們來檢查此周遊的explainAPI輸出:

g.V().has('code','ANC').out().choose(hasLabel('airport'), values('code'), constant('Not an airport'))

如您在下圖中所見,Neptune 無法轉換 choose() 步驟:

說明無法轉換所有步驟的API輸出。

您可以做幾件事來調校周遊的效能。第一件事是以這樣的方式重寫它,以消除無法轉換的步驟。另一件事是將步驟移至周遊尾端,以便所有其他步驟都可以轉換為原生步驟。

具有未轉換之步驟的查詢計畫並不一定需要調校。如果無法轉換的步驟位於周遊尾端,並且與輸出的格式化方式相關,而不是圖形的周遊方式,它們可能對效能沒有什麼影響。

檢查 Neptune 輸出時需要注意的另一件事explainAPI是不使用索引的步驟。以下周遊會尋找有航班降落在安克拉治的所有機場:

g.V().has('code','ANC').in().values('code')

此周遊API的說明輸出為:

******************************************************* Neptune Gremlin Explain ******************************************************* Query String ============ g.V().has('code','ANC').in().values('code') Original Traversal ================== [GraphStep(vertex,[]), HasStep([code.eq(ANC)]), VertexStep(IN,vertex), PropertiesStep([code],value)] Converted Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(PropertyValue) { JoinGroupNode { PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .] PatternNode[(?1, <code>, "ANC", ?) . project ask .] PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .] PatternNode[(?3, <~label>, ?4, <~>) . project ask .] PatternNode[(?3, ?7, ?8, <~>) . project ?3,?8 . ContainsFilter(?7 in (<code>)) .] }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9} }, NeptuneTraverserConverterStep ] Optimized Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(PropertyValue) { JoinGroupNode { PatternNode[(?1, <code>, "ANC", ?) . project ?1 .], {estimatedCardinality=1} PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=INFINITY} PatternNode[(?3, ?7=<code>, ?8, <~>) . project ?3,?8 .], {estimatedCardinality=7564} }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9} }, NeptuneTraverserConverterStep ] Predicates ========== # of predicates: 26 WARNING: reverse traversal with no edge label(s) - .in() / .both() may impact query performance

因為無法使用 Neptune 維護的 3 個索引之一,處理周遊中的 in() 步驟,所以輸出底部出現 WARNING 訊息 (請參閱 陳述式在 Neptune 中如何編製索引Neptune 中的 Gremlin 陳述式)。因為 in() 步驟沒有包含任何邊緣篩選條件,所以無法使用 SPOGPOGSGPSO 索引來剖析它。相反地,Neptune 必須執行聯合掃描以尋找請求的頂點,效率要低得多。

在此情況下,有兩種方法可以調校周遊。第一種方法是將一或多個篩選條件新增至 in() 步驟,以便可以使用已編製索引的查詢來剖析查詢。對於上面範例,這可能是:

g.V().has('code','ANC').in('route').values('code')

修訂周遊explainAPI的 Neptune 輸出不再包含WARNING訊息:

******************************************************* Neptune Gremlin Explain ******************************************************* Query String ============ g.V().has('code','ANC').in('route').values('code') Original Traversal ================== [GraphStep(vertex,[]), HasStep([code.eq(ANC)]), VertexStep(IN,[route],vertex), PropertiesStep([code],value)] Converted Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(PropertyValue) { JoinGroupNode { PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .] PatternNode[(?1, <code>, "ANC", ?) . project ask .] PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) . ContainsFilter(?5 in (<route>)) .] PatternNode[(?3, <~label>, ?4, <~>) . project ask .] PatternNode[(?3, ?7, ?8, <~>) . project ?3,?8 . ContainsFilter(?7 in (<code>)) .] }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9} }, NeptuneTraverserConverterStep ] Optimized Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(PropertyValue) { JoinGroupNode { PatternNode[(?1, <code>, "ANC", ?) . project ?1 .], {estimatedCardinality=1} PatternNode[(?3, ?5=<route>, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=32042} PatternNode[(?3, ?7=<code>, ?8, <~>) . project ?3,?8 .], {estimatedCardinality=7564} }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9} }, NeptuneTraverserConverterStep ] Predicates ========== # of predicates: 26

如果您正在執行許多此類周遊,則另一個選項是在已啟用選用 OSGP 索引的 Neptune 資料庫叢集中執行它們 (請參閱 啟用OSGP索引)。啟用 OSGP 索引有缺點:

  • 在載入任何資料之前,必須在資料庫叢集中啟用它。

  • 頂點和邊緣的插入速率可能會降低多達 23%。

  • 儲存體用量將增加 20% 左右。

  • 將請求分散到所有索引的讀取查詢可能會增加延遲。

有一個 OSGP 索引對於一組受限制的查詢模式很有意義,但除非您經常執行這些模式,否則通常最好嘗試確保您撰寫的周遊可以使用三個主要索引來剖析。

使用大量述詞

Neptune 會將圖形中的每個邊緣標籤和每個不同的頂點或邊屬性名稱視為述詞,並且根據預設,設計為使用相當少量的不同述詞。當您的圖形資料中有幾千個以上的述詞時,效能可能會降低。

若是這種情況,Neptune explain 輸出將會警告您:

Predicates ========== # of predicates: 9549 WARNING: high predicate count (# of distinct property names and edge labels)

如果不方便重新設計資料模型以減少標籤和屬性的數目,從而減少述詞的數目,則調校周遊的最佳方法是在已啟用 OSGP 索引的資料庫叢集中執行它們,如上所述。

使用 Neptune Gremlin profileAPI來調校周遊

Neptune 與 Gremlin profile() 步驟profileAPI截然不同。如同 explain API,其輸出包含 Neptune 引擎在執行周遊時使用的查詢計畫。此外,鑑於其參數的設定方式,profile 輸出還包括周遊的實際執行統計資料。

再次採取簡單的周遊,尋找安克拉治的所有機場頂點:

g.V().has('code','ANC')

explain 一樣API,您可以使用 profileAPIREST呼叫叫用 :

curl -X POST https://your-neptune-endpoint:port/gremlin/profile -d '{"gremlin":"g.V().has('code','ANC')"}'

您也可以搭配 profile 參數使用 Neptune 工作台的 %%gremlin 儲存格魔法。這會將儲存格內文中包含的周遊傳遞至 Neptune,profileAPI然後在執行儲存格時顯示產生的輸出:

%%gremlin profile g.V().has('code','ANC')

產生的profileAPI輸出包含 Neptune 的周遊執行計畫,以及計畫執行的統計資料,如您在此映像所示:

Neptune profileAPI輸出的範例。

profile 輸出中,執行計畫區段僅包含周遊的最終執行計畫,而不包含中繼步驟。管道區段包含已執行的實體管道操作,以及周遊執行所花費的實際時間 (以毫秒為單位)。執行期指標在比較兩個不同版本的周遊在您最佳化它們時所花費的時間非常有用。

注意

周遊的初始執行期通常比後續執行期長,因為第一個執行期會導致快取相關資料。

profile 輸出的第三個區段包含執行統計資料和周遊的結果。若要了解此資訊在調校周遊時如何有用,請考慮以下周遊,它會尋找名稱以 "Anchora" 開頭的每個機場,以及從這些機場二趟短航程即可到達的所有機場,同時傳回機場代碼、航線和距離:

%%gremlin profile g.withSideEffect("Neptune#fts.endpoint", "{your-OpenSearch-endpoint-URL"). V().has("city", "Neptune#fts Anchora~"). repeat(outE('route').inV().simplePath()).times(2). project('Destination', 'Route'). by('code'). by(path().by('code').by('dist'))

Neptune profileAPI輸出中的周遊指標

所有 profile 輸出中可用的第一組指標是周遊指標。這些類似於 Gemlin profile() 步驟指標,但有一些差異:

Traversal Metrics ================= Step Count Traversers Time (ms) % Dur ------------------------------------------------------------------------------------------------------------- NeptuneGraphQueryStep(Vertex) 3856 3856 91.701 9.09 NeptuneTraverserConverterStep 3856 3856 38.787 3.84 ProjectStep([Destination, Route],[value(code), ... 3856 3856 878.786 87.07 PathStep([value(code), value(dist)]) 3856 3856 601.359 >TOTAL - - 1009.274 -

周遊指標資料表的第一個資料行列出周遊所執行的步驟。前兩個步驟通常是 Neptune 特定的步驟,即 NeptuneGraphQueryStepNeptuneTraverserConverterStep

NeptuneGraphQueryStep 代表可由 Neptune 引擎以原生方式轉換和執行之周遊整個部分的執行時間。

NeptuneTraverserConverterStep 代表將這些轉換步驟的輸出轉換為周遊者的程序,這些周 TinkerPop 遊者允許處理無法轉換步驟的步驟,如果有的話,或將結果傳回為 TinkerPop相容的格式。

在上述範例中,我們有數個未轉換的步驟,因此我們看到每個 TinkerPop 步驟 (ProjectStepPathStep) 都會以資料列的形式出現在資料表中。

資料表中的第二欄 會Count報告通過步驟的表示周遊者數量,而第三欄 Traversers會報告通過該步驟的周遊者數量,如TinkerPop設定檔步驟文件 中所述。

在我們的範例中,有 3,856 個頂點和 3,856 個由 NeptuneGraphQueryStep 傳回的周遊,並且這些數目在整個剩餘處理過程中保持不變,因為 ProjectStepPathStep 正在格式化結果,而不是篩選它們。

注意

與 不同 TinkerPop,Neptune 引擎不會透過大量分配NeptuneGraphQueryStepNeptuneTraverserConverterStep 步驟來最佳化效能。Bulking TinkerPop是結合相同頂點上的周遊者以減少操作額外負荷的操作,而這正是導致 CountTraversers 數字差異的原因。由於大量處理只會在 Neptune 委派給 的步驟中發生 TinkerPop,而不是在 Neptune 原生處理的步驟中發生,因此 CountTraverser 資料欄很少有差異。

時間資料行會報告步驟所花費的毫秒數,而 % Dur 資料行則報告步驟所花費的總處理時間百分比。這些指標會透過顯示花費最多時間的步驟,告訴您將調校工作集中在哪裡。

Neptune profileAPI輸出中的索引操作指標

Neptune 設定檔輸出中的另一組指標API是索引操作:

Index Operations ================ Query execution: # of statement index ops: 23191 # of unique statement index ops: 5960 Duplication ratio: 3.89 # of terms materialized: 0

這些報告:

  • 索引查閱的總數。

  • 已執行的唯一索引查詢數目。

  • 索引查詢總數與唯一索引查詢數目的比率。較低的比率指示較少的冗餘。

  • 從字詞字典具體化的字詞數目。

在 Neptune profileAPI輸出中重複指標

如果您的周遊使用上述範例中的 repeat() 步驟,則輸出中會出現包含重複指標的區段:profile

Repeat Metrics ============== Iteration Visited Output Until Emit Next ------------------------------------------------------ 0 2 0 0 0 2 1 53 0 0 0 53 2 3856 3856 3856 0 0 ------------------------------------------------------ 3911 3856 3856 0 55

這些報告:

  • 資料列的迴圈計數 (Iteration 資料行)。

  • 迴圈造訪的元素數目 (Visited 資料行)。

  • 迴圈輸出的元素數目 (Output 資料行)。

  • 迴圈輸出的最後一個元素 (Until 資料行)。

  • 迴圈發出的元素數目 (Emit 資料行)。

  • 從迴圈傳遞至後續迴圈的元素數目 (Next 資料行)。

這些重複指標對於理解周遊的分支因素非常有幫助,如此可讓您開始了解資料庫正在完成多少工作。您可以使用這些數字來診斷效能問題,尤其是當相同的周遊搭配不同的參數執行有很大不同時。

Neptune profileAPI輸出中的全文搜尋指標

當周遊使用全文搜尋查詢時,如上述範例所示,包含全文搜尋 (FTS) 指標的區段會顯示在profile輸出中:

FTS Metrics ============== SearchNode[(idVar=?1, query=Anchora~, field=city) . project ?1 .], {endpoint=your-OpenSearch-endpoint-URL, incomingSolutionsThreshold=1000, estimatedCardinality=INFINITY, remoteCallTimeSummary=[total=65, avg=32.500000, max=37, min=28], remoteCallTime=65, remoteCalls=2, joinTime=0, indexTime=0, remoteResults=2} 2 result(s) produced from SearchNode above

這會顯示傳送至 ElasticSearch (ES) 叢集的查詢,並報告與 互動的數個指標 ElasticSearch ,可協助您找出與全文搜尋相關的效能問題:

  • ElasticSearch 索引中呼叫的摘要資訊:

    • 滿足 remoteCalls 查詢所需的毫秒總數 (total)。

    • 在 remoteCall () 中花費的平均毫秒數avg

    • 在 remoteCall () 中花費的毫秒數下限min

    • 在 remoteCall () 中花費的毫秒數上限max

  • remoteCalls 至 ElasticSearch () 所耗用的總時間remoteCallTime

  • remoteCalls 設為 ElasticSearch (remoteCalls) 的 數目。

  • 結果聯結 ElasticSearch () 所花費的毫秒數joinTime

  • 進行索引查詢所花費的毫秒數 (indexTime)。

  • ElasticSearch () 傳回的結果總數remoteResults