在 Spanner Graph 上執行演算法的最佳做法

本文說明在 Spanner Graph 上執行演算法的最佳做法。涵蓋下列主題:

演算法適用的圖表結構定義最佳做法

如果您在 Spanner Graph 中使用了自訂標籤和屬性,建議一律將元素鍵公開為屬性。這樣一來,您就能在演算法查詢的 RETURN 子句中存取這些鍵屬性,並使用這些屬性找出演算法產生輸出內容的圖表元素。

處理演算法輸出內容

本節將說明演算法輸出內容的傳回建議,以及傳回結果的保存位置。

需退回的項目

如果演算法的輸出簽章包含圖表元素,Spanner Graph 可讓您傳回這些元素的任何屬性。為降低處理成本,建議您盡量減少要傳回的屬性清單。舉例來說,只傳回元素的鍵屬性,因為鍵是識別元素時的必要條件。

保留位置

Spanner Graph 支援將演算法查詢結果保留在 Cloud Storage 中,或保留在您啟動查詢的相同 Spanner Graph 執行個體中。

將資料存回 Spanner 後,所有 Spanner 作業都能以原生方式存取演算法輸出內容。舉例來說,您可以執行 WeaklyConnectedComponent,將 cluster_id 持久儲存回圖表。隨後,針對具有特定 cluster_id 的輸入圖表執行 PageRank。 Spanner 的變更串流等功能會將新演算法輸出內容傳播至下游系統。如要在 Spanner 中使用演算法輸出內容,建議將內容保留在 Spanner 中。雖然 Spanner Graph 會以有效率的方式批次處理演算法輸出內容,並將其保存至 Spanner,但所有寫入作業都必須透過相同的領導者副本。如果重要工作負載已佔用領導者容量,建議您錯開演算法執行時間,避免與重要工作負載競爭。

如果您不打算在 Spanner 中使用演算法輸出內容,或想在將演算法輸出內容擷取至主要資料來源前進行評估,請考慮將輸出內容保存至 Cloud Storage。請參閱支援的檔案格式

擴增圖表的資料模型注意事項

本節將討論將演算法輸出內容保存至 Spanner 時,需要考量的一些資料模型。

屬性與邊緣

演算法可產生各種洞察資料,包括:

  • 節點層級指標 (例如中心度分數)。這些屬性可以保留為節點上的新屬性。
  • 成對洞察 (例如兩個節點之間的相似度)。這些值可以做為兩個節點之間的新邊緣,並以邊緣屬性形式保存輸出值。
  • 社群偵測演算法會找出圖表中的邏輯社群,並將一或多個社群指派給特定節點。您可以將社群成員資格視為節點上的新屬性,方法是為每個成員節點標記其所屬社群的 ID。您也可以將社群成員資格建模為新的子圖,並將邏輯社群儲存為圖中的新型節點,邊緣會將這些節點連結至成員節點。如果您想在存取節點時瞭解節點所屬的社群,社群屬性可能就已足夠。如要依社群瀏覽 (例如尋找與起始節點位於同一社群的節點),社群子圖可能更適合。視您打算運用社群資訊的方式而定,您可以選擇其中一項或兩項。

預先定義的結構定義與彈性結構定義

將演算法輸出內容保留回 Spanner 之前,請透過下列任一方式,在結構定義中定義目的地資料欄或資料表:

  • 如果演算法的使用情境穩定且已知,您可以預先新增其他資料欄或表格。
  • 如果您要疊代及實驗不同的洞察資料擷取方式 (例如嘗試不同的演算法、參數、輸入子圖),您可能需要彈性的方式來保存及區分多項實驗的輸出內容,而不必為每次執行更新 Spanner 結構定義。在這種情況下,您可以考慮將演算法產生的新屬性和邊緣儲存在一般子資料表中。

如要將演算法產生的新屬性和邊緣儲存在一般子資料表中,請按照下列步驟操作:

步驟 1:建立一般子表格

-- Create `AccountAlgoProperty` as a child table of `Account`, to store all
-- properties produced by algorithms for `Account`.
-- `algo_run_id`: a unique ID for a given algorithm run.
-- `int_val`: column to store integer algorithm output.
-- `float_val`: column to store float algorithm output.
CREATE TABLE AccountAlgoProperty (
 id INT64 NOT NULL,
 algo_run_id STRING(200) NOT NULL,
 int_val INT64,
 float_val FLOAT64
) PRIMARY KEY(id, algo_run_id),
 INTERLEAVE IN PARENT Account;

-- Create `AccountAlgoEdge` as a child table of `Account`, to store all
-- outgoing edges produced by algorithms for `Account`.
-- `algo_run_id`: a unique ID for a given algorithm run.
-- `to_id`: destination `Account` id.
-- `int_val`: column to store integer algorithm output.
-- `float_val`: column to store float algorithm output.
CREATE TABLE AccountAlgoEdge (
 id INT64 NOT NULL,
 algo_run_id STRING(200) NOT NULL,
 to_id INT64 NOT NULL,
 int_val INT64,
 float_val FLOAT64,
 CONSTRAINT FK_AccountId FOREIGN KEY (to_id) REFERENCES Account (id) NOT ENFORCED,
) PRIMARY KEY(id, algo_run_id, to_id),
 INTERLEAVE IN PARENT Account;

步驟 2:將演算法產生的屬性和邊緣儲存為子項資料表列,以及產生這些屬性和邊緣的專屬 algo_run_id

-- Store PageRank score as property in child table.
EXPORT DATA
 OPTIONS (format = "CLOUD_SPANNER",
          table = "AccountAlgoProperty",
          write_mode = 'upsert_ignore_all' ) AS
