本文說明調整 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有許多傳入的轉移作業時。