圖形查詢最佳做法
本文說明如何運用最佳做法,提升 BigQuery 圖形查詢的效能。
從基數較低的節點開始路徑遍歷
為縮小中繼結果集並加快查詢執行速度,請編寫圖形查詢,讓路徑遍歷從基數較低的節點開始,無論路徑遍歷的方向為何。下列 MATCH 陳述式使用屬性篩選器來減少可能的起始節點數量,而不是計算所有相符項目,然後再進行篩選:
MATCH (p:Person {id: 10})-[own:Owns]->(a:Account)
MATCH (a:Account WHERE balance > 10)<-[own:Owns]-(p:Person)
這對量化路徑查詢特別重要:
MATCH (p:Person {id: 10})-[own:Owns]->{1,3}(a:Account)
使用 ANY 或 ANY SHORTEST 語法檢查連線
量化路徑查詢可能會傳回來源節點和目的地節點之間的重複路徑。如果目標是檢查連線,且不需要所有可能的路徑,請使用 ANY 或 ANY SHORTEST 減少多餘的運算,並提升路徑查詢效率。舉例來說,下列 MATCH 陳述式會使用 ANY SHORTEST,只保留每對節點之間的一條路徑:
MATCH ANY SHORTEST (a1:Account)-[t:Transfers]->{1,3}(a2:Account)
使用方向路徑遍歷
BigQuery 圖表結構定義是有方向性的,也就是說,每個邊緣都有來源節點和目的地節點。雖然圖表查詢語法允許任何方向的路徑遍歷 (例如 -[edge]-),但我們建議使用定向路徑遍歷 (例如 -[edge]-> 或 <-[edge]-),以提升效能。任何方向的路徑遍歷都可能導致效能降低。
下列 MATCH 陳述式會使用任何方向的路徑遍歷:
-- Avoid.
MATCH (a1:Account {id: 7})-[t:Transfers]-(a2:Account)
請改為使用 UNION ALL 合併兩個方向性遍歷:
MATCH (a1:Account {id: 7})-[t:Transfers]->(a2:Account)
...
UNION ALL
...
MATCH (a1:Account {id: 7})<-[t:Transfers]-(a2:Account)
明確指定標籤
如果在查詢中省略節點或邊緣標籤,BigQuery Graph 會列舉所有符合資格的節點和邊緣標籤。這項列舉作業可能會導致掃描的標籤數量超出必要範圍。為避免發生這種情況,請盡可能在查詢中為所有節點和邊緣指定標籤。
舉例來說,下列查詢會指定 Account 和 Transfers 標籤:
GRAPH graph_db.FinGraph
MATCH (a1:Account)-[t:Transfers]->(a2:Account)
RETURN COUNT(*) AS num_transfers;
請勿省略標籤,否則系統可能會掃描節點之間其他不必要的關係。在下列查詢中,a1 可以代表帳戶或人員,而 t 可以代表轉移或帳戶擁有權。
GRAPH graph_db.FinGraph
MATCH (a1)-[t]->(a2)
RETURN COUNT(*) AS num_transfers;
偏好單一 MATCH 陳述式
BigQuery Graph 可讓您在單一圖形查詢中加入多個 MATCH 陳述式。這些陳述式由代表相同節點或邊緣的多重宣告變數連結。不過,使用多個 MATCH 陳述式可能會降低陳述式之間的基數效益。請盡可能使用單一 MATCH 陳述式,以提高執行效能。
舉例來說,下列查詢等效,但第一個查詢的效能較佳,因為它使用單一 MATCH 陳述式:
-- Preferred syntax.
GRAPH graph_db.FinGraph
MATCH
(p:Person {id: 1})-[o:Owns]->
(a:Account)-[t:Transfers]->(a2:Account)
RETURN o.account_id, t.amount;
-- Avoid this syntax.
GRAPH graph_db.FinGraph
MATCH (p:Person {id: 1})-[o:Owns]->(a:Account)
MATCH (a:Account)-[t:Transfers]->(a2:Account)
RETURN o.account_id, t.amount;
限制從高基數節點遍歷的邊緣
查詢圖表時,某些節點的傳入或傳出邊緣數量,可能遠多於其他節點。這類高基數節點有時稱為「超級節點」或「中樞節點」。超級節點可能會導致效能問題,因為透過超級節點的遍歷可能涉及處理大量資料,進而導致資料偏斜和執行時間過長。
如要針對含有超級節點的圖表最佳化查詢,請在 FILTER 子句或 MATCH 中的 WHERE 子句內使用 ROW_NUMBER() 函式,限制查詢從節點或到節點的邊緣數量。如果您不需要完整列舉來自或前往超級節點的所有連線,這項技術就特別實用。
舉例來說,如果 FinGraph 中的某些帳戶有大量交易,您可以使用 ROW_NUMBER() 限制每個 Account 要考慮的 Transfers 邊緣數量,避免查詢效率不彰:
GRAPH graph_db.FinGraph
MATCH (a1:Account)-[e1:Transfers WHERE e1 IN {
GRAPH graph_db.FinGraph
-- Sample 5 edges per source node
MATCH -[selected_e:Transfers]->
FILTER ROW_NUMBER() OVER (
PARTITION BY SOURCE_NODE_ID(selected_e)) < 5
RETURN selected_e
}]->{1,3}(a2:Account)
RETURN COUNT(*) AS cnt;
多重躍點查詢中的中繼節點或邊緣範例
您也可以使用 ROW_NUMBER() 對多重躍點查詢中的中繼節點進行取樣,藉此提升查詢效率。這項技術會限制查詢為每個中繼節點考量的路徑數量,藉此提高效率。如要執行這項操作,請將多重躍點查詢拆分為多個以 NEXT 分隔的 MATCH 陳述式,並在需要取樣的中點套用 ROW_NUMBER():
GRAPH graph_db.FinGraph
MATCH (a1:Account)-[e1:Transfers]->(a2:Account)
-- Sample 5 destination nodes per a1 source node
FILTER ROW_NUMBER() OVER (PARTITION BY ELEMENT_ID(a1)) < 5
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;