GRAPH FinGraph
CALL PageRank(node_labels => ['Account'], edge_labels => ['Transfers'])
RETURN node.id, "page_rank_123" AS algo_run_id, score As float_val;

-- Store ShortestPath output as edge in child table.
EXPORT DATA
 OPTIONS (format = "CLOUD_SPANNER",
          table = "AccountAlgoEdge",
          write_mode = 'upsert_ignore_all' ) AS
GRAPH FinGraph
CALL ShortestPath(
    source_nodes => ARRAY {
                      MATCH (n:Account {id: 7})
                      RETURN n
                    },
    target_nodes => ARRAY {
                      MATCH (n:Account {id: 16})
                      RETURN n
                    }
  ) YIELD source_node, target_node, path, cost
RETURN source_node.id AS id, "shortest_path_456" AS algo_run_id,
  target_node.id AS to_id, PATH_LENGTH(path) AS int_val, cost AS float_val;

步驟 3:使用 GRAPH_TABLE 存取演算法輸出內容

SELECT acct.id, prop.float_val AS page_rank_score
FROM GRAPH_TABLE(
  FinGraph
  MATCH (n:Account)
  RETURN n.id
) acct JOIN AccountAlgoProperty prop ON acct.id = prop.id
  AND prop.algo_run_id = 'page_rank_123';

SELECT acct.id, edge.to_id, edge.int_val AS path_len, edge.float_val AS cost
FROM GRAPH_TABLE(
  FinGraph
  MATCH (n:Account)
  RETURN n.id
) acct JOIN AccountAlgoEdge edge ON acct.id = edge.id
  AND edge.algo_run_id = 'shortest_path_456';

步驟 4:(選用) 建立以演算法產生的屬性和邊緣擴增的邏輯圖表,方便存取演算法輸出內容。

-- Create a view that aggregates non-null algorithm properties per node into
-- name-value pairs in JSON.
CREATE OR REPLACE VIEW AccountWithAlgoProperties SQL SECURITY INVOKER AS
SELECT
  n.id, ANY_VALUE(n.create_time) AS create_time,
  ANY_VALUE(n.is_blocked) AS is_blocked, ANY_VALUE(n.nick_name) AS nick_name,
  JSON_OBJECT(
    IF(COUNT(c.algo_run_id) = 0, [], ARRAY_AGG(c.algo_run_id)),
    IF(COUNT(c.algo_run_id) = 0, [], ARRAY_AGG(
      COALESCE(
        IF (c.int_val IS NULL, NULL, TO_JSON(c.int_val)),
        IF (c.float_val IS NULL, NULL, TO_JSON(c.float_val))
      )))) AS algo_props
FROM Account AS n LEFT JOIN AccountAlgoProperty AS c ON n.id = c.id
GROUP BY n.id;

-- Create FinGraphAugmented with algorithm generated properties and edges.
CREATE OR REPLACE PROPERTY GRAPH FinGraphAugmented
  NODE TABLES (
    AccountWithAlgoProperties AS Account
      KEY(id)
      LABEL Account PROPERTIES(
        create_time,
        id,
        is_blocked,
        nick_name,
        algo_props),
    Person
  )
  EDGE TABLES (
    PersonOwnAccount
      SOURCE KEY (id) REFERENCES Person (id)
      DESTINATION KEY (account_id) REFERENCES Account (id)
      LABEL Owns,
    AccountTransferAccount
      SOURCE KEY (id) REFERENCES Account (id)
      DESTINATION KEY (to_id) REFERENCES Account (id)
      LABEL Transfers,
    AccountAlgoEdge
      SOURCE KEY (id) REFERENCES Account (id)
      DESTINATION KEY (to_id) REFERENCES Account (id)
  );

然後直接存取演算法屬性,並在圖形查詢中瀏覽演算法產生的邊緣:

-- Retrieve PageRank score property in graph query.
GRAPH FinGraphAugmented
MATCH (a:Account)
RETURN a.id, a.algo_props.page_rank_123 AS page_rank
ORDER BY page_rank DESC
LIMIT 5;

-- Navigate through ShortestPath edge in graph query.
GRAPH FinGraphAugmented
MATCH (s:Account {id: 7})-[e:AccountAlgoEdge {algo_run_id : 'shortest_path_456'}]->(d:Account)
RETURN s.id AS src, d.id AS dst, e.int_val AS path_len, e.float_val AS cost

針對大型工作負載指定 machine_category

對於大多數工作負載,default machine_category 都很合適。如果輸入圖形有超過 10 億個節點和超過 100 億條邊緣,建議您明確選擇 large 做為 machine_category

重複使用運算資源,加快疊代速度

如果您要對小型資料集進行不同演算法或不同輸入參數的實驗,以便快速疊代,建議使用 max_idle_time。較大的 max_idle_time Spanner Graph 可讓您重複使用已為更多演算法工作負載佈建的運算資源。這可減少隨選運算冷啟動的負擔,並降低因暫時缺乏容量而無法佈建運算的風險。請注意,演算法運算閒置時仍會計費。

區分輸出內容中的元素類型

如果演算法輸出內容包含多種元素類型,您可能會想在輸出內容中加入 ELEMENT_DEFINITION_NAME,以便區分這些類型。例如:

EXPORT DATA OPTIONS (
  uri = "gs://bucket-name/page_rank.csv",
  format = "csv",
  overwrite = TRUE) AS
GRAPH FinGraph
CALL PageRank()
YIELD node, score
RETURN ELEMENT_DEFINITION_NAME(node) AS node_type, node.id, score;

後續步驟