View a markdown version of this page

標記屬性圖表的集區模型 - AWS 方案指引

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

標記屬性圖表的集區模型

Amazon Neptune 上 LPGs的集區模型有三種不同的方法:

  • 屬性策略 ‒ 當您需要優先使用已建立的程式庫建構時,例如 Apache TinkerPop Gremlin 語言的 PartitionStrategy 而非效能,請選擇屬性策略。

  • 首標籤策略 ‒ 我們根據效能和限制雜訊鄰近效果,建議大多數案例採用字首標籤策略。

  • 多標籤策略 ‒ 多標籤策略改善了前綴標籤策略的效能。它還支援跨叢集上所有租用戶的執行查詢 (例如,用於報告或監控所有租用戶的 ISV 查詢)。

屬性策略

使用 LPGs,使用者可以將鍵值對屬性新增至節點、頂點和邊緣。為了實現邏輯分離,大多數客戶直覺地將此建模為每個節點和邊緣上具有通用租用戶屬性索引鍵的唯一屬性。租用戶屬性索引鍵代表擁有節點的所有租用戶。租用戶識別符是識別個別租用戶的唯一值。

下圖顯示此模型。兩個中斷連接的子圖具有各種標記的節點和邊緣,租用戶屬性索引鍵由 表示TId。一個子圖的每個節點和邊緣都有 TId的值1。在另一個子圖中,每個節點和邊緣TId的值為 2

節點及其關係。

在標記的屬性圖表中,有兩種管理此項目的方法。Gremlin 查詢語言提供  PartitionStrategy 周遊程式庫,以協助管理資料的資料分割。下列範例中的程式碼預期每個節點和邊緣都有名為 的屬性TId

strategy1 = new PartitionStrategy(partitionKey: "TId", writePartition: "1", readPartitions: ["1"]) strategy2 = new PartitionStrategy(partitionKey: "TId", writePartition: "2", readPartitions: ["2"])

寫入新節點或邊緣時,會根據是否strategy2選取 strategy1"2",以 "1"或 的值"TId"新增 屬性。對於使用 "TId"的客戶"1",您可以使用 strategy1。下列範例顯示為該客戶寫入資料:

g.withStrategies(strategy1).addV("Label1").property("Value", "123456").property(id, "Item_1")

對於讀取查詢, "TId == '1'"或 的篩選條件"TId == '2'"strategy2分別使用 strategy1或 新增至每個節點或邊緣周遊。這些分割區策略可簡化您的程式碼,但並非必要。使用 策略的好處是它可以在授權層級注入,並傳遞至形成查詢的較低層級程式碼。這會將決定客戶識別符 (TId) 的程式碼與查詢的邏輯分開。

下列範例程式碼顯示讀取資料的 Gremlin 查詢:

g.withStrategies(strategy1).V().hasLabel("Label1")

上述程式碼等同於下列範例:

g.V().hasLabel("Label1").has("TId", "1")

同樣地,使用 Gremlin 寫入資料時,您可以使用下列查詢:

g.withStrategies(strategy1).addV("Label1").property("Value").property(id, "Item_1")

上述程式碼等同於下列範例,該範例不使用 分割區策略,因此需要明確寫入 "TId" 屬性:

g.addV("Label1").property("TId", "1").property("Value").property(id, "Item_1")

在 openCypher 中,這些程式庫不存在。您有責任撰寫和修改查詢,將租戶識別符新增為節點和邊緣的屬性。例如:

CREATE (n:Item {`~id`: 'Item_1', Value: '123456', TId: '1'}) CREATE (n:Item {`~id`: 'Item_2', Value: '123456', TId: '2'})

請注意 Gremlin 程式碼之間沒有分割區策略的相似性。然後,您可以使用下列程式碼讀取從第一個CREATE陳述式寫入的節點:

MATCH (n:Item {TId: '1'}) RETURN n --or MATCH (n:Item) WHERE n.TId == '1' RETURN n

