

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

# 重新分片串流
<a name="kinesis-using-sdk-java-resharding"></a>

**重要**  
您可以使用 [UpdateShardCount](https://docs.aws.amazon.com/kinesis/latest/APIReference/API_UpdateShardCount.html) API 來將串流重新分片。否則，您可以如此處的說明，繼續執行分割和合併。

Amazon Kinesis Data Streams 支援*重新分片*，它可讓您調整串流中的碎片數量，以適應通過串流的資料流程速率的變化。重新分片為進階操作。如果您是 Kinesis Data Streams 的新使用者，請在您熟悉 Kinesis Data Streams 的所有其他層面之後，回到此主題。

重新分片操作有兩個類型：碎片分割和碎片合併。在碎片分割中，您會將單一碎片分成兩個碎片。在碎片合併中，您會將兩個碎片結合成單一碎片。重新分片一律為*逐對*，因為您無法在單一操作中分割成兩個以上的碎片，而且您無法在單一操作合併超過兩個碎片。重新分片操作執行所在的碎片或碎片對，稱為*父*碎片。重新分片操作所產生的碎片或碎片對，稱為*子*碎片。

分割會增加串流中碎片的數量，因此增加串流的資料容量。由於您需按碎片付費，分割會增加串流的成本。相同地，合併會減少串流中碎片的數量，因此會減少串流的資料容量和成本。

重新分片通常是由與生產程式 (put) 應用程式和使用程式 (get) 應用程式不同的管理應用程式執行。這類管理應用程式會根據 Amazon CloudWatch 提供的指標或根據從生產程式和使用程式收集的指標，監控串流的整體效能。管理應用程式也需要較使用程式或生產程式更廣泛的一組 IAM 許可，因為使用程式和生產程式通常不應需要存取用於重新分片的 API。如需 Kinesis Data Streams 的 IAM 許可的詳細資訊，請參閱 [使用 IAM 控制對 Amazon Kinesis Data Streams 資源的存取](controlling-access.md)。

如需有關重新分片的詳細資訊，請參閱[如何變更 Kinesis Data Streams 中開啟的碎片數量？](https://aws.amazon.com/premiumsupport/knowledge-center/kinesis-data-streams-open-shards/)

**Topics**
+ [決定重新分片的策略](kinesis-using-sdk-java-resharding-strategies.md)
+ [分割碎片](kinesis-using-sdk-java-resharding-split.md)
+ [合併兩個碎片](kinesis-using-sdk-java-resharding-merge.md)
+ [完成重新分片動作](kinesis-using-sdk-java-after-resharding.md)

# 決定重新分片的策略
<a name="kinesis-using-sdk-java-resharding-strategies"></a>

Amazon Kinesis Data Streams 中重新分片的目的是讓您的串流適應資料流程速率的變化。您分割碎片，以增加串流的容量 (和成本)。您合併碎片，以減少串流的成本 (和容量)。

 重新分片的其中一個方法可能是分割串流中的每個碎片，這樣會讓串流的容量加倍。不過，這可能會提供超過您實際所需額外的容量，因此產生不必要的成本。

您也可以使用指標來判斷哪些是您的*熱*或*冷*碎片，也就是說，較預期接收非常多資料或非常少資料的碎片。然後您可以選擇性地分割熱碎片，以增加以這些碎片為目標之雜湊索引鍵的容量。同樣地，您可以合併冷碎片以更明智利用其未使用的容量。

您可以從 Kinesis Data Streams 發佈的 Amazon CloudWatch 指標為串流取得一些效能資料。不過，您也可以為串流收集一些自己的指標。其中一個方法會是記錄分割區索引鍵為資料記錄產生的雜湊索引鍵值。回想您在將記錄新增到串流時指定的分割區索引鍵。

```
putRecordRequest.setPartitionKey( String.format( "myPartitionKey" ) );
```

Kinesis Data Streams 使用 [MD5](http://en.wikipedia.org/wiki/MD5)，從分割區索引鍵運算雜湊索引鍵。由於您會指定記錄的分割區索引鍵，您可以使用 MD5 來運算該記錄的雜湊索引鍵鍵值，並記錄它。

您也可以記錄資料記錄獲指派的碎片 ID。您可以使用 `getShardId` 方法傳回的 `putRecordResults` 物件的 `putRecords` 方法，以及 `putRecordResult` 方法傳回的 `putRecord` 物件，來取得碎片 ID。

```
String shardId = putRecordResult.getShardId();
```

有了碎片 ID 和雜湊索引鍵值，您可以判斷哪些碎片和雜湊索引鍵正接收最多或最少的流量。然後，您可以使用重新分片來提供更多或更少的容量，視這些索引鍵的需要而定。

# 分割碎片
<a name="kinesis-using-sdk-java-resharding-split"></a>

若要在 Amazon Kinesis Data Streams 中分割碎片，您必須指定如何將來自父碎片的雜湊索引鍵值重新分配到子碎片。將資料記錄新增到串流時，它會根據雜湊索引鍵值指派給碎片。雜湊索引鍵值是在您將資料記錄新增至串流時，為資料記錄指定之分割區索引鍵的 [MD5](http://en.wikipedia.org/wiki/MD5) 雜湊。具有相同分割區索引鍵的資料記錄也會有相同的雜湊索引鍵值。

指定碎片的可能雜湊索引鍵值會組成一組有次序的連續非負整數。此可能的雜湊索引鍵值範圍假設為：

```
shard.getHashKeyRange().getStartingHashKey();
shard.getHashKeyRange().getEndingHashKey();
```

分割碎片時，您會指定在這個範圍中的值。該雜湊索引鍵值和所有較高的雜湊索引鍵值會分配至其中一個子碎片。所有較低的雜湊索引鍵值會分配至其他子碎片。

以下程式碼示範碎片分割操作，該操作會在每個子碎片之間將雜湊索引鍵平均分配，基本上是將父碎片分割成兩半。這只是分割父碎片的一個可能方式。例如，您可以分割碎片，使得來自父系較少的三分之一的索引鍵前往第一個子碎片，以及來自父系較高的三分之二索引鍵則前往其他子碎片。不過，對於許多應用程式來說，將碎片分割成兩半是有效的方法。

此程式碼假設 `myStreamName` 擁有串流的名稱，以及物件變數 `shard` 則擁有要分割的碎片。從將新的 `splitShardRequest` 物件執行個體化開始，並設定串流名稱和碎片 ID。

```
SplitShardRequest splitShardRequest = new SplitShardRequest();
splitShardRequest.setStreamName(myStreamName);
splitShardRequest.setShardToSplit(shard.getShardId());
```

決定碎片中介於最低和最高值之間一半的雜湊索引鍵值。這是將包含來自父碎片上半雜湊索引鍵之子碎片的開始雜湊索引鍵值。在 `setNewStartingHashKey` 方法中指定這個值。您只需要指定這個值。Kinesis Data Streams 會將低於這個值的雜湊索引鍵自動分配至分割建立的另一個子碎片。最後一個步驟是呼叫 Kinesis Data Streams 用戶端上的 `splitShard` 方法。

```
BigInteger startingHashKey = new BigInteger(shard.getHashKeyRange().getStartingHashKey());
BigInteger endingHashKey   = new BigInteger(shard.getHashKeyRange().getEndingHashKey());
String newStartingHashKey  = startingHashKey.add(endingHashKey).divide(new BigInteger("2")).toString();

splitShardRequest.setNewStartingHashKey(newStartingHashKey);
client.splitShard(splitShardRequest);
```

[等待串流再次變成作用中](kinesis-using-sdk-java-after-resharding.md#kinesis-using-sdk-java-resharding-wait-until-active)中顯示此程序後的第一個步驟。

# 合併兩個碎片
<a name="kinesis-using-sdk-java-resharding-merge"></a>

 碎片合併操作需要兩個指定的碎片，並且會將它們合併為單一碎片。合併之後，該單一子碎片會收到兩個父碎片涵蓋的所有雜湊索引鍵值的資料。

**碎片相鄰**  
若要合併兩個碎片，碎片必須*相鄰*。如果兩個碎片的雜湊索引鍵範圍的聯結形成連續的一組 (無間隙)，則將其視為相鄰。舉例來說，假設您有兩個碎片，其中一個的雜湊鍵範圍為 276...381，另一個的雜湊鍵範圍為 382...454。您可以將這兩個碎片合併為一個碎片，就會得到範圍為 276...454 的雜湊鍵。

再舉另一個例子，假設您有兩個碎片，其中一個的雜湊鍵範圍為 276...381，另一個的雜湊鍵範圍為 455...560。您可以合併這兩個碎片，因為這樣在這兩個碎片之間就會有一個或多個碎片，範圍涵蓋 382...454。

串流中所有 `OPEN` 碎片的集合 – 群組形式 – 一律會跨越 MD5 雜湊索引鍵值的整個範圍。如需碎片狀態—例如 `CLOSED`—的詳細資訊，請參閱 [考慮重新碎片之後的資料路由、資料持久性和碎片狀態](kinesis-using-sdk-java-after-resharding.md#kinesis-using-sdk-java-resharding-data-routing)。

若要識別用於合併之候選項目的碎片，您應該篩選掉處於 `CLOSED` 狀態的所有碎片。狀態為 `OPEN` (即非 `CLOSED`) 的碎片，其結束序號為 `null`。您可以使用下列方式來測試碎片的結束序號：

```
if( null == shard.getSequenceNumberRange().getEndingSequenceNumber() ) 
{
  // Shard is OPEN, so it is a possible candidate to be merged.
}
```

篩選掉排序關閉碎片後，將剩餘的碎片依每個碎片支援的最高雜湊索引鍵值排序。您可以使用下列方式來擷取此值：

```
shard.getHashKeyRange().getEndingHashKey();
```

 如果在這個經篩選、排序之清單中的兩個碎片為相鄰，則可以將它們合併。

**合併操作的程式碼**  
 以下程式碼會合併兩個碎片。此程式碼假設 `myStreamName` 擁有串流的名稱，以及物件變數 `shard1` 和 `shard2` 則擁有要合併的兩個相鄰碎片。

對於合併操作，從將新的 `mergeShardsRequest` 物件執行個體化開始。使用 `setStreamName` 方法指定串流名稱。然後，使用 `setShardToMerge` 和 `setAdjacentShardToMerge` 方法指定要合併的兩個碎片。最後，呼叫 Kinesis Data Streams 用戶端上的 `mergeShards` 方法，以執行操作。

```
MergeShardsRequest mergeShardsRequest = new MergeShardsRequest();
mergeShardsRequest.setStreamName(myStreamName);
mergeShardsRequest.setShardToMerge(shard1.getShardId());
mergeShardsRequest.setAdjacentShardToMerge(shard2.getShardId());
client.mergeShards(mergeShardsRequest);
```

[等待串流再次變成作用中](kinesis-using-sdk-java-after-resharding.md#kinesis-using-sdk-java-resharding-wait-until-active)中顯示此程序後的第一個步驟。

# 完成重新分片動作
<a name="kinesis-using-sdk-java-after-resharding"></a>

在 Amazon Kinesis Data Streams 中任何種類的重新分片程序之後，以及在繼續正常記錄處理之前，需要其他程序和考量。下列小節描述這些情況。

**Topics**
+ [等待串流再次變成作用中](#kinesis-using-sdk-java-resharding-wait-until-active)
+ [考慮重新碎片之後的資料路由、資料持久性和碎片狀態](#kinesis-using-sdk-java-resharding-data-routing)

## 等待串流再次變成作用中
<a name="kinesis-using-sdk-java-resharding-wait-until-active"></a>

呼叫重新分片操作 `splitShard`或 之後`mergeShards`，您必須等待串流再次變成作用中。要使用的程式碼與[建立串流](kinesis-using-sdk-java-create-stream.md)之後，等待串流變得作用中的程式碼相同。該程式碼如下所示：

```
DescribeStreamRequest describeStreamRequest = new DescribeStreamRequest();
describeStreamRequest.setStreamName( myStreamName );

long startTime = System.currentTimeMillis();
long endTime = startTime + ( 10 * 60 * 1000 );
while ( System.currentTimeMillis() < endTime ) 
{
  try {
    Thread.sleep(20 * 1000);
  } 
  catch ( Exception e ) {}
  
  try {
    DescribeStreamResult describeStreamResponse = client.describeStream( describeStreamRequest );
    String streamStatus = describeStreamResponse.getStreamDescription().getStreamStatus();
    if ( streamStatus.equals( "ACTIVE" ) ) {
      break;
    }
   //
    // sleep for one second
    //
    try {
      Thread.sleep( 1000 );
    }
    catch ( Exception e ) {}
  }
  catch ( ResourceNotFoundException e ) {}
}
if ( System.currentTimeMillis() >= endTime ) 
{
  throw new RuntimeException( "Stream " + myStreamName + " never went active" );
}
```

## 考慮重新碎片之後的資料路由、資料持久性和碎片狀態
<a name="kinesis-using-sdk-java-resharding-data-routing"></a>

Kinesis Data Streams 是一種即時資料串流服務。您的應用程式應該假設資料持續流經串流中的碎片。執行重新分片時，流動至父碎片的資料記錄會重新路由，以根據資料記錄分割區索引鍵所對應的雜湊索引鍵值，流動至子碎片。不過，重新分片之前在父碎片中的任何資料記錄，會維持在那些碎片中。重新碎片發生時，父碎片不會消失。它們會隨著重新分片之前包含的資料保留。父碎片中的資料記錄可使用 Kinesis Data Streams API 中的 [`getShardIterator` 和 `getRecords`](developing-consumers-with-sdk.md#kinesis-using-sdk-java-get-data) 操作或透過 Kinesis Client Library 存取。

**注意**  
資料記錄可供存取的時間是從新增至串流的時間到目前的保留期間。無論在該時間期間對串流中的碎片進行任何變更，都是如此。如需串流保留期間的詳細資訊，請參閱[變更資料保留期間](kinesis-extended-retention.md)。

在重新分片的程序中，父碎片會從 `OPEN` 狀態轉換為 `CLOSED` 狀態然後為 `EXPIRED` 狀態。
+  **OPEN**：重新分片操作之前，父碎片會處於 `OPEN` 狀態，這表示資料記錄可同時新增至碎片和從碎片中擷取。
+  **CLOSED**：重新分片操作之後，父碎片會轉換為 `CLOSED` 狀態。這表示資料記錄不再會新增至碎片。應新增至此碎片的資料記錄，現在會改為新增至子碎片。不過，您仍可以在限定時間內從碎片擷取資料記錄。
+  **EXPIRED**：在串流的保留期過期之後，父碎片中的所有資料記錄會過期，並不再可供存取。此時，碎片本身會轉換為 `EXPIRED` 狀態。為了列舉串流中碎片對 `getStreamDescription().getShards` 的呼叫，在傳回的清單碎片中不會包含 `EXPIRED` 碎片。如需串流保留期間的詳細資訊，請參閱[變更資料保留期間](kinesis-extended-retention.md)。

重新分片發生且串流再次處於 `ACTIVE` 狀態之後，您可以立即開始從子碎片讀取資料。不過，在重新碎片之後保留的父碎片可能仍然包含您尚未讀取，且在重新碎片之前新增至串流的資料。如果您在從父碎片讀取所有資料之前從子碎片讀取資料，則可以不按資料記錄的序號指定的順序讀取特定雜湊索引鍵的資料。因此，假設資料的順序很重要，在重新分片之後，您應該一律持續從父碎片讀取資料，直至耗盡為止。只有這樣您才應該從子碎片開始讀取資料。當 `getRecordsResult.getNextShardIterator` 傳回 `null`，表示您已讀取父碎片中的所有資料。