本文說明如何運用設計 Spanner Graph 結構定義的最佳做法,建立有效率的查詢。您可以疊代結構定義設計,因此建議您先找出重要查詢模式,做為結構定義設計的指引。
如要瞭解 Spanner 結構定義設計最佳做法的一般資訊,請參閱「結構定義設計最佳做法」。
最佳化邊緣遍歷
邊緣遍歷是指透過圖形的邊緣進行導覽的程序,從特定節點開始,沿著相連的邊緣移動,抵達其他節點。邊緣的方向是由結構定義決定。邊緣遍歷是 Spanner Graph 的基本作業,因此提升邊緣遍歷效率是應用程式效能的關鍵。
您可以沿著邊緣在兩個方向上移動:
正向邊緣遍歷:追蹤來源節點的外向邊緣。
反向邊緣遍歷:追蹤目的地節點的傳入邊緣。
以下列查詢為例,針對特定人員執行 Owns
邊緣的前向邊緣遍歷:
GRAPH FinGraph
MATCH (person:Person {id: 1})-[owns:Owns]->(accnt:Account)
RETURN accnt.id;
假設有一個帳戶,下列範例查詢會對 Owns
邊緣執行反向邊緣遍歷:
GRAPH FinGraph
MATCH (accnt:Account {id: 1})<-[owns:Owns]-(person:Person)
RETURN person.name;
使用交錯處理功能,最佳化前向邊緣遍歷
如要提升正向邊緣遍歷效能,請將邊緣輸入資料表交錯插入來源節點輸入資料表,將邊緣與來源節點共置。交錯是 Spanner 的儲存空間最佳化技術,可將子項資料表資料列與儲存空間中對應的父項資料列,實際儲存在同一個位置。如要進一步瞭解交錯,請參閱結構定義總覽。
以下範例會說明這些最佳做法:
CREATE TABLE Person (
id INT64 NOT NULL,
name STRING(MAX),
) PRIMARY KEY (id);
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
使用外部鍵最佳化反向邊緣遍歷
如要有效率地遍歷反向邊緣,請在邊緣和目的地節點之間建立強制執行的外部鍵限制。這個強制執行的外部索引鍵會在邊緣建立次要索引,並以目的地節點鍵做為索引鍵。查詢執行期間會自動使用次要索引。
以下範例會說明這些最佳做法:
CREATE TABLE Person (
id INT64 NOT NULL,
name STRING(MAX),
) PRIMARY KEY (id);
CREATE TABLE Account (
id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (id);
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
CONSTRAINT FK_Account FOREIGN KEY (account_id) REFERENCES Account (id),
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
使用次要索引最佳化反向邊緣遍歷
如果您不想在邊緣建立強制執行的外部鍵 (例如因為強制執行的嚴格資料完整性),可以直接在邊緣輸入資料表上建立次要索引,如下列範例所示:
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
CREATE INDEX AccountOwnedByPerson
ON PersonOwnAccount (account_id), INTERLEAVE IN Account;
INTERLEAVE IN
會在次要索引和交錯的資料表 (本例中為 Account
) 之間宣告資料位置關係。交錯式索引會將 AccountOwnedByPerson
次要索引的資料列與 Account
資料表的對應資料列儲存在同個位置。如要進一步瞭解交錯,請參閱「父項子項資料表關係」。如要進一步瞭解交錯式索引,請參閱「索引和交錯式索引」。
使用資訊外鍵最佳化邊緣遍歷
如果您的情境有因強制執行的外部鍵而導致的寫入效能瓶頸,例如經常更新具有許多連線邊緣的中心節點,請考慮使用資訊外部鍵。在邊緣資料表的參照資料欄上使用資訊外鍵,有助於查詢最佳化工具捨棄多餘的節點資料表掃描。不過,由於資訊外鍵不需要邊緣資料表上的次要索引,因此當查詢嘗試使用結束節點尋找邊緣時,資訊外鍵不會提升查詢速度。詳情請參閱「比較外鍵類型」。
請務必瞭解,如果應用程式無法確保參照完整性,使用資訊外鍵進行查詢最佳化可能會導致查詢結果不正確。
下列範例會在 account_id
資料欄上建立具有資訊性外鍵的資料表:
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
CONSTRAINT FK_Account FOREIGN KEY (account_id)
REFERENCES Account (id) NOT ENFORCED
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
如果無法交錯,可以利用資訊性外鍵標示兩個邊緣參照,如下例所示:
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
CONSTRAINT FK_Person FOREIGN KEY (id)
REFERENCES Person (id) NOT ENFORCED,
CONSTRAINT FK_Account FOREIGN KEY (account_id)
REFERENCES Account (id) NOT ENFORCED
) PRIMARY KEY (id, account_id);
不允許懸空邊緣
懸空邊緣是指連接的節點少於兩個的邊緣。如果刪除節點時未移除相關聯的邊緣,或是建立邊緣時未正確連結至節點,就可能發生懸空邊緣。
禁止懸空邊緣有以下好處:
- 強制執行圖形結構完整性。
- 避免額外工作,篩除端點不存在的邊緣,進而提升查詢效能。
使用參照限制禁止懸空邊緣
如要禁止懸空邊緣,請在兩個端點上指定限制:
- 將邊緣輸入資料表交錯插入來源節點輸入資料表。這種做法可確保邊緣的來源節點一律存在。
- 在邊緣上建立強制執行的外部鍵限制,確保邊緣的目的地節點一律存在。
下列範例使用交錯和強制執行的外鍵,確保參考完整性:
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
CONSTRAINT FK_Account FOREIGN KEY (account_id) REFERENCES Account (id) ON DELETE CASCADE,
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
使用 ON DELETE CASCADE,在刪除節點時自動移除邊緣
使用交錯或強制執行的外部鍵來禁止懸空邊緣時,如要刪除仍附加邊緣的節點,請使用 ON DELETE
子句控制行為。詳情請參閱「交錯式資料表的連鎖刪除作業」和「外部鍵動作」。
你可以透過下列方式使用 ON DELETE
:
ON DELETE NO ACTION
(或省略ON DELETE
子句):如果節點有邊,刪除節點就會失敗。ON DELETE CASCADE
:刪除節點會自動移除同一交易中的相關邊緣。
刪除連接不同類型節點的邊緣時,會連帶刪除這些節點
刪除來源節點時一併刪除邊緣。舉例來說,
INTERLEAVE IN PARENT Person ON DELETE CASCADE
會從要刪除的Person
節點中刪除所有外向PersonOwnAccount
邊緣。詳情請參閱「建立交錯式資料表」。刪除目的地節點時,一併刪除邊緣。舉例來說,
CONSTRAINT FK_Account FOREIGN KEY(account_id) REFERENCES Account(id) ON DELETE CASCADE
會刪除所有連入要刪除的Account
節點的PersonOwnAccount
邊緣。
刪除連接相同類型節點的邊緣的層疊
如果邊緣的來源和目的地節點具有相同類型,且邊緣交錯到來源節點中,則只能為來源節點或目的地節點 (但不能同時為兩個節點) 定義 ON DELETE CASCADE
。
如要在這兩種情況中移除懸空邊緣,請在邊緣來源節點參照上建立強制執行的外部鍵,而不是將邊緣輸入資料表交錯插入來源節點輸入資料表。
建議您交錯執行,盡量減少前向邊緣遍歷。請務必先確認工作負載是否會受到影響,再繼續操作。請參閱以下範例,其中使用 AccountTransferAccount
做為邊緣輸入資料表:
--Define two Foreign Keys, each on one end Node of Transfer Edge, both with ON DELETE CASCADE action:
CREATE TABLE AccountTransferAccount (
id INT64 NOT NULL,
to_id INT64 NOT NULL,
amount FLOAT64,
create_time TIMESTAMP NOT NULL,
order_number STRING(MAX),
CONSTRAINT FK_FromAccount FOREIGN KEY (id) REFERENCES Account (id) ON DELETE CASCADE,
CONSTRAINT FK_ToAccount FOREIGN KEY (to_id) REFERENCES Account (id) ON DELETE CASCADE,
) PRIMARY KEY (id, to_id);
使用次要索引依節點或邊緣屬性篩選
次要索引對於有效處理查詢至關重要。您不必遍歷整個圖形結構,就能根據特定屬性值快速查詢節點和邊緣。處理大型圖表時,這點非常重要,因為遍歷所有節點和邊緣的效率可能很低。
依屬性加快篩選節點的速度
如要加快依節點屬性篩選的速度,請在屬性上建立次要索引。舉例來說,下列查詢會找出特定暱稱的帳戶。如果沒有次要索引,系統會掃描所有 Account
節點,找出符合篩選條件的節點。
GRAPH FinGraph
MATCH (acct:Account)
WHERE acct.nick_name = "abcd"
RETURN acct.id;
如要加快查詢速度,請在篩選的屬性上建立次要索引,如下列範例所示:
CREATE TABLE Account (
id INT64 NOT NULL,
create_time TIMESTAMP,
is_blocked BOOL,
nick_name STRING(MAX),
) PRIMARY KEY (id);
CREATE INDEX AccountByNickName
ON Account (nick_name);
提示:針對稀疏屬性使用 NULL 篩選索引。詳情請參閱「停用 NULL 值的索引編製」。
透過篩選邊緣屬性,加快轉送邊緣遍歷速度
在邊緣屬性上進行篩選時,您可以建立邊緣屬性的次要索引,並將索引交錯插入來源節點,藉此加快查詢速度。
舉例來說,下列查詢會尋找特定人員在特定時間之後擁有的帳戶:
GRAPH FinGraph
MATCH (person:Person)-[owns:Owns]->(acct:Account)
WHERE person.id = 1
AND owns.create_time >= PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008")
RETURN acct.id;
根據預設,這項查詢會讀取指定人員的所有邊緣,然後篩選出符合 create_time
條件的邊緣。
以下範例說明如何透過在邊緣來源節點參照 (id
) 和邊緣屬性 (create_time
) 上建立次要索引,提升查詢效率。將索引交錯於來源節點輸入資料表下方,即可將索引與來源節點共置。
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
CREATE INDEX PersonOwnAccountByCreateTime
ON PersonOwnAccount (id, create_time)
INTERLEAVE IN Person;
使用這種方法,查詢可以有效率地找出所有滿足 create_time
條件的邊。
透過邊緣屬性篩選功能,加快反向邊緣遍歷速度
在依屬性篩選反向邊緣時,您可以建立次要索引,使用目的地節點和邊緣屬性進行篩選,加快查詢速度。
下列查詢範例會執行反向邊緣遍歷,並根據邊緣屬性進行篩選:
GRAPH FinGraph
MATCH (acct:Account)<-[owns:Owns]-(person:Person)
WHERE acct.id = 1
AND owns.create_time >= PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008")
RETURN person.id;
如要使用次要索引加快這項查詢的速度,請使用下列其中一個選項:
在邊緣目的地節點參照 (
account_id
) 和邊緣屬性 (create_time
) 上建立次要索引,如下列範例所示:CREATE TABLE PersonOwnAccount ( id INT64 NOT NULL, account_id INT64 NOT NULL, create_time TIMESTAMP, ) PRIMARY KEY (id, account_id), INTERLEAVE IN PARENT Person ON DELETE CASCADE; CREATE INDEX PersonOwnAccountByCreateTime ON PersonOwnAccount (account_id, create_time);
這種做法可提升效能,因為反向邊緣會依
account_id
和create_time
排序,查詢引擎就能有效率地找出符合create_time
條件的account_id
邊緣。不過,如果不同的查詢模式會篩選不同的屬性,則每個屬性可能都需要個別索引,這會增加額外負擔。在邊緣目的地節點參照 (
account_id
) 上建立次要索引,並將邊緣屬性 (create_time
) 儲存在儲存資料欄中,如下列範例所示:CREATE TABLE PersonOwnAccount ( id INT64 NOT NULL, account_id INT64 NOT NULL, create_time TIMESTAMP, ) PRIMARY KEY (id, account_id), INTERLEAVE IN PARENT Person ON DELETE CASCADE; CREATE INDEX PersonOwnAccountByCreateTime ON PersonOwnAccount (account_id) STORING (create_time);
這種方法可以儲存多個屬性,但查詢必須讀取目的地節點的所有邊緣,然後篩選邊緣屬性。
如要結合這些方法,請按照下列規範操作:
- 如果效能至關重要的查詢會使用邊緣屬性,請在索引欄中使用這些屬性。
- 如果屬性用於對效能較不敏感的查詢,請將屬性新增至儲存資料欄。
使用標籤和屬性建立節點和邊緣類型模型
節點和邊緣類型通常會以標籤建立模型。不過,您也可以使用屬性來模擬型別。舉例來說,假設您有多種不同類型的帳戶,例如 BankAccount
、InvestmentAccount
和 RetirementAccount
。您可以將帳戶儲存在不同的輸入資料表中,並將其建模為不同的標籤,也可以將帳戶儲存在單一輸入資料表中,並使用屬性來區分類型。
請先為附有標籤的型別建立模型,然後開始建模程序。建議您在下列情況下使用屬性。
改善結構定義管理
如果圖表有多種不同的節點和邊緣類型,管理每個節點和邊緣類型的個別輸入資料表可能會很困難。為簡化結構定義管理作業,請將型別設為屬性。
物業中的模型類型,可管理經常變更的類型
將型別當做標籤建立模型時,新增或移除型別需要變更結構定義。如果在短時間內執行過多結構定義更新,Spanner 可能會節流處理佇列中的結構定義更新。詳情請參閱限制結構定義更新頻率。
如果需要經常變更結構定義,建議您在屬性中建立類型模型,以規避結構定義更新頻率的限制。
加快查詢速度
如果節點或邊緣模式參照多個標籤,使用屬性建立模型可能會加快查詢速度。以下查詢範例會找出 Person
擁有的所有 SavingsAccount
和 InvestmentAccount
執行個體,假設帳戶類型是以標籤建立模型:
GRAPH FinGraph
MATCH (:Person {id: 1})-[:Owns]->(acct:SavingsAccount|InvestmentAccount)
RETURN acct.id;
acct
節點模式會參照兩個標籤。如果這是效能關鍵查詢,請考慮使用屬性對 Account
進行建模。如下列查詢範例所示,這種做法可能會提高查詢效能。建議您為這兩項查詢建立基準。
GRAPH FinGraph
MATCH (:Person {id: 1})-[:Owns]->(acct:Account)
WHERE acct.type IN ("Savings", "Investment")
RETURN acct.id;
在節點元素鍵中儲存型別,加快查詢速度
如要加快查詢速度,並在以屬性建立節點類型模型時篩選節點類型,且類型在節點生命週期內不會變更,請按照下列步驟操作:
- 將屬性加入節點元素鍵。
- 在邊緣輸入表格中新增節點類型。
- 在邊緣參照鍵中加入節點類型。
下列範例會將這項最佳化作業套用至 Account
節點和 AccountTransferAccount
邊緣。
CREATE TABLE Account (
type STRING(MAX) NOT NULL,
id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (type, id);
CREATE TABLE AccountTransferAccount (
type STRING(MAX) NOT NULL,
id INT64 NOT NULL,
to_type STRING(MAX) NOT NULL,
to_id INT64 NOT NULL,
amount FLOAT64,
create_time TIMESTAMP NOT NULL,
order_number STRING(MAX),
) PRIMARY KEY (type, id, to_type, to_id),
INTERLEAVE IN PARENT Account ON DELETE CASCADE;
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
Account
)
EDGE TABLES (
AccountTransferAccount
SOURCE KEY (type, id) REFERENCES Account
DESTINATION KEY (to_type, to_id) REFERENCES Account
);
在節點和邊緣設定存留時間
Spanner 的存留時間 (TTL) 機制可支援在指定時間過後,自動刪除資料。這類資料通常具有有限的生命週期或相關性,例如工作階段資訊、臨時快取或事件記錄。在這些情況下,TTL 有助於維持資料庫大小和效能。
以下範例使用 TTL,在帳戶關閉 90 天後刪除帳戶:
CREATE TABLE Account (
id INT64 NOT NULL,
create_time TIMESTAMP,
close_time TIMESTAMP,
) PRIMARY KEY (id),
ROW DELETION POLICY (OLDER_THAN(close_time, INTERVAL 90 DAY));
如果節點資料表有 TTL,且交錯式邊緣資料表位於其中,則交錯式資料表必須以 ON DELETE CASCADE
定義。同樣地,如果節點資料表有 TTL,且邊緣資料表透過外鍵參照該節點資料表,則外鍵必須定義為 ON DELETE CASCADE
,才能維持參照完整性,或是定義為資訊外鍵,允許懸空邊緣存在。
在以下範例中,AccountTransferAccount
最多會儲存十年,刪除帳戶時,系統也會一併刪除轉移記錄。
CREATE TABLE AccountTransferAccount (
id INT64 NOT NULL,
to_id INT64 NOT NULL,
amount FLOAT64,
create_time TIMESTAMP NOT NULL,
order_number STRING(MAX),
) PRIMARY KEY (id, to_id),
INTERLEAVE IN PARENT Account ON DELETE CASCADE,
ROW DELETION POLICY (OLDER_THAN(create_time, INTERVAL 3650 DAY));
合併節點和邊緣輸入表格
您可以使用同一個輸入表格,在結構定義中定義多個節點和邊緣。
在下列範例資料表中,Account
節點具有複合鍵 (owner_id, account_id)
。當 id
等於 owner_id
時,具有鍵 (id
) 的 Person
節點會擁有具有複合鍵 (owner_id, account_id)
的 Account
節點,因此存在隱含邊緣定義。
CREATE TABLE Person (
id INT64 NOT NULL,
) PRIMARY KEY (id);
-- Assume each account has exactly one owner.
CREATE TABLE Account (
owner_id INT64 NOT NULL,
account_id INT64 NOT NULL,
) PRIMARY KEY (owner_id, account_id);
在這種情況下,您可以使用 Account
輸入資料表定義 Account
節點和 PersonOwnAccount
邊緣,如下列結構定義範例所示。為確保所有元素資料表名稱都是專屬的,這個範例會為邊緣資料表定義提供別名 Owns
。
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
Person,
Account
)
EDGE TABLES (
Account AS Owns
SOURCE KEY (owner_id) REFERENCES Person
DESTINATION KEY (owner_id, account_id) REFERENCES Account
);