當您想要使用原生 TinkerPop Gremlin 建構,例如 PartitionStrategy 時,可以選擇 屬性策略。不過,相較於前綴標籤策略,此模型在 Amazon Neptune 上有效能缺點。如需這些效能缺點的討論,請參閱 LPG 模型的效能影響一節。

如果符合下列條件,請考慮僅在節點上建模屬性策略,而不是在邊緣上建模:

  • 您的圖形邊緣明顯比標籤多。

  • 每個租用戶都是中斷連線的圖形。

  • 您只能使用節點做為起點,而不是標籤來存取圖形。

字首標籤策略

如果效能是首要考量,強烈建議您考慮屬性策略的前綴標籤策略。

在字首標籤策略中,您可以使用租戶識別符和節點標籤的組合來標記每個節點。例如,如果租用戶的識別符為 "1"且節點標籤為 "Label1",您可以將節點標籤指定為 "1-Label1"。下圖顯示使用此模型的兩個中斷連線子圖。

具有包含字首和節點關係之標籤的節點。

在 Gremlin 中寫入資料時,您可以將識別號碼新增至任何節點的標籤:

g.addV("1-Label1") g.addV("2-Label6")

查詢此圖形時,您可以檢查節點上是否存在此字首:

g.V().hasLabel("1-Label1")

在 openCypher 中,您可以使用CREATE陳述式寫入資料:

CREATE (n:`1-Label1` {`~id`: 'Item_1', Value: 'XYZ123456'})

若要查詢您在 openCypher 中撰寫的資料,請使用下列程式碼:

MATCH n= (:`1-Label1`) RETURN n

字首標籤策略假設所有節點都指派給一或多個租用戶,並且未在邊緣範圍內指派許可。避免在邊緣標籤上使用此策略,因為這會導致大量的述詞,並對 Neptune 效能產生負面影響。

字首標籤方法有兩個主要缺點。首先,執行跨租用戶的任何查詢並不容易。例如,查詢會計算指定標籤的所有節點以進行報告或監控。如果這是您的使用案例,請考慮將此策略與多標籤策略結合。如需結合策略的詳細資訊,請參閱混合模型一節。

其次,字首標籤策略需要控制,強制對每個查詢適當套用適當的字首,以防止資料外洩。不過,對於需要低延遲查詢的工作負載而言,此策略是最有效率的選項,我們強烈建議您這麼做。LPG 模型的效能影響區段提供為什麼這是最有效策略的範例。

多標籤策略

第三個選項是使用多標籤策略。對於此方法,您可以將額外的標籤新增至圖形上的每個節點。例如,如果您需要篩選指定租用戶的所有資料,請新增租用戶 ID 標籤。 如果您需要篩選指定標籤的所有資料,無論租用戶為何,請新增該標籤。下圖顯示針對每個節點使用三個標籤所套用的多標籤策略。

您現在可以使用三種不同的模式來存取圖形:

節點及其關係,其中每個節點都有 LabelX、X、X-LabelX。
  • 篩選 Label1以傳回所有租用戶Label1中具有 的所有節點。

  • 篩選 1以傳回租戶 1 的所有節點。

  • 篩選 1-Label1 以傳回僅具有標籤 之租用戶 1 的所有節點Label1

對於 LPGs,有兩種方式可以實作此項目。

在 Gremlin 中,您可以使用名為 SubgraphStrategy 的周遊策略,將所有查詢的範圍限制為僅具有特定標籤的頂點,例如 "Label1"

g.withStrategies( new SubgraphStrategy( vertices=hasLabel("Label1") ) )

與 PartitionStrategy 不同,SubgraphStrategy 只會影響讀取資料,不會影響寫入資料。若要寫入資料,請在每個查詢中手動指派標籤:

g.addV("Label1").property("Value","XYZ123456") .addV("Label2").property("Value","XYZ123456")

讀取資料時,您可以使用 SubgraphStrategy 透過 查詢所有節點"Label1"

