調整 Spanner 圖表查詢的最佳做法

本文說明調整 Spanner 圖形查詢效能的最佳做法,包括下列最佳化措施:

  • 避免對節點和邊緣的輸入資料表進行完整掃描。
  • 減少查詢需要從儲存空間讀取的資料量。
  • 縮減中繼資料的大小。

從基數較低的節點開始

撰寫路徑遍歷時,請從基數較低的節點開始。 這種做法可縮小中間結果集,並加快查詢執行速度。

舉例來說,下列查詢的語意相同:

  • 正向邊緣遍歷:

    GRAPH FinGraph
    MATCH (p:Person {name:"Alex"})-[:Owns]->(a:Account {is_blocked: true})
    RETURN p.id AS person_id, a.id AS account_id;
    
  • 反向邊緣遍歷:

    GRAPH FinGraph
    MATCH (a:Account {is_blocked:true})<-[:Owns]-(p:Person {name: "Alex"})
    RETURN p.id AS person_id, a.id AS account_id;
    

假設名為「Alex」的人數少於遭封鎖的帳戶數,建議您以前向邊緣遍歷方式撰寫這項查詢。

對於路徑長度可變的遍歷作業,從基數較低的節點開始尤為重要。以下範例說明如何以建議方式,找出與指定帳戶相差三筆轉移記錄的帳戶。

GRAPH FinGraph
MATCH (:Account {id: 7})-[:Transfers]->{1,3}(a:Account)
RETURN a.id;

預設指定所有標籤

如果省略標籤,Spanner Graph 會推斷符合資格的節點和邊緣標籤。建議您盡可能為所有節點和邊緣指定標籤,因為系統不一定能推斷出標籤,而且可能會掃描不必要的標籤。

單一 MATCH 陳述式

以下範例會找出最多透過 3 次轉移與指定帳戶連結的帳戶:

GRAPH FinGraph
MATCH (src:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
RETURN dst.id;

MATCH 陳述式

如果節點和邊緣參照同一個元素,但位於不同的 MATCH 陳述式中,請指定標籤。

以下範例說明建議做法:

GRAPH FinGraph
MATCH (acct:Account {id: 7})-[:Transfers]->{1,3}(other_acct:Account)
RETURN acct, COUNT(DISTINCT other_acct) AS related_accts
GROUP BY acct

NEXT

MATCH (acct:Account)<-[:Owns]-(p:Person)
RETURN p.id AS person, acct.id AS acct, related_accts;

使用 IS_FIRST 最佳化查詢

您可以使用 IS_FIRST 函式對邊緣取樣,並限制圖形中的遍歷,藉此提升查詢效能。這項函式有助於處理高基數節點,並最佳化多跳查詢。

如果指定的樣本大小太小,查詢可能不會傳回任何資料。 因此,您可能需要嘗試不同的樣本大小,找出傳回資料和查詢效能的最佳平衡點。

這些 IS_FIRST 範例使用 FinGraph,這是包含 Account 節點和 Transfers 邊緣的金融圖表,用於轉移資金。如要建立 FinGraph 並使用該項目執行範例查詢,請參閱「設定及查詢 Spanner Graph」。

限制遍歷的邊緣,以提升查詢效能

查詢圖表時,某些節點的傳入或傳出邊緣數量,可能遠多於其他節點。這些高基數節點有時稱為超級節點或中樞節點。超級節點可能會導致效能問題,因為透過超級節點的遍歷可能涉及處理大量資料,進而導致資料偏斜和執行時間過長。

如要最佳化含有超級節點的圖表查詢,請在 FILTER 子句中使用 IS_FIRST 函式,限制查詢從節點遍歷的邊緣數量。因為 FinGraph 中的帳戶交易次數可能遠高於其他帳戶,因此您可能會使用 IS_FIRST 避免查詢效率不彰。如果您不需要從超級節點完整列舉所有連線,這種做法就特別實用。

下列查詢會找出直接或間接從遭封鎖帳戶 (a1) 接收轉移的帳戶 (a2)。查詢會使用 IS_FIRST,限制每個 Account 要考慮的 Transfers 邊緣數量,避免帳戶有大量轉移時效能緩慢。

GRAPH FinGraph
MATCH
(a1:Account {is_blocked: true})
-[e:Transfers WHERE e IN
  {
    MATCH -[selected_e:Transfers]->
    FILTER IS_FIRST(@max_transfers_per_account) OVER (
      PARTITION BY SOURCE_NODE_ID(selected_e)
      ORDER BY selected_e.create_time DESC)
    RETURN selected_e
  }
]->{1,5}
(a2:Account)
RETURN a1.id AS src_id, a2.id AS dst_id;

本範例使用下列項目:

  • @max_transfers_per_account:查詢參數,用於指定每個帳戶 (a1) 要考量的 Transfers 邊緣數量上限。

  • PARTITION BY SOURCE_NODE_ID(selected_e):確保每個帳戶 (a1) 都有各自的 IS_FIRST 限制。

  • ORDER BY selected_e.create_time DESC:指定傳回最近的轉移作業。

取樣中繼節點,以最佳化多重躍點查詢

您也可以使用 IS_FIRST 對多重躍點查詢中的中繼節點取樣,藉此提升查詢效率。這項技術會限制查詢為每個中繼節點考量的路徑數量,藉此提高效率。如要這麼做,請將多重躍點查詢拆分為多個以 NEXT 分隔的 MATCH 陳述式,並在需要取樣的中間點套用 IS_FIRST

GRAPH FinGraph
MATCH (a1:Account {is_blocked: true})-[e1:Transfers]->(a2:Account)
FILTER IS_FIRST(1) OVER (PARTITION BY a2)
RETURN a1, a2

NEXT

MATCH (a2)-[e2:Transfers]->(a3:Account)
RETURN a1.id AS src_id, a2.id AS mid_id, a3.id AS dst_id;

如要瞭解 IS_FIRST 如何最佳化這項查詢,請按照下列步驟操作:

  • 子句 FILTER IS_FIRST(1) OVER (PARTITION BY a2) 會套用至第一個 MATCH 陳述式。

  • 對於每個中繼帳戶節點 (a2),IS_FIRST 只會考慮第一個傳入的 Transfers 邊緣 (e1),減少第二個 MATCH 陳述式中要探索的路徑數量。

  • 由於第二個 MATCH 不會處理不必要的資料,因此整體兩躍點查詢的效率會提高,尤其是當 a2 有許多傳入的轉移作業時。

後續步驟