

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

# 標記屬性圖表的集區模型
<a name="pool-model-lpg"></a>

Amazon Neptune 上 LPGs的集區模型有三種不同的方法：
+ **屬性策略** ‒ 當您需要優先使用已建立的程式庫建構時，例如 Apache TinkerPop Gremlin 語言的 [PartitionStrategy](https://tinkerpop.apache.org/docs/current/reference/#partitionstrategy) 而非效能，請選擇屬性策略。
+ 字**首標籤策略** ‒ 我們根據效能和限制雜訊鄰近效果，建議大多數案例採用字首標籤策略。
+ **多標籤策略** ‒ 多標籤策略改善了前綴標籤策略的效能。它還支援跨叢集上所有租用戶的執行查詢 （例如，用於報告或監控所有租用戶的 ISV 查詢）。

## 屬性策略
<a name="property"></a>

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

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



![節點及其關係。](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/multi-tenancy-amazon-neptune/images/lpg-property-strategy.png)


在標記的屬性圖表中，有兩種管理此項目的方法。Gremlin 查詢語言提供  [PartitionStrategy](https://tinkerpop.apache.org/docs/current/reference/#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 模型的效能影響](#perf)一節。

如果符合下列條件，請考慮僅在節點上建模屬性策略，而不是在邊緣上建模：
+ 您的圖形邊緣明顯比標籤多。
+ 每個租用戶都是中斷連線的圖形。
+ 您只能使用節點做為起點，而不是標籤來存取圖形。

## 字首標籤策略
<a name="prefix-label"></a>

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

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



![具有包含字首和節點關係之標籤的節點。](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/multi-tenancy-amazon-neptune/images/lpg-prefix-label-strategy.png)


在 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 效能產生負面影響。

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

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

## 多標籤策略
<a name="multi-label"></a>

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

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

![節點及其關係，其中每個節點都有 LabelX、X、X-LabelX。](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/multi-tenancy-amazon-neptune/images/lpg-multiple-label-strategy.png)

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

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

在 Gremlin 中，您可以使用名為 [SubgraphStrategy](https://tinkerpop.apache.org/javadocs/current/full/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.html) 的周遊策略，將所有查詢的範圍限制為僅具有特定標籤的頂點，例如 `"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 模型的效能影響
<a name="perf"></a>

Amazon Neptune 的 AWS 技能建置器課程資料建模詳細說明了 Neptune 資料模型內部和建模影響，但我們將在此摘要說明這些設計的重要考量事項。 [ Amazon Neptune](https://explore.skillbuilder.aws/learn/course/external/view/elearning/16133/data-modeling-for-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`。好處是支援各種存取模式。