g.withStrategies( new SubgraphStrategy(vertices=.hasLabel("Label1")) ). V().has("Value","XYZ123456")

Neptune 只會傳回第一個記錄,其具有 "Label1"和 的值"XYZ123456"。這相當於下列查詢,其不使用 SubgraphStrategy:

g.V().hasLabel("Label1").hasValue("XYZ123456")

在此基本查詢中,SubgraphStrategy 使用起來更為複雜。請記住,您的程式庫可以提供已定義策略g的 執行個體。開發人員不必確保套用適當的篩選條件:

def getGraphTraversal(): return g.withStrategies(new SubgraphStrategy(vertices=.hasLabel("Label1")) getGraphTraversal().has("Value","XYZ123456")

openCypher 程式庫沒有這些建構,因此您必須為每個節點建立多個標籤:

CREATE (n:`1`:`Label1`:`1-Label1` {`~id`: 'Item_1', Value: '12345'})

當您使用這些標籤來篩選子圖時,您可以傳回具有您要尋找之客戶標籤的節點,或與具有該標籤的其他節點共用關係的節點:

MATCH n=(:Label1:`1`) // or MATCH n=(:`1-Label1`)

多標籤策略可讓您根據類型 (Label1) 或租用戶 () 來查詢節點,或在效能最為重要 (1) 時使用更有效率的字首標籤策略1-Label1

此策略的主要缺點是每個標籤都是存放在圖形中的額外物件。物件是 LPGs 中節點或邊緣上的節點、邊緣或屬性。擷取速度由每秒物件測量和限制,儲存成本取決於消耗的 GB 數。這表示額外的物件可能會產生大規模的可測量影響。

LPG 模型的效能影響

Amazon Neptune 的 AWS 技能建置器課程資料建模詳細說明了 Neptune 資料模型內部和建模影響,但我們將在此摘要說明這些設計的重要考量事項。 Amazon Neptune 考慮在單一 Neptune 叢集上擁有三個租用戶 (T1、T2, T3)。這些租用戶具有下列屬性:

  • 租戶 1 (T1) 總共有 1 億個節點,1000 萬個是類型項目。

  • 租用戶 2 (T2) 總共有 1,000 萬個節點,100 萬個是類型項目。

  • 租戶 3 (T3) 總共有 1 億個節點,100 萬個是類型項目。

執行查詢,該查詢將使用 屬性策略擷取租用戶 3 的項目。Neptune 會檢查兩個索引呼叫的統計資料:

  • 其中 tenant property key=T3有 1 億個結果

  • 其中 label = Item有 1,200 萬筆結果 (1,000 萬筆來自 T1 + 100 萬筆來自 T2 + 100 萬筆來自 T3)

Neptune 查詢最佳化工具會判斷後者查詢最適合先套用 (1,200 萬個結果),然後檢查每個項目是否有 tenant property key=T3。您可以擷取 1,200 萬個項目來尋找 100 萬個結果。

請注意此查詢的雜訊相鄰影響。如果您每個租用戶有 1 億個項目節點,第一個查詢會有 3 億個結果,而不是 1,200 萬個 (為了說明目的,這過度簡化了。 Neptune 最佳化工具可能已套用不同的操作順序)。

接著,請考慮字首標籤策略。進行單一索引呼叫label=T3-Item,其中 會 傳回 100 萬個結果。這可實現與屬性策略相同的結果,但擷取的記錄減少 1,100 萬筆。此外,由於標籤不會在索引中重疊,因此您不再有雜訊的鄰里問題。

多標籤策略不會直接改善屬性策略的查詢效能。當搜尋空間也相當時,依屬性值篩選與依標籤值篩選相當。反之,多標籤策略支援更多彈性。 多標籤策略提供的效能等同於 label=T3或標籤 的前綴標籤策略T3-Item。多標籤策略提供的效能等同於 的屬性策略label=Item。好處是支援各種存取模式。