

# テーブル、項目、クエリ、スキャン、およびインデックスの使用
<a name="WorkingWithDynamo"></a>

このセクションでは、Amazon DynamoDB でのテーブル、項目、クエリ、その他の操作に関する詳細を説明します。

**Topics**
+ [DynamoDB でのテーブルとデータの操作](WorkingWithTables.md)
+ [グローバルテーブル - マルチアクティブ、マルチリージョンレプリケーション](GlobalTables.md)
+ [DynamoDB での項目と属性の操作](WorkingWithItems.md)
+ [DynamoDB でのセカンダリインデックスを使用したデータアクセス性の向上](SecondaryIndexes.md)
+ [DynamoDB トランザクションで複雑なワークフローを管理する](transactions.md)
+ [Amazon DynamoDB の変更データキャプチャ](streamsmain.md)

# DynamoDB でのテーブルとデータの操作
<a name="WorkingWithTables"></a>

このセクションでは、AWS Command Line Interface (AWS CLI) および AWS SDK を使用して Amazon DynamoDB でテーブルを作成、更新、削除する方法を説明します。

**注記**  
このタスクは AWS マネジメントコンソール を使用して実行することもできます。詳細については、「[コンソールを使用する](AccessingDynamoDB.md#ConsoleDynamoDB)」を参照してください。

このセクションでは、DynamoDB Auto Scaling や手動設定のプロビジョンされたスループットを使用し、スループット容量の詳細についても示します。

**Topics**
+ [DynamoDB テーブルの基本的なオペレーション](WorkingWithTables.Basics.md)
+ [DynamoDB でテーブルクラスを選択する場合の考慮事項](WorkingWithTables.tableclasses.md)
+ [DynamoDB でのリソースへのタグとラベルの追加](Tagging.md)

# DynamoDB テーブルの基本的なオペレーション
<a name="WorkingWithTables.Basics"></a>

他のデータベース管理システムと同様、Amazon DynamoDB はデータをテーブルに保存します。いくつかの基本的なオペレーションでテーブルを管理できます。

**Topics**
+ [テーブルの作成](#WorkingWithTables.Basics.CreateTable)
+ [表の説明](#WorkingWithTables.Basics.DescribeTable)
+ [テーブルの更新](#WorkingWithTables.Basics.UpdateTable)
+ [テーブルの削除](#WorkingWithTables.Basics.DeleteTable)
+ [削除保護の使用](#WorkingWithTables.Basics.DeletionProtection)
+ [テーブル名のリスト化](#WorkingWithTables.Basics.ListTables)
+ [プロビジョニングされたスループットクォータの説明](#WorkingWithTables.Basics.DescribeLimits)

## テーブルの作成
<a name="WorkingWithTables.Basics.CreateTable"></a>

`CreateTable` オペレーションを使用して、Amazon DynamoDB でテーブルを作成します。テーブルを作成するために、以下の情報を指定する必要があります。
+ **[テーブル名]** - 。名前は DynamoDB 命名規則に従う必要があります。また、現在の AWS アカウントとリージョンで一意である必要があります。たとえば、米国東部 (バージニア北部) に `People` テーブルを作成し、欧州 (アイルランド) に別の `People` テーブルを作成できます。ただし、これらの 2 つのテーブルは互いにまったく異なっています。詳細については、「[Amazon DynamoDB でサポートされるデータ型と命名規則](HowItWorks.NamingRulesDataTypes.md)」を参照してください。
+ **プライマリキー。**プライマリキーは 1 つの属性 (パーティションキー) または 2 つの属性で構成できます (パーティションキーとソートキー)。属性名、データタイプ、各属性のロール（パーティションキーでは `HASH`、ソートキーでは `RANGE`）を指定する必要があります。詳細については、「[プライマリキー](HowItWorks.CoreComponents.md#HowItWorks.CoreComponents.PrimaryKey)」を参照してください。
+ **スループット設定 (プロビジョニングされたテーブルの場合)。**プロビジョニングモードを使用している場合、最初の読み取りと書き込みのスループット設定をテーブルに指定する必要があります。これらの設定は後から変更できます。また、DynamoDB Auto Scaling を有効化して設定を管理することもできます。詳細については、「[DynamoDB プロビジョンドキャパシティモード](provisioned-capacity-mode.md)」および「[DynamoDB Auto Scaling によるスループットキャパシティの自動管理](AutoScaling.md)」を参照してください。

### 例 1: オンデマンドテーブルを作成する
<a name="create-payperrequest-example"></a>

オンデマンドモードを使用して同じテーブル `Music` を作成するには

```
aws dynamodb create-table \
    --table-name Music \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --billing-mode=PAY_PER_REQUEST
```

`CreateTable` オペレーションは、以下のようにテーブルにメタデータを返します。

```
{
    "TableDescription": {
        "TableArn": "arn:aws:dynamodb:us-east-1:123456789012:table/Music",
        "AttributeDefinitions": [
            {
                "AttributeName": "Artist",
                "AttributeType": "S"
            },
            {
                "AttributeName": "SongTitle",
                "AttributeType": "S"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 0,
            "ReadCapacityUnits": 0
        },
        "TableSizeBytes": 0,
        "TableName": "Music",
        "BillingModeSummary": {
            "BillingMode": "PAY_PER_REQUEST"
        },
        "TableStatus": "CREATING",
        "TableId": "12345678-0123-4567-a123-abcdefghijkl",
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "Artist"
            },
            {
                "KeyType": "RANGE",
                "AttributeName": "SongTitle"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1542397468.348
    }
}
```

**重要**  
 オンデマンドテーブルで `DescribeTable` を呼び出すと、読み込みキャパシティユニットと書き込みキャパシティユニットが 0 に設定されます。

### 例 2: プロビジョン済みテーブルを作成する
<a name="create-provisioned-example"></a>

以下の AWS CLI サンプルは、テーブル (`Music`) の作成方法を示しています。プライマリキーは、`Artist` (パーティションキー) と `SongTitle` (ソートキー) で構成されており、データ型はそれぞれ `String` です。このテーブルの最大スループットは、読み取りキャパシティーユニット数が 10、書き込みキャパシティーユニット数が 5 です。

```
aws dynamodb create-table \
    --table-name Music \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=10,WriteCapacityUnits=5
```

`CreateTable` オペレーションは、以下のようにテーブルにメタデータを返します。

```
{
    "TableDescription": {
        "TableArn": "arn:aws:dynamodb:us-east-1:123456789012:table/Music",
        "AttributeDefinitions": [
            {
                "AttributeName": "Artist",
                "AttributeType": "S"
            },
            {
                "AttributeName": "SongTitle",
                "AttributeType": "S"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 5,
            "ReadCapacityUnits": 10
        },
        "TableSizeBytes": 0,
        "TableName": "Music",
        "TableStatus": "CREATING",
        "TableId": "12345678-0123-4567-a123-abcdefghijkl",
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "Artist"
            },
            {
                "KeyType": "RANGE",
                "AttributeName": "SongTitle"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1542397215.37
    }
}
```

`TableStatus` エレメントは、テーブルの現在の状態を示します（`CREATING`）。`ReadCapacityUnits` および `WriteCapacityUnits` に指定した値によって、テーブルの作成にはしばらく時間がかかる場合があります。これらの値を大きくすると、DynamoDB はより多くのリソースをテーブルに割り当てなければならなくなります。

### 例 3: DynamoDB Standard-Infrequent Access テーブルクラスを使用してテーブルを作成する
<a name="create-infrequent-access-example"></a>

DynamoDB Standard-Infrequent Access テーブルクラスを使用して同じ `Music` テーブルを作成するには

```
aws dynamodb create-table \
    --table-name Music \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --table-class STANDARD_INFREQUENT_ACCESS
```

`CreateTable` オペレーションは、以下のようにテーブルにメタデータを返します。

```
{
    "TableDescription": {
        "TableArn": "arn:aws:dynamodb:us-east-1:123456789012:table/Music",
        "AttributeDefinitions": [
            {
                "AttributeName": "Artist",
                "AttributeType": "S"
            },
            {
                "AttributeName": "SongTitle",
                "AttributeType": "S"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 5,
            "ReadCapacityUnits": 10
        },
        "TableClassSummary": {
            "LastUpdateDateTime": 1542397215.37,
            "TableClass": "STANDARD_INFREQUENT_ACCESS"
        },
        "TableSizeBytes": 0,
        "TableName": "Music",
        "TableStatus": "CREATING",
        "TableId": "12345678-0123-4567-a123-abcdefghijkl",
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "Artist"
            },
            {
                "KeyType": "RANGE",
                "AttributeName": "SongTitle"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1542397215.37
    }
}
```

## 表の説明
<a name="WorkingWithTables.Basics.DescribeTable"></a>

テーブルの詳細を表示するには、`DescribeTable` オペレーションを使用します。テーブル名を入力する必要があります。`DescribeTable` の出力は `CreateTable` の出力と同じ形式です。このメタデータには、テーブル作成時のタイムスタンプ、キースキーマ、プロビジョンドスループット設定、推定サイズ、既存のすべてのセカンダリインデックスが含まれています。

**重要**  
 オンデマンドテーブルで `DescribeTable` を呼び出すと、読み込みキャパシティーユニットと書き込みキャパシティーユニットが 0 に設定されます。

**Example**  

```
aws dynamodb describe-table --table-name Music
```

`TableStatus` が `CREATING` から `ACTIVE` に変わると、テーブルは使用できる状態になります。

**注記**  
`DescribeTable` のリクエスト直後に `CreateTable` リクエストを発行した場合、DynamoDB によってエラー (`ResourceNotFoundException`) が返されることがあります。`DescribeTable` で結果整合性のあるクエリが使用されており、テーブルのメタデータがその時点で使用できない可能性があるためです。数秒間待ってから、再び `DescribeTable` リクエストを試してみてください。  
請求目的上、DynamoDB ストレージコストには、100 バイトの項目あたりのオーバーヘッドが含まれます。(詳細は、[DynamoDB 料金表](https://aws.amazon.com/dynamodb/pricing/)を参照してください。) 各項目の余分な 100 バイトは、容量単位の計算または `DescribeTable` オペレーションでは使用されません。

## テーブルの更新
<a name="WorkingWithTables.Basics.UpdateTable"></a>

`UpdateTable` オペレーションを使用すると、以下のいずれかを実行できます。
+ テーブルのプロビジョニングされたスループット設定を変更します (プロビジョニングモードのテーブルの場合)。
+ テーブルの読み取り/書き込みキャパシティーモードを変更します。
+ テーブルでグローバルセカンダリインデックスを操作します (「[DynamoDB のグローバルセカンダリインデックスの使用](GSI.md)」を参照)。
+ テーブルの DynamoDB Streams を有効または無効にできます ([DynamoDB Streams の変更データキャプチャ](Streams.md) を参照)。

**Example**  
次の AWS CLI の例では、テーブルのプロビジョニングされたスループット設定を変更する方法を示します。  

```
aws dynamodb update-table --table-name Music \
    --provisioned-throughput ReadCapacityUnits=20,WriteCapacityUnits=10
```

**注記**  
`UpdateTable` リクエストを発行すると、テーブルのステータスが `AVAILABLE` から `UPDATING` に変わります。テーブルは `UPDATING` 中も全面的に利用できます。この処理が完了すると、テーブルのステータスが `UPDATING` から `AVAILABLE` に変わります。

**Example**  
次の AWS CLI の例では、テーブルの読み取り/書き込みキャパシティーモードをオンデマンドモードに変更する方法を示します。  

```
aws dynamodb update-table --table-name Music \
    --billing-mode PAY_PER_REQUEST
```

## テーブルの削除
<a name="WorkingWithTables.Basics.DeleteTable"></a>

`DeleteTable` オペレーションで未使用のテーブルを削除できます。テーブルの削除は回復不可能な操作です。AWS マネジメントコンソール を使用してテーブルを削除するには、「[ステップ 6: (オプション) DynamoDB テーブルを削除してリソースをクリーンアップする](getting-started-step-6.md)」を参照してください。

**Example**  
次の AWS CLI の例で、テーブルを削除する方法について説明します。  

```
aws dynamodb delete-table --table-name Music
```

`DeleteTable` リクエストを発行すると、テーブルのステータスが `ACTIVE` から `DELETING` に変わります。使用しているリソース（テーブルに保存されたデータ、ストリーム、テーブルのインデックスなど）によって、テーブルの削除には時間がかかる場合があります。

`DeleteTable` オペレーションが完了すると、テーブルは DynamoDB に存在しなくなります。

## 削除保護の使用
<a name="WorkingWithTables.Basics.DeletionProtection"></a>

削除保護プロパティを使用すると、テーブルを誤って削除しないように保護できます。テーブルに対してこのプロパティを有効にすると、管理者が通常のテーブル管理オペレーションを行うときにテーブルが誤って削除されるのを防ぐことができます。これにより、通常業務が中断されるのを防ぐことができます。

 テーブル所有者または権限のある管理者が、各テーブルの削除保護プロパティを制御します。すべてのテーブルで削除保護プロパティは、デフォルトでオフに設定されています。これには、グローバルレプリカやバックアップから復元されたテーブルが含まれます。テーブルの削除保護が無効になっている場合、Identity and Access Management (IAM) ポリシーによって承認されたすべてのユーザーがそのテーブルを削除できます。テーブルの削除保護が有効になっているときは、誰も削除できません。

この設定を変更するには、表の**[Additional settings]** (追加設定) に移動し、**[Deletion Protection]** (削除保護)パネルに移動して、**[Enable delete protection]** (削除保護を有効にする) を選択します。

削除保護プロパティは、DynamoDB コンソール、API、CLI/SDK、および CloudFormation でサポートされています。`CreateTable` API は、テーブル作成時の削除保護プロパティをサポートしており、`UpdateTable` API は既存のテーブルに対する削除保護プロパティの変更をサポートしています。

**注記**  
AWS アカウントを削除しても、テーブルを含むそのアカウントのすべてのデータは 90 日以内に削除されます。
DynamoDB は、テーブルを暗号化したカスタマーマネージドキーにアクセスできない場合、テーブルをアーカイブします。アーカイブには、テーブルのバックアップの作成と元のテーブルの削除が含まれます。

## テーブル名のリスト化
<a name="WorkingWithTables.Basics.ListTables"></a>

`ListTables` オペレーションは、現在の AWS アカウントやリージョンの DynamoDB テーブルの名前を返します。

**Example**  
次の AWS CLI の例は、DynamoDB テーブル名をリストする方法を示しています。  

```
aws dynamodb list-tables
```

## プロビジョニングされたスループットクォータの説明
<a name="WorkingWithTables.Basics.DescribeLimits"></a>

`DescribeLimits` オペレーションは、現在の AWS アカウントやリージョンの現在の読み取りおよび書き込みキャパシティーのクォータを返します。

**Example**  
次の AWS CLI の例では、現在のプロビジョニングされたスループットクォータを記述する方法を示します。  

```
aws dynamodb describe-limits
```
出力は、現在の AWS アカウントやリージョンの読み取りおよび書き込みキャパシティーユニットの上限クォータを返します。

これらのクォータの詳細およびクォータの引き上げをリクエストする方法については、「[スループットのデフォルトクォータ](ServiceQuotas.md#default-limits-throughput)」を参照してください。

# DynamoDB でテーブルクラスを選択する場合の考慮事項
<a name="WorkingWithTables.tableclasses"></a>

DynamoDB には、コストの最適化に役立つように設計された 2 つのテーブルクラスが用意されています。DynamoDB 標準テーブルクラスがデフォルトで、大半のワークロードで推奨されています。DynamoDB Standard-Infrequent Access (DynamoDB 標準-IA) テーブルクラスは、ストレージが主要なコストとなるテーブル用に最適化されています。例えば、アプリケーションログ、古いソーシャルメディアの投稿、E コマースの注文履歴、過去のゲーム実績など、アクセス頻度の低いデータを格納するテーブルは、標準-IA テーブルクラスの候補として適しています。

すべての DynamoDB テーブルは、テーブルクラスに関連付けられます。テーブルに関連付けられたすべてのセカンダリインデックスは、同じテーブルクラスを使用します。テーブル (デフォルトでは DynamoDB 標準) の作成時にテーブルクラスを設定できます。また、AWS マネジメントコンソール、AWS CLI、または AWS SDK を使用して、既存のテーブルのテーブルクラスを更新できます。DynamoDB は、シングルリージョンテーブル（グローバルテーブルではないテーブル）に対して AWS CloudFormation を使用したテーブルクラスの管理もサポートしています。テーブルクラスごとに、データストレージ、読み取りおよび書き込みリクエストに対して異なる料金が設定されています。テーブルのテーブルクラスを選択する場合、次の点に注意してください。
+ DynamoDB 標準テーブルクラスは、DynamoDB Standard-Infrequent Access (DynamoDB 標準 – IA) よりもスループットコストが低く、スループットが主要なコストであるテーブルで最もコスト効率の高いオプションです。
+ DynamoDB Standard-Infrequent Access (DynamoDB 標準 – IA) テーブルクラスは、DynamoDB 標準よりも低いストレージコストを提供し、ストレージが主要なコストであるテーブルで最もコスト効率の高いオプションです。ストレージが DynamoDB 標準テーブルクラスを使用するテーブルのスループット (読み取りと書き込み) コストの 50% を超える場合、DynamoDB Standard-Infrequent Access (DynamoDB 標準 – IA) テーブルクラスを使用すると、テーブルの総コストを削減できます。
+ DynamoDB 標準 IA テーブルは、DynamoDB 標準テーブルと同じパフォーマンス、耐久性、可用性を提供します。
+ DynamoDB 標準テーブルクラスと DynamoDB 標準 — IA テーブルクラスを入れ替えても、アプリケーションコードを変更する必要はありません。テーブルで使用するテーブルクラスに関係なく、同じ DynamoDB API とサービスエンドポイントを使用します。
+ DynamoDB 標準 IA テーブルは、Auto Scaling、オンデマンドモード、有効期限 (TTL)、オンデマンドバックアップ、ポイントインタイムリカバリ (PITR)、グローバルセカンダリインデックスなど、既存の DynamoDB に備わる機能のすべてと互換性があります。

テーブルにとって最も費用対効果の高いテーブルクラスは、そのテーブルの予想されるストレージとスループットの使用パターンによって異なります。テーブルのストレージとスループットのコストおよび使用状況の履歴は、AWS コストと使用状況レポート、および AWS Cost Explorer から確認できます。この履歴データを使用して、テーブルに対して最も費用対効果の高いテーブルクラスを特定します。AWS コストと使用状況レポート、および AWS Cost Explorer の使用の詳細については、[AWS Billing and Cost Management のドキュメント](https://docs.aws.amazon.com/account-billing/index.html)を参照してください。テーブルクラスの料金の詳細については、[Amazon DynamoDB Pricing (Amazon DynamoDB の料金表)](https://aws.amazon.com/dynamodb/pricing/on-demand/) を参照してください。

**注記**  
テーブルクラスの更新はバックグラウンドで処理されます。テーブルクラスの更新中も、通常どおりテーブルにアクセスできます。テーブルクラスを更新する時間は、テーブルトラフィック、ストレージサイズ、およびその他の関連する変数によって異なります。30 日間の追跡期間では、最大 2 つのテーブルクラスの更新が可能です。

# DynamoDB でのリソースへのタグとラベルの追加
<a name="Tagging"></a>

Amazon DynamoDB リソースにラベルを付けるには、*タグ*を使用します。タグを使用すると、リソースを目的、所有者、環境、その他の条件などさまざまな方法で分類することができます。タグは、以下のことに役立ちます。
+ リソースに割り当てたタグに基づいてリソースをすばやく特定します。
+ タグ別に分類された AWS 請求を表示する。
**注記**  
タグが付けられたテーブルに関連するローカルセカンダリインデックス (LSI) およびグローバルセカンダリインデックス (GSI) は、自動的に同じタグでラベルが付けられます。現在のところ、DynamoDB Streams の使用にタグを付けることはできません。

タグ付けは、Amazon EC2、Amazon S3、DynamoDB などの AWS のサービスでサポートされています。効率的なタグ付けを行うと、特定のタグを持つサービス間でレポートを作成でき、コストインサイトを得ることができます。

タグ付けの使用を開始するには、次のことを行います。

1. [DynamoDB でのタグ付けの制限](#TaggingRestrictions) について理解します。

1. [DynamoDB でのタグ付けのリソース](Tagging.Operations.md) を使用してタグを作成します。

1. [DynamoDB タグを使用して、コスト配分レポートを作成する](#CostAllocationReports) を使用して、アクティブなタグごとに AWS のコストを追跡します。

最後に、最適のタグ付け手法に従うことをお勧めします。詳細については、「[AWS tagging strategies ( タグ付け戦略)](https://d0.awsstatic.com/aws-answers/AWS_Tagging_Strategies.pdf)」を参照してください。

## DynamoDB でのタグ付けの制限
<a name="TaggingRestrictions"></a>

 タグはそれぞれ、1 つのキーと 1 つの値で構成されており、どちらもお客様側が定義します。以下の制限が適用されます。
+  各 DynamoDB テーブルは同じキーを含む 1 つのタグのみを持つことができます。既存のタグ (同じキー) を追加しようとすると、既存のタグの値は新しい値に更新されます。
+  タグのキーと値は大文字と小文字が区別されます。
+  キーの最大長は Unicode 文字で 128 文字です。
+ 値の最大長は Unicode 文字で 256 文字です。
+  使用できる文字は、文字、ホワイトスペース、数字、および特殊文字 (`+ - = . _ : /`) です。
+  リソースあたりのタグの最大数は 50 です。
+ テーブル内のすべてのタグにサポートされる最大サイズは 10 KB です。
+ AWS 割り当てタグの名前と値には、`aws:` プレフィックスが自動的に割り当てられます。これをユーザーが割り当てることはできません。AWS 割り当てタグの名前は、タグ制限数の 50 または最大サイズ制限の 10 KB にカウントされません。ユーザー側で割り当てたタグ名は、`user:` というプレフィックスを付けてコスト配分レポートに表示されます。
+  過去にさかのぼってタグを適用することはできません。

# DynamoDB でのタグ付けのリソース
<a name="Tagging.Operations"></a>

Amazon DynamoDB コンソールまたは AWS Command Line Interface (AWS CLI) を使用して、タグを追加、リスト、編集、または削除できます。次に、これらのユーザー定義タグをアクティブ化し、コスト配分の追跡のため、AWS Billing and Cost Management コンソールに表示できます。詳細については、「[DynamoDB タグを使用して、コスト配分レポートを作成する](Tagging.md#CostAllocationReports)」を参照してください。

 一括編集の場合は、AWS マネジメントコンソール のタグエディタを使用することもできます。詳細については、「[タグエディタの使用](https://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/tag-editor.html)」を参照してください。

 代わりに DynamoDB API を使用するには、[Amazon DynamoDB API リファレンス](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/)で以下のオペレーションを参照してください。
+ [TagResource](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TagResource.html)
+ [UntagResource](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UntagResource.html)
+ [ListTagsOfResource](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ListTagsOfResource.html)

**Topics**
+ [タグでフィルターする権限の設定](#Tagging.Operations.permissions)
+ [新しいテーブルまたは既存のテーブルへのタグの追加 (AWS マネジメントコンソール)](#Tagging.Operations.using-console)
+ [新しいテーブルまたは既存のテーブルへのタグの追加 (AWS CLI)](#Tagging.Operations.using-cli)

## タグでフィルターする権限の設定
<a name="Tagging.Operations.permissions"></a>

タグを使用して DynamoDB コンソールでテーブルリストをフィルタリングするには、ユーザーのポリシーに以下のオペレーションへのアクセス権が含まれていることを確認します。
+ `tag:GetTagKeys`
+ `tag:GetTagValues`

これらのオペレーションにアクセスするには、以下の手順に従って、新しい IAM ポリシーをユーザーにアタッチします。

1. 管理者ユーザーで [IAM コンソール](https://console.aws.amazon.com/iam/)に移動します。

1. 左のナビゲーションメニューで [Policies (ポリシー)] を選択します。

1. [Create policy (ポリシーの作成)] を選択します。

1. 以下のポリシーを JSON エディタに貼り付けます。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "tag:GetTagKeys",
                   "tag:GetTagValues"
               ],
               "Resource": "*"
           }
       ]
   }
   ```

------

1. ウィザードを完了し、ポリシーに名前を割り当てます (例:`TagKeysAndValuesReadAccess`)。

1. 左のナビゲーションメニューで [Users (ユーザー)] を選択します。

1. リストから、DynamoDB コンソールにアクセスする通常のユーザーを選択します。

1. [Add permissions (アクセス許可の追加)] を選択します。

1. [Attach existing policies directly (既存のポリシーを直接アタッチ)] を選択します。

1. リストから、前に作成したポリシーを選択します。

1. ウィザードを終了します。

## 新しいテーブルまたは既存のテーブルへのタグの追加 (AWS マネジメントコンソール)
<a name="Tagging.Operations.using-console"></a>

新しいタグを作成するときや、既存のテーブルのタグを追加、編集、削除するときは、DynamoDB コンソールを使用してタグを新しいテーブルに追加できます。

**作成時にリソースにタグを付けるには (コンソール)**

1. AWS マネジメントコンソール にサインインして DynamoDB コンソール ([https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/)) を開きます。

1. ナビゲーションペインで [**テーブル**] を選択して、[**テーブルの作成**] を選択します。

1. [**Create DynamoDB table (DynamoDB テーブルの作成)**] ページで、名前とプライマリキーを指定します。[**タグ**] セクションで、[**新しいタグを追加**] を選択し、使用するタグを入力します。

   タグ構造の詳細については、「[DynamoDB でのタグ付けの制限](Tagging.md#TaggingRestrictions)」を参照してください。

   テーブル作成の詳細については、「[DynamoDB テーブルの基本的なオペレーション](WorkingWithTables.Basics.md)」を参照してください。

**既存のリソースにタグを付けるには (コンソール)**

[https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/) で DynamoDB コンソールを開きます。

1. ナビゲーションペインで [**テーブル**] を選択します。

1. リストでテーブルを選択し、[**追加設定**] タブを選択します。ページの下部にある [**タグ**] セクションで、タグを追加、編集、削除できます。

## 新しいテーブルまたは既存のテーブルへのタグの追加 (AWS CLI)
<a name="Tagging.Operations.using-cli"></a>

次の例は、テーブルとインデックスを作成するとき、および既存のリソースへのタグを付けるときに、AWS CLI を使用してタグを指定する方法を示しています。

**作成時にリソースにタグを付けるには (AWS CLI)**
+ 次の例では、新しい `Movies` テーブルを作成し、値が `Owner` の `blueTeam` タグを追加します。

  ```
  aws dynamodb create-table \
      --table-name Movies \
      --attribute-definitions AttributeName=Title,AttributeType=S \
      --key-schema AttributeName=Title,KeyType=HASH \
      --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
      --tags Key=Owner,Value=blueTeam
  ```

**既存のリソースにタグを付けるには (AWS CLI)**
+ 次の例では、`Owner` テーブル用に、値が `blueTeam` の `Movies` タグを追加します。

  ```
  aws dynamodb tag-resource \
      --resource-arn arn:aws:dynamodb:us-east-1:123456789012:table/Movies \
      --tags Key=Owner,Value=blueTeam
  ```

**テーブルのすべてのタグを一覧表示するには (AWS CLI)**
+ 次の例では、`Movies` テーブルに関連付けられたすべてのタグを一覧表示します。

  ```
  aws dynamodb list-tags-of-resource \
      --resource-arn arn:aws:dynamodb:us-east-1:123456789012:table/Movies
  ```

## DynamoDB タグを使用して、コスト配分レポートを作成する
<a name="CostAllocationReports"></a>

AWS はタグを使用して、コスト配分レポートでリソースコストを分類します。AWS には 2 つのタイプのコスト配分タグがあります。
+ AWS で生成されたタグ。AWS はユーザーのためにこのタグを定義、作成、適用します。
+ ユーザー定義のタグ。これらのタグを定義、作成、適用します。

Cost Explorer またはコスト配分レポートで使用するには、事前に両方のタイプのタグを別々にアクティブ化しておく必要があります。

 AWS で生成されたタグをアクティブ化するには: 

1.  AWS マネジメントコンソール にサインインし、[https://console.aws.amazon.com/billing/home\$1/](https://console.aws.amazon.com/billing/home#/.) で請求およびコスト管理コンソールを開きます。

1.  ナビゲーションペインで、**[Cost Allocation Tags]** (コスト配分タグ) を選択します。

1.  [**AWS-Generated Cost Allocation Tags**] で、[**Activate**] を選択します。

 ユーザー定義タグをアクティブにするには: 

1.  AWS マネジメントコンソール にサインインし、[https://console.aws.amazon.com/billing/home\$1/](https://console.aws.amazon.com/billing/home#/.) で請求およびコスト管理コンソールを開きます。

1.  ナビゲーションペインで、**[Cost Allocation Tags]** (コスト配分タグ) を選択します。

1.  **[User-Defined Cost Allocation Tags]** (ユーザー定義のコスト配分タグ) で、**[Activate]** (アクティブ化) を選択します。

 タグを作成してアクティブ化すると、AWS は、アクティブなタグごとにグループ化された使用量とコストを含むコスト配分レポートを生成します。コスト配分レポートには、ご利用の AWS のサービスのコストすべてが請求期間別に出力されます。タグ付きとタグなしのどちらのリソースもこのレポートに出力されるので、リソース別の請求額を明確に分類できます。

**注記**  
 現時点では、DynamoDB から転送されたデータは、コスト配分レポートのタグによって分類されません。

 詳細については、「[Using cost allocation tags](https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/cost-alloc-tags.html)」(コスト配分タグの使用) を参照してください。

# グローバルテーブル - マルチアクティブ、マルチリージョンレプリケーション
<a name="GlobalTables"></a>

*Amazon DynamoDB グローバルテーブル*は、フルマネージドで、マルチリージョン、マルチアクティブなデータベース機能で、グローバルにスケールされたアプリケーション向けに、使いやすいレプリケーションと高速なローカルの読み込みと書き込みのパフォーマンスを提供します。

グローバルテーブルは、独自のレプリケーションソリューションを構築および維持することなく、AWS リージョン間、オプションで AWS アカウント間で DynamoDB テーブルデータを自動的にレプリケートします。グローバルテーブルは、マルチリージョンデプロイを通じてビジネス継続性と高可用性を必要とするアプリケーションに最適です。グローバルテーブルのレプリカは、読み取りと書き込みの両方を処理できます。アプリケーションは、リージョンでアプリケーション処理が中断された場合にトラフィックを別のリージョンに移行することで、目標復旧時点 (RPO) を低くまたはゼロにして高い回復力を実現できます。グローバルテーブルは、DynamoDB が利用可能なすべてのリージョンで使用できます。

## 整合性モード
<a name="GlobalTables.consistency-modes"></a>

グローバルテーブルを作成するときに、その整合性モードを設定できます。グローバルテーブルは、マルチリージョンの結果整合性 (MREC) およびマルチリージョンの強力な整合性 (MRSC) の 2 つの整合性モードをサポートしています。

グローバルテーブルの作成時に整合性モードを指定しない場合、グローバルテーブルはデフォルトでマルチリージョンの結果整合性 (MREC) になります。グローバルテーブルには、異なる整合性モードで設定されたレプリカを含めることはできません。グローバルテーブルの整合性モードを作成後に変更することはできません。

## アカウント設定
<a name="GlobalTables.account-configurations"></a>

DynamoDB は現在、それぞれ異なるアーキテクチャパターン用に設計された 2 つのグローバルテーブルモデルをサポートしています。
+ **同一アカウントグローバルテーブル** – すべてのレプリカは 1 つの AWS アカウント内で作成および管理されます。
+ **マルチアカウントグローバルテーブル** – レプリカは、共有レプリケーショングループに参加している間、複数の AWS アカウントにデプロイされます。

同一アカウントモデルとマルチアカウントモデルの両方で、マルチリージョン書き込み、非同期レプリケーション、最後の書き込みを優先による競合解決、および同じ請求モデルがサポートされます。ただし、アカウント、アクセス許可、暗号化、テーブルガバナンスの管理方法は異なります。

MRSC 用に設定されたグローバルテーブルは、同じアカウント設定のみをサポートします。

AWS マネジメントコンソールを使用してグローバルテーブルを設定できます。グローバルテーブルは、既存の DynamoDB API を使用してテーブルにデータを読み書きするため、アプリケーションの変更は必要ありません。プロビジョニングまたは使用したリソースに対してのみ料金が発生します。前払い料金や契約はありません。


| **プロパティ** | **同一アカウントグローバルテーブル** | **マルチアカウントグローバルテーブル** | 
| --- | --- | --- | 
| 主なユースケース | 単一 AWS アカウント内のアプリケーションのマルチリージョン回復性 | 異なるチーム、異なるビジネスユニット、またはアカウント間の強力なセキュリティ境界が所有するアプリケーションのための、マルチリージョン、マルチアカウントレプリケーション | 
| アカウントモデル: | すべてのレプリカは 1 つの AWS アカウントで作成および管理されます | 同じデプロイ内の複数の AWS アカウントに作成されたレプリカ | 
| リソース所有権 | 1 つのアカウントがテーブルとすべてのレプリカを所有します | 各アカウントはローカルレプリカを所有し、レプリケーショングループはアカウントにまたがります | 
| サポートされているバージョン | グローバルテーブルバージョン 2019.11.21 (現行) およびバージョン 2017.11.29 (レガシー) | グローバルテーブルバージョン 2019.11.21 (現行) | 
| コントロールプレーンオペレーション | テーブル所有者アカウントを使用してレプリカを作成、変更、削除します | 分散コントロールプレーンオペレーション: アカウントがレプリケーショングループに参加または退出します | 
| データプレーンオペレーション | リージョンあたりの標準 DynamoDB エンドポイント | アカウント/リージョンあたりのデータプレーンアクセス、レプリケーショングループ経由のルーティング | 
| セキュリティ境界 | 単一の IAM と KMS の境界 | アカウントごとに異なる IAM、KMS、請求、CloudTrail、ガバナンス | 
| 最適な用途 | テーブルの一元的な所有権を持つ組織 | フェデレーティッドチーム、ガバナンスの境界、またはマルチアカウント設定を持つ組織 | 

**Topics**
+ [整合性モード](#GlobalTables.consistency-modes)
+ [アカウント設定](#GlobalTables.account-configurations)
+ [グローバルテーブルの重要な概念](globaltables-CoreConcepts.md)
+ [DynamoDB 同一アカウントグローバルテーブル](globaltables-SameAccount.md)
+ [DynamoDB マルチアカウントグローバルテーブル](globaltables-MultiAccount.md)
+ [Amazon DynamoDB のグローバルテーブルの請求について](global-tables-billing.md)
+ [DynamoDB グローバルテーブルのバージョン](V2globaltables_versions.md)
+ [グローバルテーブルのベストプラクティス](globaltables-bestpractices.md)

# グローバルテーブルの重要な概念
<a name="globaltables-CoreConcepts"></a>

以下のセクションでは、Amazon DynamoDB におけるグローバルテーブルの概念および動作について説明します

## 概念
<a name="globaltables-CoreConcepts.KeyConcepts"></a>

*グローバルテーブル*は、AWS リージョン間でテーブルデータをレプリケートする DynamoDB 機能です。

*レプリカテーブル* (または、レプリカ) は、グローバルテーブルの一環として機能する DynamoDB テーブルです。グローバルテーブルは、異なる AWS リージョンにまたがる 2 つ以上のレプリカテーブルで構成されます。各グローバルテーブルは、AWS リージョンごとに 1 つのレプリカのみを持つことができます。グローバルテーブル内のすべてのレプリカは、同じテーブル名、プライマリキースキーマ、および項目データを共有します。

アプリケーションが 1 つのリージョンのレプリカにデータを書き込むと、DynamoDB は自動的にその書き込みをグローバルテーブル内の他のすべてのレプリカにレプリケートします。グローバルテーブルの詳しい使用方法については、「[チュートリアル: グローバルテーブルの作成](V2globaltables.tutorial.md)」または「[チュートリアル: マルチアカウントグローバルテーブルの作成](V2globaltables_MA.tutorial.md)」を参照してください。

## バージョン
<a name="globaltables-CoreConcepts.Versions"></a>

DynamoDB グローバルテーブルには、[グローバルテーブルバージョン 2019.11.21 (現行)](GlobalTables.md) と [グローバルテーブルバージョン 2017.11.29 (レガシー)](globaltables.V1.md) の 2 つのバージョンがあります。可能な限り、グローバルテーブルバージョン 2019.11.21 (現行) を使用する必要があります。このドキュメントセクションの情報は、バージョン 2019.11.21 (現行) 用です。詳細については、「グローバルテーブルのバージョンを確認する[グローバルテーブルのバージョンを確認する](V2globaltables_versions.md#globaltables.DetermineVersion)」を参照してください。

## 利用可能な状況
<a name="globaltables-CoreConcepts.availability"></a>

グローバルテーブルは、マルチリージョンの高可用性アーキテクチャの実装を容易にすることで、ビジネス継続性の向上に役立ちます。1 つの AWS リージョンのワークロードに障害が発生した場合、アプリケーショントラフィックを別のリージョンに移行し、同じグローバルテーブル内の別のレプリカテーブルに対して読み取りと書き込みを実行できます。

グローバルテーブル内の各レプリカテーブルは、単一リージョンの DynamoDB テーブルと同じ耐久性と可用性を提供します。グローバルテーブルは 99.999% の可用性[サービスレベルアグリーメント (SLA)](https://aws.amazon.com//dynamodb/sla/) を提供しますが、単一リージョンテーブルでは 99.99% です。

## フォールトインジェクションテスト
<a name="fault-injection-testing"></a>

MREC と MRSC のグローバルテーブルはどちらも、制御されたフォールトインジェクション実験を実行してアプリケーションの耐障害性を向上させるためのフルマネージドサービスである [AWS Fault Injection Service](https://docs.aws.amazon.com/resilience-hub/latest/userguide/testing.html) (AWS FIS) と統合されています。AWS FIS を使用すると、次のことができます。
+ 特定の障害シナリオを定義する実験テンプレートを作成します。
+ アプリケーションの耐障害性を検証するため、リージョンの分離 (つまり、選択したレプリカとのレプリケーションを一時停止すること) をシミュレートして障害を注入し、エラー処理、復旧メカニズム、1 つの AWS リージョンで中断が発生した場合のマルチリージョントラフィックシフトの動作をテストします。

例えば、米国東部 (バージニア北部)、米国東部 (オハイオ)、米国西部 (オレゴン) にレプリカがあるグローバルテーブルでは、米国東部 (バージニア北部) と米国西部 (オレゴン) が通常のオペレーションを継続している間に、米国東部 (オハイオ) で実験を実行してリージョンの分離をテストできます。この制御されたテストは、本番稼働用ワークロードに影響を与える前に潜在的な問題を特定して解決するのに役立ちます。

AWS FIS でサポートされているアクションの完全なリストについては、「*AWS FIS ユーザーガイド*」の「[アクションターゲット](https://docs.aws.amazon.com/fis/latest/userguide/action-sequence.html#action-targets)」を参照してください。また、リージョン間の DynamoDB レプリケーションを一時停止するには、「[クロスリージョン接続](https://docs.aws.amazon.com/fis/latest/userguide/cross-region-scenario.html)」を参照してください。

AWS FIS で使用できる Amazon DynamoDB グローバルテーブルアクションの詳細については、「*AWS FIS ユーザーガイド*」の「[DynamoDB グローバルテーブルアクションリファレンス](https://docs.aws.amazon.com/fis/latest/userguide/fis-actions-reference.html#dynamodb-actions-reference)」を参照してください。

フォールトインジェクション実験の実行を開始するには、「AWS FIS ユーザーガイド」の「[AWS FIS 実験の計画](https://docs.aws.amazon.com/fis/latest/userguide/getting-started-planning.html)」を参照してください。

**注記**  
MRSC での AWS FIS 実験中は、結果整合性のある読み込みが許可されますが、MREC と同様に請求モードの変更やテーブルスループットの設定などのテーブル設定の更新は許可されません。エラーコードの詳細については、CloudWatch メトリクス [`FaultInjectionServiceInducedErrors`](metrics-dimensions.md#FaultInjectionServiceInducedErrors) を確認してください。

## 有効期限 (TTL)
<a name="global-tables-ttl"></a>

MREC 用に設定されたグローバルテーブルは[有効期限](TTL.md) (TTL) 削除の設定をサポートしています。TTL 設定は、グローバルテーブル内のすべてのレプリカに対して自動的に同期されます。TTL がリージョン内のレプリカから項目を削除すると、削除はグローバルテーブル内の他のすべてのレプリカにレプリケートされます。TTL は書き込み容量を消費しないため、削除が発生したリージョンでは TTL 削除に対して課金されません。ただし、グローバルテーブルのレプリカが存在する他のリージョンでレプリケートされた削除に対しては課金されます。

TTL 削除レプリケーションは、削除がレプリケートされるレプリカの書き込み容量を消費します。プロビジョンドキャパシティ用に設定されたレプリカは、書き込みスループットと TTL 削除スループットの組み合わせがプロビジョンド書き込みキャパシティを超える場合、リクエストをスロットリングすることがあります。

マルチリージョンの強力な整合性 (MRSC) 用に設定されたグローバルテーブルは、有効期限 (TTL) 削除の設定をサポートしていません。

## Streams
<a name="global-tables-streams"></a>

マルチリージョンの結果整合性 (MREC) 用に設定されたグローバルテーブルは、レプリカテーブルの [DynamoDB Stream](Streams.md) からこれらの変更を読み取り、その変更を他のすべてのレプリカテーブルに適用することで、変更をレプリケートします。したがって、Streams は MREC グローバルテーブル内のすべてのレプリカでデフォルトで有効になっており、それらのレプリカで無効にすることはできません。MREC レプリケーションプロセスでは、短期間に複数の変更を 1 つのレプリケートされた書き込みに結合する場合があり、各レプリカの Stream にわずかに異なるレコードが含まれる場合があります。MREC レプリカのストリームレコードは、同じ項目に対するすべての変更の順序を維持しますが、異なる項目に対する変更の相対的な順序はレプリカによって異なる場合があります。

グローバルテーブル内の他のリージョンではなく、特定のリージョンで発生した変更について Streams レコードを処理するアプリケーションを作成する場合は、各項目にその項目の変更が発生したリージョンを定義する属性を追加できます。この属性を使用して、特定のリージョンの変更に対してのみ Lambda 関数を呼び出す Lambda イベントフィルターの使用など、他のリージョンで発生した変更について Streams レコードをフィルタリングできます。

マルチリージョンの強力な整合性 (MRSC) 用に設定されたグローバルテーブルは、レプリケーションに DynamoDB Streams を使用しないため、MRSC レプリカでは Streams はデフォルトで有効になっていません。MRSC レプリカで Streams を有効にできます。MRSC レプリカの Streams レコードは、Stream レコードの順序付けを含め、すべてのレプリカで同じです。

## トランザクション
<a name="global-tables-transactions"></a>

MREC 用に設定されたグローバルテーブルでは、DynamoDB トランザクションオペレーション ([https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html) および [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html)) は、操作が呼び出されたリージョン内でのみアトミックです。トランザクション書き込みはリージョン間でユニットとしてレプリケートされません。つまり、特定の時点で、トランザクション内の書き込みの一部のみが、他のレプリカでの読み取り操作によって返される可能性があります。

例えば、米国東部 (オハイオ) リージョンと米国西部 (オレゴン) リージョンにレプリカを持つグローバルテーブルがある場合、米国東部 (オハイオ) リージョンで `TransactWriteItems` オペレーションを実行すると、変更がレプリケートされたときに米国西部 (オレゴン) では部分的に完了したトランザクションが観察されることがあります。変更は、ソースリージョンでコミットされた場合にのみ、他のリージョンにレプリケートされます。

マルチリージョンの強力な整合性 (MRSC) 用に設定されたグローバルテーブルはトランザクションオペレーションをサポートしておらず、これらのオペレーションが MRSC レプリカで呼び出されるとエラーが返されます。

## 読み取りおよび書き込みスループット
<a name="globaltables-CoreConcepts.Throughput"></a>

### プロビジョニングモード
<a name="gt_throughput.provisioned"></a>

レプリケーションは書き込み容量を消費します。プロビジョンドキャパシティ用に設定されたレプリカは、アプリケーションの書き込みスループットとレプリケーションスループットの組み合わせがプロビジョンド書き込みキャパシティを超えると、リクエストをスロットリングすることがあります。プロビジョニングモードを使用するグローバルテーブルの場合、読み取り容量と書き込み容量の両方の自動スケーリング設定がレプリカ間で同期されます。

レプリカレベルで [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ProvisionedThroughputOverride.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ProvisionedThroughputOverride.html) パラメータを使用して、グローバルテーブル内の各レプリカの読み取り容量設定を個別に設定できます。デフォルトでは、プロビジョニングされた読み取り容量への変更は、グローバルテーブル内のすべてのレプリカに適用されます。グローバルテーブルに新しいレプリカを追加する場合、レプリカレベルのオーバーライドが明示的に指定されていない限り、ソーステーブルまたはレプリカの読み取り容量が初期値として使用されます。

### オンデマンドモード
<a name="gt_throughput.on-demand"></a>

オンデマンドモードに設定されたグローバルテーブルの場合、書き込み容量はすべてのレプリカ間で自動的に同期されます。DynamoDB はトラフィックに基づいて容量を自動的に調整するため、レプリカ固有の読み取りまたは書き込み容量設定を管理する必要はありません。

## グローバルテーブルのモニタリング
<a name="monitoring-global-tables"></a>

マルチリージョンの結果整合性 (MREC) 用に設定されたグローバルテーブルは [`ReplicationLatency`](metrics-dimensions.md#ReplicationLatency) メトリクスを CloudWatch に発行します。このメトリクスは、項目がレプリカテーブルに書き込まれてから、その項目がグローバルテーブルの別のレプリカに表示されるまでの経過時間を追跡します。`ReplicationLatency` はミリ秒単位で表し、グローバルテーブル内のすべての送信元と送信先のリージョンペアに対して出力されます。

一般的な `ReplicationLatency` 値は、選択した AWS リージョン間の距離と、ワークロードタイプやスループットなどの他の変数によって異なります。例えば、米国西部 (北カリフォルニア) (us-west-1) リージョンのソースレプリカは、アフリカ (ケープタウン) (af-south-1) リージョンと比較して、米国西部 (オレゴン) (us-west-2) リージョンへの `ReplicationLatency` が低くなります。

`ReplicationLatency` の値が上昇している場合、1 つのレプリカからの更新が他のレプリカテーブルにタイムリーに伝播されていないことを示している可能性があります。この場合、アプリケーションの読み込みおよび書き込みアクティビティを別の AWS リージョンに一時的にリダイレクトすることができます。

マルチリージョンの強力な整合性 (MRSC) 用に設定されたグローバルテーブルは、`ReplicationLatency` メトリクスを発行しません。

## グローバルテーブルの管理に関する考慮事項
<a name="management-considerations"></a>

新しいグローバルテーブルレプリカの追加に使用したテーブルは、新しいレプリカが作成されてから 24 時間が経過するまで削除できません。

グローバルテーブルレプリカを含む AWS リージョンを無効にすると、それらのレプリカは、リージョンが無効になってから 20 時間後に単一リージョンテーブルに永続的に変換されます。

# DynamoDB 同一アカウントグローバルテーブル
<a name="globaltables-SameAccount"></a>

同一アカウントグローバルテーブルは、1 つの AWS アカウント内の AWS リージョン間で DynamoDB テーブルデータを自動的にレプリケートします。同一アカウントグローバルテーブルは、すべてのレプリカが同じアカウントの境界、所有権、アクセス許可モデルを共有するため、マルチリージョンアプリケーションを実行するための最もシンプルなモデルを提供します。レプリカテーブル用の AWS リージョンを選択すると、グローバルテーブルはすべてのレプリケーションを自動的に処理します。グローバルテーブルは、DynamoDB が利用可能なすべてのリージョンで使用できます。

同一アカウントグローバルテーブルには以下の利点があります。
+ 選択した AWS リージョン間で DynamoDB テーブルデータを自動的にレプリケートして、ユーザーに近いデータを配置する
+ リージョンの分離またはパフォーマンス低下時にアプリケーションの可用性を高める
+ 組み込みの競合の解決を使用して、アプリケーションのビジネスロジックに集中できるようにする
+ 同一アカウントグローバルテーブルを作成するときは、[マルチリージョンの結果整合性 (MREC)](V2globaltables_HowItWorks.md#V2globaltables_HowItWorks.consistency-modes.mrec) または [マルチリージョンの強力な整合性 (MRSC)](V2globaltables_HowItWorks.md#V2globaltables_HowItWorks.consistency-modes.mrsc) のいずれかを選択できます。

**Topics**
+ [DynamoDB グローバルテーブルの仕組み](V2globaltables_HowItWorks.md)
+ [チュートリアル: グローバルテーブルの作成](V2globaltables.tutorial.md)
+ [DynamoDB グローバルテーブルのセキュリティ](globaltables-security.md)

# DynamoDB グローバルテーブルの仕組み
<a name="V2globaltables_HowItWorks"></a>

以下のセクションでは、Amazon DynamoDB におけるグローバルテーブルの概念および動作について説明します。

## 概念
<a name="V2globaltables_HowItWorks.KeyConcepts"></a>

*グローバルテーブル*は、AWS リージョン間でテーブルデータをレプリケートする DynamoDB 機能です。

*レプリカテーブル* (または、レプリカ) は、グローバルテーブルの一環として機能する DynamoDB テーブルです。グローバルテーブルは、異なる AWS リージョンにまたがる 2 つ以上のレプリカテーブルで構成されます。各グローバルテーブルは、AWS リージョンごとに 1 つのレプリカのみを持つことができます。グローバルテーブル内のすべてのレプリカは、同じテーブル名、プライマリキースキーマ、および項目データを共有します。

アプリケーションが 1 つのリージョンのレプリカにデータを書き込むと、DynamoDB は自動的にその書き込みをグローバルテーブル内の他のすべてのレプリカにレプリケートします。グローバルテーブルの詳しい使用方法については、[チュートリアル: グローバルテーブルの作成](V2globaltables.tutorial.md) を参照してください。

## バージョン
<a name="V2globaltables_HowItWorks.versions"></a>

DynamoDB グローバルテーブルには、バージョン 2019.11.21 (現行) と[バージョン 2017.11.29 (レガシー)](globaltables.V1.md) の 2 つのバージョンがあります。可能な限り、バージョン 2019.11.21 (現行) を使用する必要があります。このドキュメントセクションの情報は、バージョン 2019.11.21 (現行) 用です。詳細については、「[グローバルテーブルのバージョンを確認する](V2globaltables_versions.md#globaltables.DetermineVersion)」を参照してください。

## 利用可能な状況
<a name="V2globaltables_HowItWorks.availability"></a>

グローバルテーブルは、マルチリージョンの高可用性アーキテクチャの実装を容易にすることで、ビジネス継続性の向上に役立ちます。1 つの AWS リージョンのワークロードに障害が発生した場合、アプリケーショントラフィックを別のリージョンに移行し、同じグローバルテーブル内の別のレプリカテーブルに対して読み取りと書き込みを実行できます。

グローバルテーブル内の各レプリカテーブルは、単一リージョンの DynamoDB テーブルと同じ耐久性と可用性を提供します。グローバルテーブルは 99.999% の可用性[サービスレベルアグリーメント (SLA)](https://aws.amazon.com//dynamodb/sla/) を提供しますが、単一リージョンテーブルでは 99.99% です。

## 整合性モード
<a name="V2globaltables_HowItWorks.consistency-modes"></a>

グローバルテーブルを作成するときに、その整合性モードを設定できます。グローバルテーブルは、マルチリージョンの結果整合性 (MREC) およびマルチリージョンの強力な整合性 (MRSC) の 2 つの整合性モードをサポートしています。

グローバルテーブルの作成時に整合性モードを指定しない場合、グローバルテーブルはデフォルトでマルチリージョンの結果整合性 (MREC) になります。グローバルテーブルには、異なる整合性モードで設定されたレプリカを含めることはできません。グローバルテーブルの整合性モードを作成後に変更することはできません。

### マルチリージョンの結果整合性 (MREC)
<a name="V2globaltables_HowItWorks.consistency-modes.mrec"></a>

マルチリージョンの結果整合性 (MREC) は、グローバルテーブルのデフォルトの整合性モードです。MREC グローバルテーブルレプリカの項目の変更は、通常は 1 秒以内で他のすべてのレプリカに非同期でレプリケートされます。万一、MREC グローバルテーブルのレプリカで分離または障害が発生した場合、他のリージョンにまだレプリケートされていないデータは、レプリカが正常になるとレプリケートされます。

同じ項目が複数のリージョンで同時に変更された場合、DynamoDB は項目ごとに最新の内部タイムスタンプを使用して競合を解決します。これは「最終書き込み者優先」競合解決方法と呼ばれます。最終的に、項目はすべてのレプリカで最後の書き込みによって作成されたバージョンに収束します。

[強力な整合性のある読み込みオペレーション](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html#DDB-GetItem-request-ConsistentRead)は、その項目が読み取りが発生したリージョンで最後に更新された場合は項目の最新バージョンを返しますが、項目が別のリージョンで最後に更新された場合は古いデータを返す可能性があります。条件付き書き込みは、リージョン内の項目のバージョンに対して条件式を評価します。

MREC グローバルテーブルを作成するには、既存の DynamoDB テーブルにレプリカを追加します。レプリカを追加しても、既存の単一リージョン DynamoDB テーブルまたはグローバルテーブルレプリカのパフォーマンスには影響しません。MREC グローバルテーブルにレプリカを追加して、データがレプリケートされるリージョンの数を拡張したり、不要になったレプリカを MREC グローバルテーブルから削除したりできます。MREC グローバルテーブルは、DynamoDB が利用可能な任意のリージョンにレプリカを持つことができ、[AWS パーティション](https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/partitions.html)内のリージョンと同じ数のレプリカを持つことができます。

### マルチリージョンの強力な整合性 (MRSC)
<a name="V2globaltables_HowItWorks.consistency-modes.mrsc"></a>

グローバルテーブルを作成する際に、マルチリージョンの強力な整合性 (MRSC) モードを設定できます。MRSC グローバルテーブルレプリカの項目の変更は、書き込み操作が正常なレスポンスを返す前に、少なくとも 1 つの他のリージョンに同期的にレプリケートされます。MRSC レプリカに対する強力な整合性のある読み込み操作は、常に項目の最新バージョンを返します。条件付き書き込みは、常に条件式を項目の最新バージョンと照合して評価します。

MRSC グローバルテーブルは、厳密に 3 つのリージョンにデプロイする必要があります。MRSC グローバルテーブルは、3 つのレプリカ、または 2 つのレプリカと 1 つの監視で設定できます。監視は、グローバルテーブルレプリカに書き込まれたデータを含む MRSC グローバルテーブルのコンポーネントであり、MRSC の可用性アーキテクチャをサポートしながら、完全なレプリカに代わるオプションを提供します。監視に対して読み取りまたは書き込み操作を実行することはできません。監視は、2 つのレプリカとは異なるリージョンにあります。MRSC グローバルテーブルを作成する際、MRSC テーブルの作成時にレプリカと監視デプロイの両方のリージョンを選択します。MRSC グローバルテーブルに監視が設定されているかどうか、どのリージョンに設定されているかは、[https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeTable.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeTable.html) API の出力から判断できます。監視は DynamoDB によって所有および管理され、設定されているリージョンの AWS アカウントには表示されません。

MRSC グローバルテーブルは、次のリージョンセットで利用できます。米国リージョンセット (米国東部バージニア北部、米国東部オハイオ、米国西部オレゴン)、欧州リージョンセット (欧州アイルランド、欧州ロンドン、欧州パリ、欧州フランクフルト)、および AP リージョンセット (アジアパシフィック東京、アジアパシフィックソウル、アジアパシフィック大阪)。MRSC グローバルテーブルはリージョンセットにまたがることはできません (例えば、MRSC グローバルテーブルに米国と欧州の両方のリージョンセットからのレプリカを含めることはできません)。

MRSC グローバルテーブルを作成するには、データを含まない既存の DynamoDB テーブルに 1 つのレプリカと監視または 2 つのレプリカを追加します。既存の単一リージョンテーブルを MRSC グローバルテーブルに変換する場合は、テーブルが空であることを確認する必要があります。単一リージョンテーブルを既存の項目を持つ MRSC グローバルテーブルに変換することはサポートされていません。変換プロセス中にデータがテーブルに書き込まれないよう確認してください。既存の MRSC グローバルテーブルに追加のレプリカを追加することはできません。MRSC グローバルテーブルから単一のレプリカまたは監視を削除することはできません。MRSC グローバルテーブルから 2 つのレプリカを削除するか、1 つのレプリカと監視を削除して、残りのレプリカを単一リージョンの DynamoDB テーブルに変換できます。

別のリージョンで既に変更されている項目を変更しようとすると、書き込み操作は `ReplicatedWriteConflictException` で失敗します。`ReplicatedWriteConflictException` で失敗した書き込みは再試行でき、項目が別のリージョンで変更されなくなった場合に成功します。

MRSC グローバルテーブルには、次の考慮事項が適用されます。
+ MRSC グローバルテーブルでは、有効期限 (TTL) はサポートされていません。
+ ローカルセカンダリインデックス (LSI) は、MRSC グローバルテーブルではサポートされていません。
+ CloudWatch Contributor Insights の情報は、オペレーションが発生したリージョンについてのみ報告されます。

## 整合性モードの選択
<a name="V2globaltables_HowItWorks.choosing-consistency-mode"></a>

マルチリージョン整合性モードを選択するための重要な基準は、アプリケーションが低レイテンシーの書き込みと強力な整合性のある読み込みを優先するか、グローバルな強力な整合性を優先するかです。

MREC グローバルテーブルは、MRSC グローバルテーブルと比較して書き込みレイテンシーが低く、強力な整合性のある読み込みレイテンシーを持ちます。MREC グローバルテーブルには、レプリカ間のレプリケーション遅延と等しい目標復旧時点 (RPO) があり、レプリカのリージョンに応じて通常は数秒です。

次の場合は、MREC モードを使用する必要があります。
+ アプリケーションは、データが別のリージョンで更新された場合に、強力な整合性のある読み込みオペレーションから返される古いデータを許容できます。
+ マルチリージョンの読み取り整合性よりも、書き込みレイテンシーが低く、強力な整合性のある読み込みレイテンシーを優先します。
+ マルチリージョンの高可用性戦略では、0 を超える RPO を許容できます。

MRSC グローバルテーブルは、MREC グローバルテーブルと比較して書き込みレイテンシーが高く、強力な整合性のある読み込みレイテンシーを持ちます。MRSC グローバルテーブルは、目標復旧時点 (RPO) ゼロをサポートします。

MRSC モードは、次の場合に使用します。
+ 複数のリージョン間で強力な整合性のある読み込みが必要です。
+ グローバル読み取り整合性は、書き込みレイテンシーの短縮よりも優先されます。
+ マルチリージョンの高可用性戦略では、RPO を 0 にする必要があります。

## グローバルテーブルのモニタリング
<a name="monitoring-global-tables"></a>

マルチリージョンの結果整合性 (MREC) 用に設定されたグローバルテーブルは [`ReplicationLatency`](metrics-dimensions.md#ReplicationLatency) メトリクスを CloudWatch に発行します。このメトリクスは、項目がレプリカテーブルに書き込まれてから、その項目がグローバルテーブルの別のレプリカに表示されるまでの経過時間を追跡します。`ReplicationLatency` はミリ秒単位で表し、グローバルテーブル内のすべての送信元と送信先のリージョンペアに対して出力されます。

一般的な `ReplicationLatency` 値は、選択した AWS リージョン間の距離と、ワークロードタイプやスループットなどの他の変数によって異なります。例えば、米国西部 (北カリフォルニア) (us-west-1) リージョンのソースレプリカは、アフリカ (ケープタウン) (af-south-1) リージョンと比較して、米国西部 (オレゴン) (us-west-2) リージョンへの `ReplicationLatency` が低くなります。

`ReplicationLatency` の値が上昇している場合、1 つのレプリカからの更新が他のレプリカテーブルにタイムリーに伝播されていないことを示している可能性があります。この場合、アプリケーションの読み込みおよび書き込みアクティビティを別の AWS リージョンに一時的にリダイレクトすることができます。

マルチリージョンの強力な整合性 (MRSC) 用に設定されたグローバルテーブルは、`ReplicationLatency` メトリクスを発行しません。

## フォールトインジェクションテスト
<a name="fault-injection-testing"></a>

MREC と MRSC のグローバルテーブルはどちらも、制御されたフォールトインジェクション実験を実行してアプリケーションの耐障害性を向上させるためのフルマネージドサービスである [AWS Fault Injection Service](https://docs.aws.amazon.com/resilience-hub/latest/userguide/testing.html) (AWS FIS) と統合されています。AWS FIS を使用すると、次のことができます。
+ 特定の障害シナリオを定義する実験テンプレートを作成します。
+ アプリケーションの耐障害性を検証するため、リージョンの分離 (つまり、選択したレプリカとのレプリケーションを一時停止すること) をシミュレートして障害を注入し、エラー処理、復旧メカニズム、1 つの AWS リージョンで中断が発生した場合のマルチリージョントラフィックシフトの動作をテストします。

例えば、米国東部 (バージニア北部)、米国東部 (オハイオ)、米国西部 (オレゴン) にレプリカがあるグローバルテーブルでは、米国東部 (バージニア北部) と米国西部 (オレゴン) が通常のオペレーションを継続している間に、米国東部 (オハイオ) で実験を実行してリージョンの分離をテストできます。この制御されたテストは、本番稼働用ワークロードに影響を与える前に潜在的な問題を特定して解決するのに役立ちます。

AWS FIS でサポートされているアクションの完全なリストについては、「*AWS FIS ユーザーガイド*」の「[アクションターゲット](https://docs.aws.amazon.com/fis/latest/userguide/action-sequence.html#action-targets)」を参照してください。また、リージョン間の DynamoDB レプリケーションを一時停止するには、「[クロスリージョン接続](https://docs.aws.amazon.com/fis/latest/userguide/cross-region-scenario.html)」を参照してください。

AWS FIS で使用できる Amazon DynamoDB グローバルテーブルアクションの詳細については、「*AWS FIS ユーザーガイド*」の「[DynamoDB グローバルテーブルアクションリファレンス](https://docs.aws.amazon.com/fis/latest/userguide/fis-actions-reference.html#dynamodb-actions-reference)」を参照してください。

フォールトインジェクション実験の実行を開始するには、「AWS FIS ユーザーガイド」の「[AWS FIS 実験の計画](https://docs.aws.amazon.com/fis/latest/userguide/getting-started-planning.html)」を参照してください。

**注記**  
MRSC での AWS FIS 実験中は、結果整合性のある読み込みが許可されますが、MREC と同様に請求モードの変更やテーブルスループットの設定などのテーブル設定の更新は許可されません。エラーコードの詳細については、CloudWatch メトリクス [`FaultInjectionServiceInducedErrors`](metrics-dimensions.md#FaultInjectionServiceInducedErrors) を確認してください。

## 有効期限 (TTL)
<a name="global-tables-ttl"></a>

MREC 用に設定されたグローバルテーブルは[有効期限](TTL.md) (TTL) 削除の設定をサポートしています。TTL 設定は、グローバルテーブル内のすべてのレプリカに対して自動的に同期されます。TTL がリージョン内のレプリカから項目を削除すると、削除はグローバルテーブル内の他のすべてのレプリカにレプリケートされます。TTL は書き込み容量を消費しないため、削除が発生したリージョンでは TTL 削除に対して課金されません。ただし、グローバルテーブルのレプリカが存在する他のリージョンでレプリケートされた削除に対しては課金されます。

TTL 削除レプリケーションは、削除がレプリケートされるレプリカの書き込み容量を消費します。プロビジョンドキャパシティ用に設定されたレプリカは、書き込みスループットと TTL 削除スループットの組み合わせがプロビジョンド書き込みキャパシティを超える場合、リクエストをスロットリングすることがあります。

マルチリージョンの強力な整合性 (MRSC) 用に設定されたグローバルテーブルは、有効期限 (TTL) 削除の設定をサポートしていません。

## Streams
<a name="global-tables-streams"></a>

マルチリージョンの結果整合性 (MREC) 用に設定されたグローバルテーブルは、レプリカテーブルの [DynamoDB Stream](Streams.md) からこれらの変更を読み取り、その変更を他のすべてのレプリカテーブルに適用することで、変更をレプリケートします。したがって、Streams は MREC グローバルテーブル内のすべてのレプリカでデフォルトで有効になっており、それらのレプリカで無効にすることはできません。MREC レプリケーションプロセスでは、短期間に複数の変更を 1 つのレプリケートされた書き込みに結合する場合があり、各レプリカの Stream にわずかに異なるレコードが含まれる場合があります。MREC レプリカの Streams レコードは常に項目ごとに順序付けられますが、項目間の順序はレプリカによって異なる場合があります。

マルチリージョンの強力な整合性 (MRSC) 用に設定されたグローバルテーブルは、レプリケーションに DynamoDB Streams を使用しないため、MRSC レプリカでは Streams はデフォルトで有効になっていません。MRSC レプリカで Streams を有効にできます。MRSC レプリカの Streams レコードは、Stream レコードの順序付けを含め、すべてのレプリカで同じです。

グローバルテーブル内の他のリージョンではなく、特定のリージョンで発生した変更について Streams レコードを処理するアプリケーションを作成する場合は、各項目にその項目の変更が発生したリージョンを定義する属性を追加できます。この属性を使用して、特定のリージョンの変更に対してのみ Lambda 関数を呼び出す Lambda イベントフィルターの使用など、他のリージョンで発生した変更について Streams レコードをフィルタリングできます。

## トランザクション
<a name="global-tables-transactions"></a>

MREC 用に設定されたグローバルテーブルでは、DynamoDB トランザクションオペレーション ([https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html) および [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html)) は、操作が呼び出されたリージョン内でのみアトミックです。トランザクション書き込みはリージョン間でユニットとしてレプリケートされません。つまり、特定の時点で、トランザクション内の書き込みの一部のみが、他のレプリカでの読み取り操作によって返される可能性があります。

例えば、米国東部 (オハイオ) リージョンと米国西部 (オレゴン) リージョンにレプリカを持つグローバルテーブルがある場合、米国東部 (オハイオ) リージョンで `TransactWriteItems` オペレーションを実行すると、変更がレプリケートされたときに米国西部 (オレゴン) では部分的に完了したトランザクションが観察されることがあります。変更は、ソースリージョンでコミットされた場合にのみ、他のリージョンにレプリケートされます。

マルチリージョンの強力な整合性 (MRSC) 用に設定されたグローバルテーブルはトランザクションオペレーションをサポートしておらず、これらのオペレーションが MRSC レプリカで呼び出されるとエラーが返されます。

## 読み取りおよび書き込みスループット
<a name="V2globaltables_HowItWorks.Throughput"></a>

### プロビジョニングモード
<a name="gt_throughput.provisioned"></a>

レプリケーションは書き込み容量を消費します。プロビジョンドキャパシティ用に設定されたレプリカは、アプリケーションの書き込みスループットとレプリケーションスループットの組み合わせがプロビジョンド書き込みキャパシティを超えると、リクエストをスロットリングすることがあります。プロビジョニングモードを使用するグローバルテーブルの場合、読み取り容量と書き込み容量の両方の自動スケーリング設定がレプリカ間で同期されます。

レプリカレベルで [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ProvisionedThroughputOverride.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ProvisionedThroughputOverride.html) パラメータを使用して、グローバルテーブル内の各レプリカの読み取り容量設定を個別に設定できます。デフォルトでは、プロビジョニングされた読み取り容量への変更は、グローバルテーブル内のすべてのレプリカに適用されます。グローバルテーブルに新しいレプリカを追加する場合、レプリカレベルのオーバーライドが明示的に指定されていない限り、ソーステーブルまたはレプリカの読み取り容量が初期値として使用されます。

### オンデマンドモード
<a name="gt_throughput.on-demand"></a>

オンデマンドモードに設定されたグローバルテーブルの場合、書き込み容量はすべてのレプリカ間で自動的に同期されます。DynamoDB はトラフィックに基づいて容量を自動的に調整するため、レプリカ固有の読み取りまたは書き込み容量設定を管理する必要はありません。

## 設定の同期
<a name="V2globaltables_HowItWorks.setting-synchronization"></a>

DynamoDB グローバルテーブルの設定は、テーブルの動作とレプリケーションのさまざまな側面を制御する設定パラメータです。これらの設定は DynamoDB コントロールプレーン API を通じて管理され、グローバルテーブルを作成または変更するときに設定できます。グローバルテーブルは、すべてのレプリカで特定の設定を自動的に同期して一貫性を維持し、リージョン固有の最適化に柔軟性を持たせます。どの設定が同期され、どのように動作するかを理解することは、グローバルテーブルを効果的に設定するのに役立ちます。設定は、レプリカ間でどのように同期されるかに基づいて、3 つの主要なカテゴリに分類されます。

次の設定は常にグローバルテーブルのレプリカ間で同期されます。
+ キャパシティモード (プロビジョンドキャパシティまたはオンデマンド)
+ テーブルのプロビジョニングされた書き込み容量
+ テーブル書き込みの自動スケーリング
+ キースキーマの属性定義
+ グローバルセカンダリインデックス (GSI) 定義
+ GSI のプロビジョニングされた書き込みキャパシティ
+ GSI の書き込み自動スケーリング
+ サーバー側の暗号化 (SSE)
+ MREC モードでの Streams 定義
+ 有効期限 (TTL)
+ ウォームスループット
+ オンデマンドの最大書き込みスループット

以下の設定はレプリカ間で同期されますが、レプリカごとに上書きできます。
+ テーブルのプロビジョニングされた読み込みキャパシティ
+ テーブル読み取りの自動スケーリング
+ GSI のプロビジョニングされた読み込み容量キャパシティ
+ GSI の読み取り自動スケーリング
+ テーブルクラス
+ オンデマンドの最大読み取りスループット

**注記**  
上書き可能な設定値は、設定が他のレプリカで変更されると変更されます。例えば、米国東部 (バージニア北部) と米国西部 (オレゴン) にレプリカを持つ MREC グローバルテーブルがあるとします。米国東部 (バージニア北部) レプリカのプロビジョニングされた読み込みスループットは 200 RCU に設定されています。米国西部 (オレゴン) のレプリカでは、プロビジョニングされた読み込みスループットオーバーライドが 100 RCU に設定されています。米国東部 (バージニア北部) レプリカのプロビジョニングされた読み込みスループット設定を 200 RCU から 300 RCU に更新すると、新しいプロビジョニングされた読み込みスループット値が米国西部 (オレゴン) のレプリカにも適用されます。これにより、米国西部 (オレゴン) レプリカのプロビジョニングされた読み込みスループット設定が、オーバーライドされた値である 100 RCU から新しい値である 300 RCU に変更されます。

以下の設定はレプリカ間で同期されません。
+ 削除保護
+ ポイントインタイムリカバリ
+ タグ
+ テーブルの CloudWatch Contributor Insights の有効化
+ GSI の CloudWatch Contributor Insights の有効化
+ Kinesis Data Streams 定義
+ リソースポリシー
+ MRSC モードでの Streams 定義

他のすべての設定はレプリカ間で同期されません。

## DynamoDB Accelerator (DAX)
<a name="V2globaltables_HowItWorks.dax"></a>

グローバルテーブルレプリカへの書き込みは DynamoDB Accelerator (DAX) をバイパスし、DynamoDB を直接更新します。その結果、書き込みが DAX キャッシュを更新しないため、DAX キャッシュが古くなる可能性があります。グローバルテーブルレプリカ用に設定された DAX キャッシュは、キャッシュ TTL の有効期限が切れた場合にのみ更新されます。

## グローバルテーブルの管理に関する考慮事項
<a name="management-considerations"></a>

新しいグローバルテーブルレプリカの追加に使用したテーブルは、新しいレプリカが作成されてから 24 時間が経過するまで削除できません。

グローバルテーブルレプリカを含む AWS リージョンを無効にすると、それらのレプリカは、リージョンが無効になってから 20 時間後に単一リージョンテーブルに永続的に変換されます。

# チュートリアル: グローバルテーブルの作成
<a name="V2globaltables.tutorial"></a>

このセクションでは、任意の整合性モード用に設定された DynamoDB グローバルテーブルを作成するための手順を順を追って説明します。アプリケーションの要件に基づいて、マルチリージョンの結果整合性 (MREC) モードまたはマルチリージョンの強力な整合性 (MRSC) モードのいずれかを選択します。

MREC グローバルテーブルは、書き込みレイテンシーを低くし、AWS リージョン全体で結果整合性を実現します。MRSC グローバルテーブルは、MREC よりも書き込みレイテンシーがわずかに高いリージョン間で強力な整合性のある読み込みを提供します。データ整合性、レイテンシー、可用性に関するアプリケーションのニーズに最適な整合性モードを選択します。

**Topics**
+ [MREC 用に設定されたグローバルテーブルの作成](#V2creategt_mrec)
+ [MRSC 用に設定されたグローバルテーブルの作成](#create-gt-mrsc)

## MREC 用に設定されたグローバルテーブルの作成
<a name="V2creategt_mrec"></a>

このセクションでは、マルチリージョンの結果整合性 (MREC) モードでグローバルテーブルを作成する方法を示します。MREC はグローバルテーブルのデフォルトの整合性モードであり、AWS リージョン全体で非同期レプリケーションによる低レイテンシーの書き込みを提供します。1 つのリージョンの項目に加えられた変更は、通常、1 秒以内に他のすべてのリージョンにレプリケートされます。これにより、MREC は低い書き込みレイテンシーを優先し、異なるリージョンがわずかに異なるバージョンのデータを返す短い期間を許容できるアプリケーションに最適です。

DynamoDB が利用可能な任意の AWS リージョンでレプリカを含む MREC グローバルテーブルを作成し、レプリカをいつでも追加または削除できます。次の例は、複数のリージョンにレプリカを持つ MREC グローバルテーブルを作成する方法を示しています。

### DynamoDB コンソールを使用した MREC グローバルテーブルの作成
<a name="mrec-console"></a>

AWS マネジメントコンソール を使用してグローバルテーブルを作成するには、次の手順に従います。以下の例では、レプリカテーブルを持つグローバルテーブルを米国およびヨーロッパに作成します。

1. AWS マネジメントコンソール にサインインして DynamoDB コンソール ([https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/)) を開きます。

1. この例では、ナビゲーションバーのリージョンセレクターから **[米国東部 (オハイオ)]** を選択します。

1. コンソールの左側のナビゲーションペインで、**[テーブル]** を選択します。

1. **[Create Table]** (テーブルの作成) を選択します。

1. **[テーブルの作成]** ページで、次の操作を行います。

   1. **[テーブル名]** に **Music** と入力します。

   1. **[パーティションキー]** に **Artist** と入力します。

   1. **[ソートキー]** に **SongTitle** と入力します。

   1. 他のデフォルトの設定はそのままにして、**[テーブルの作成]** を選択します。

      この新しいテーブルは、新しいグローバルテーブルの最初のレプリカテーブルとして機能します。これは、後で追加する他のレプリカテーブルのプロトタイプです。

1. テーブルがアクティブになった後、次の操作を行います。

   1. テーブルのリストから **[ミュージック]** テーブルを選択します。

   1. **[グローバルテーブル]** タブを選択します。

   1. **レプリカの作成**を選択します。

1. **[利用可能なレプリケーションリージョン]** ドロップダウンリストから、**[米国西部 (オレゴン) us-west-2]** を選択します。

   コンソールは、選択したリージョンに同一名のテーブルが存在しないことを確認します。同一名のテーブルが存在する場合は、そのリージョンで新しいレプリカテーブルを作成する前に既存のテーブルを削除する必要があります。

1. **レプリカの作成**を選択します。これで、米国西部 (オレゴン) us-west-2 リージョンでテーブル作成プロセスが開始されます。

   **[Music]** テーブル (およびその他すべてのレプリカテーブル) の **[グローバルテーブル]** タブに、テーブルが複数のリージョンでレプリケートされたことが示されます。

1. 前のステップを繰り返して別のリージョンを追加しますが、リージョンとして **[欧州 (フランクフルト) eu-central-1]** を選択します。

1. レプリケーションをテストするには、次の操作を行います。

   1. 米国東部 (オハイオ) リージョンでAWS マネジメントコンソールを使用していることを確認します。

   1. **[テーブルアイテムの探索]** を選択します。

   1. **[項目を作成]** を選択します。

   1. **[アーティスト]** に「**item\$11**」、**[曲名]** に「**Song Value 1**」と入力します。

   1. **[項目を作成]** を選択します。

1. 他のリージョンに切り替えてレプリケーションを確認します。

   1. 右上隅のリージョンセレクターから、**[欧州 (フランクフルト)]** を選択します。

   1. **[ミュージック]** テーブルに、作成した項目が含まれていることを確認します。

   1. **[米国西部 (オレゴン)]** で検証を繰り返します。

### AWS CLI または Java を使用した MREC グローバルテーブルの作成
<a name="mrec-cli-java"></a>

------
#### [ CLI ]

次のコード例は、最終的な一貫性を備えたマルチリージョンレプリケーション (MREC) を使用して DynamoDB グローバルテーブルを管理する方法を示しています。
+ マルチリージョンレプリケーション (MREC)を備えたテーブルを作成します。
+ レプリカテーブルの項目を配置および取得します。
+ レプリカを一つずつ削除します。
+ テーブルを削除してクリーンアップします。

**Bash スクリプトを使用した AWS CLI**  
マルチリージョンレプリケーションを備えたテーブルを作成します。  

```
# Step 1: Create a new table (MusicTable) in US East (Ohio), with DynamoDB Streams enabled (NEW_AND_OLD_IMAGES)
aws dynamodb create-table \
    --table-name MusicTable \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --billing-mode PAY_PER_REQUEST \
    --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES \
    --region us-east-2

# Step 2: Create an identical MusicTable table in US East (N. Virginia)
aws dynamodb update-table --table-name MusicTable --cli-input-json \
'{
  "ReplicaUpdates":
  [
    {
      "Create": {
        "RegionName": "us-east-1"
      }
    }
  ]
}' \
--region us-east-2

# Step 3: Create a table in Europe (Ireland)
aws dynamodb update-table --table-name MusicTable --cli-input-json \
'{
  "ReplicaUpdates":
  [
    {
      "Create": {
        "RegionName": "eu-west-1"
      }
    }
  ]
}' \
--region us-east-2
```
マルチリージョンテーブルについて説明します。  

```
# Step 4: View the list of replicas created using describe-table
aws dynamodb describe-table \
    --table-name MusicTable \
    --region us-east-2 \
    --query 'Table.{TableName:TableName,TableStatus:TableStatus,MultiRegionConsistency:MultiRegionConsistency,Replicas:Replicas[*].{Region:RegionName,Status:ReplicaStatus}}'
```
レプリカテーブルに項目を配置します。  

```
# Step 5: To verify that replication is working, add a new item to the Music table in US East (Ohio)
aws dynamodb put-item \
    --table-name MusicTable \
    --item '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \
    --region us-east-2
```
レプリカテーブルから項目を取得します。  

```
# Step 6: Wait for a few seconds, and then check to see whether the item has been 
# successfully replicated to US East (N. Virginia) and Europe (Ireland)
aws dynamodb get-item \
    --table-name MusicTable \
    --key '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \
    --region us-east-1

aws dynamodb get-item \
    --table-name MusicTable \
    --key '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \
    --region eu-west-1
```
レプリカを削除します。  

```
# Step 7: Delete the replica table in Europe (Ireland) Region
aws dynamodb update-table --table-name MusicTable --cli-input-json \
'{
  "ReplicaUpdates":
  [
    {
      "Delete": {
        "RegionName": "eu-west-1"
      }
    }
  ]
}' \
--region us-east-2

# Delete the replica table in US East (N. Virginia) Region
aws dynamodb update-table --table-name MusicTable --cli-input-json \
'{
  "ReplicaUpdates":
  [
    {
      "Delete": {
        "RegionName": "us-east-1"
      }
    }
  ]
}' \
--region us-east-2
```
テーブルを削除してクリーンアップします。  

```
# Clean up: Delete the primary table
aws dynamodb delete-table --table-name MusicTable --region us-east-2

echo "Global table demonstration complete."
```
+ API の詳細については、*AWS CLI コマンドリファレンス*の以下のトピックを参照してください。
  + [CreateTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/CreateTable)
  + [DeleteTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/DeleteTable)
  + [DescribeTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/DescribeTable)
  + [GetItem](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/GetItem)
  + [PutItem](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/PutItem)
  + [UpdateTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/UpdateTable)

------
#### [ Java ]

次のコード例は、複数のリージョンにまたがるレプリカを含む DynamoDB グローバルテーブルを作成して管理する方法を示しています。
+ グローバルセカンダリインデックスおよび DynamoDB Streams を含むテーブルを作成します。
+ 異なるリージョンにレプリカを追加して、グローバルテーブルを作成します。
+ グローバルテーブルからレプリカを削除します。
+ テスト項目を追加して、リージョン間のレプリケーションを検証します。
+ グローバルテーブル設定とレプリカのステータスについて説明します。

**SDK for Java 2.x**  
AWS SDK for Java 2.x を使用して、グローバルセカンダリインデックスおよび DynamoDB Streams を含むテーブルを作成します。  

```
    public static CreateTableResponse createTableWithGSI(
        final DynamoDbClient dynamoDbClient, final String tableName, final String indexName) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (indexName == null || indexName.trim().isEmpty()) {
            throw new IllegalArgumentException("Index name cannot be null or empty");
        }

        try {
            LOGGER.info("Creating table: " + tableName + " with GSI: " + indexName);

            CreateTableRequest createTableRequest = CreateTableRequest.builder()
                .tableName(tableName)
                .attributeDefinitions(
                    AttributeDefinition.builder()
                        .attributeName("Artist")
                        .attributeType(ScalarAttributeType.S)
                        .build(),
                    AttributeDefinition.builder()
                        .attributeName("SongTitle")
                        .attributeType(ScalarAttributeType.S)
                        .build())
                .keySchema(
                    KeySchemaElement.builder()
                        .attributeName("Artist")
                        .keyType(KeyType.HASH)
                        .build(),
                    KeySchemaElement.builder()
                        .attributeName("SongTitle")
                        .keyType(KeyType.RANGE)
                        .build())
                .billingMode(BillingMode.PAY_PER_REQUEST)
                .globalSecondaryIndexes(GlobalSecondaryIndex.builder()
                    .indexName(indexName)
                    .keySchema(KeySchemaElement.builder()
                        .attributeName("SongTitle")
                        .keyType(KeyType.HASH)
                        .build())
                    .projection(
                        Projection.builder().projectionType(ProjectionType.ALL).build())
                    .build())
                .streamSpecification(StreamSpecification.builder()
                    .streamEnabled(true)
                    .streamViewType(StreamViewType.NEW_AND_OLD_IMAGES)
                    .build())
                .build();

            CreateTableResponse response = dynamoDbClient.createTable(createTableRequest);
            LOGGER.info("Table creation initiated. Status: "
                + response.tableDescription().tableStatus());

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to create table: " + tableName + " - " + e.getMessage());
            throw e;
        }
    }
```
AWS SDK for Java 2.x を使用して、テーブルがアクティブになるまで待ちます。  

```
    public static void waitForTableActive(final DynamoDbClient dynamoDbClient, final String tableName) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }

        try {
            LOGGER.info("Waiting for table to become active: " + tableName);

            try (DynamoDbWaiter waiter =
                DynamoDbWaiter.builder().client(dynamoDbClient).build()) {
                DescribeTableRequest request =
                    DescribeTableRequest.builder().tableName(tableName).build();

                waiter.waitUntilTableExists(request);
                LOGGER.info("Table is now active: " + tableName);
            }

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to wait for table to become active: " + tableName + " - " + e.getMessage());
            throw e;
        }
    }
```
AWS SDK for Java 2.x を使用して、レプリカを追加して、グローバルテーブルを作成または拡張します。  

```
    public static UpdateTableResponse addReplica(
        final DynamoDbClient dynamoDbClient,
        final String tableName,
        final Region replicaRegion,
        final String indexName,
        final Long readCapacity) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (replicaRegion == null) {
            throw new IllegalArgumentException("Replica region cannot be null");
        }
        if (indexName == null || indexName.trim().isEmpty()) {
            throw new IllegalArgumentException("Index name cannot be null or empty");
        }
        if (readCapacity == null || readCapacity <= 0) {
            throw new IllegalArgumentException("Read capacity must be a positive number");
        }

        try {
            LOGGER.info("Adding replica in region: " + replicaRegion.id() + " for table: " + tableName);

            // Create a ReplicationGroupUpdate for adding a replica
            ReplicationGroupUpdate replicationGroupUpdate = ReplicationGroupUpdate.builder()
                .create(builder -> builder.regionName(replicaRegion.id())
                    .globalSecondaryIndexes(ReplicaGlobalSecondaryIndex.builder()
                        .indexName(indexName)
                        .provisionedThroughputOverride(ProvisionedThroughputOverride.builder()
                            .readCapacityUnits(readCapacity)
                            .build())
                        .build())
                    .build())
                .build();

            UpdateTableRequest updateTableRequest = UpdateTableRequest.builder()
                .tableName(tableName)
                .replicaUpdates(replicationGroupUpdate)
                .build();

            UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest);
            LOGGER.info("Replica addition initiated in region: " + replicaRegion.id());

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to add replica in region: " + replicaRegion.id() + " - " + e.getMessage());
            throw e;
        }
    }
```
AWS SDK for Java 2.x を使用してグローバルテーブルからレプリカを削除します。  

```
    public static UpdateTableResponse removeReplica(
        final DynamoDbClient dynamoDbClient, final String tableName, final Region replicaRegion) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (replicaRegion == null) {
            throw new IllegalArgumentException("Replica region cannot be null");
        }

        try {
            LOGGER.info("Removing replica in region: " + replicaRegion.id() + " for table: " + tableName);

            // Create a ReplicationGroupUpdate for removing a replica
            ReplicationGroupUpdate replicationGroupUpdate = ReplicationGroupUpdate.builder()
                .delete(builder -> builder.regionName(replicaRegion.id()).build())
                .build();

            UpdateTableRequest updateTableRequest = UpdateTableRequest.builder()
                .tableName(tableName)
                .replicaUpdates(replicationGroupUpdate)
                .build();

            UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest);
            LOGGER.info("Replica removal initiated in region: " + replicaRegion.id());

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to remove replica in region: " + replicaRegion.id() + " - " + e.getMessage());
            throw e;
        }
    }
```
AWS SDK for Java 2.x を使用してテスト項目を追加して、レプリケーションを検証します。  

```
    public static PutItemResponse putTestItem(
        final DynamoDbClient dynamoDbClient, final String tableName, final String artist, final String songTitle) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (artist == null || artist.trim().isEmpty()) {
            throw new IllegalArgumentException("Artist cannot be null or empty");
        }
        if (songTitle == null || songTitle.trim().isEmpty()) {
            throw new IllegalArgumentException("Song title cannot be null or empty");
        }

        try {
            LOGGER.info("Adding test item to table: " + tableName);

            Map<String, software.amazon.awssdk.services.dynamodb.model.AttributeValue> item = new HashMap<>();
            item.put(
                "Artist",
                software.amazon.awssdk.services.dynamodb.model.AttributeValue.builder()
                    .s(artist)
                    .build());
            item.put(
                "SongTitle",
                software.amazon.awssdk.services.dynamodb.model.AttributeValue.builder()
                    .s(songTitle)
                    .build());

            PutItemRequest putItemRequest =
                PutItemRequest.builder().tableName(tableName).item(item).build();

            PutItemResponse response = dynamoDbClient.putItem(putItemRequest);
            LOGGER.info("Test item added successfully");

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to add test item to table: " + tableName + " - " + e.getMessage());
            throw e;
        }
    }
```
AWS SDK for Java 2.x を使用して、グローバルテーブル設定とレプリカを記述します。  

```
    public static DescribeTableResponse describeTable(final DynamoDbClient dynamoDbClient, final String tableName) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }

        try {
            LOGGER.info("Describing table: " + tableName);

            DescribeTableRequest request =
                DescribeTableRequest.builder().tableName(tableName).build();

            DescribeTableResponse response = dynamoDbClient.describeTable(request);

            LOGGER.info("Table status: " + response.table().tableStatus());
            if (response.table().replicas() != null
                && !response.table().replicas().isEmpty()) {
                LOGGER.info("Number of replicas: " + response.table().replicas().size());
                response.table()
                    .replicas()
                    .forEach(replica -> LOGGER.info(
                        "Replica region: " + replica.regionName() + ", Status: " + replica.replicaStatus()));
            }

            return response;

        } catch (ResourceNotFoundException e) {
            LOGGER.severe("Table not found: " + tableName + " - " + e.getMessage());
            throw e;
        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to describe table: " + tableName + " - " + e.getMessage());
            throw e;
        }
    }
```
AWS SDK for Java 2.x を使用したグローバルテーブル操作の完全な例。  

```
    public static void exampleUsage(final Region sourceRegion, final Region replicaRegion) {

        String tableName = "Music";
        String indexName = "SongTitleIndex";
        Long readCapacity = 15L;

        // Create DynamoDB client for the source region
        try (DynamoDbClient dynamoDbClient =
            DynamoDbClient.builder().region(sourceRegion).build()) {

            try {
                // Step 1: Create the initial table with GSI and streams
                LOGGER.info("Step 1: Creating table in source region: " + sourceRegion.id());
                createTableWithGSI(dynamoDbClient, tableName, indexName);

                // Step 2: Wait for table to become active
                LOGGER.info("Step 2: Waiting for table to become active");
                waitForTableActive(dynamoDbClient, tableName);

                // Step 3: Add replica in destination region
                LOGGER.info("Step 3: Adding replica in region: " + replicaRegion.id());
                addReplica(dynamoDbClient, tableName, replicaRegion, indexName, readCapacity);

                // Step 4: Wait a moment for replica creation to start
                Thread.sleep(5000);

                // Step 5: Describe table to view replica information
                LOGGER.info("Step 5: Describing table to view replicas");
                describeTable(dynamoDbClient, tableName);

                // Step 6: Add a test item to verify replication
                LOGGER.info("Step 6: Adding test item to verify replication");
                putTestItem(dynamoDbClient, tableName, "TestArtist", "TestSong");

                LOGGER.info("Global table setup completed successfully!");
                LOGGER.info("You can verify replication by checking the item in region: " + replicaRegion.id());

                // Step 7: Remove replica and clean up table
                LOGGER.info("Step 7: Removing replica from region: " + replicaRegion.id());
                removeReplica(dynamoDbClient, tableName, replicaRegion);
                DeleteTableResponse deleteTableResponse = dynamoDbClient.deleteTable(
                    DeleteTableRequest.builder().tableName(tableName).build());
                LOGGER.info("MREC global table demonstration completed successfully!");

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Thread was interrupted", e);
            } catch (DynamoDbException e) {
                LOGGER.severe("DynamoDB operation failed: " + e.getMessage());
                throw e;
            }
        }
    }
```
+ API の詳細については、「*AWS SDK for Java 2.x API リファレンス*」の以下のトピックを参照してください。
  + [CreateTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/CreateTable)
  + [DescribeTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/DescribeTable)
  + [PutItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/PutItem)
  + [UpdateTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/UpdateTable)

------

## MRSC 用に設定されたグローバルテーブルの作成
<a name="create-gt-mrsc"></a>

このセクションでは、マルチリージョンの強力な整合性 (MRSC) グローバルテーブルを作成する方法について説明します。MRSC グローバルテーブルは、リージョン間で項目の変更を同期的にレプリケートし、レプリカに対する強力な整合性のある読み込みオペレーションが常に項目の最新バージョンを返すようにします。単一リージョンテーブルを MRSC グローバルテーブルに変換する場合は、テーブルが空であることを確認する必要があります。単一リージョンテーブルを既存の項目を持つ MRSC グローバルテーブルに変換することはサポートされていません。変換プロセス中にデータがテーブルに書き込まれないよう確認してください。

MRSC グローバルテーブルは、3 つのレプリカ、または 2 つのレプリカと 1 つの監視で設定できます。MRSC グローバルテーブルを作成するときに、レプリカとオプションの監視がデプロイされるリージョンを選択します。次の例では、米国東部 (バージニア北部) および米国東部 (オハイオ) リージョンにレプリカを持つ MRSC グローバルテーブルを作成し、米国西部 (オレゴン) リージョンに監視を配置します。

**注記**  
グローバルテーブルを作成する前に、Service Quota のスループット制限がすべてのターゲットリージョンで一貫していることを確認してください。これは、グローバルテーブルの作成に必要です。グローバルテーブルのスループット制限の詳細については、「[グローバルテーブルのクォータ](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ServiceQuotas.html#gt-limits-throughput)」を参照してください。

### DynamoDB コンソールを使用した MRSC グローバルテーブルの作成
<a name="mrsc_console"></a>

AWS マネジメントコンソール を使用してグローバルテーブルを作成するには、次の手順に従います。

1. AWS マネジメントコンソール にサインインして DynamoDB コンソール ([https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/)) を開きます。

1. ナビゲーションバーのリージョンセレクターで、MRSC を使用したグローバルテーブルが[サポートされている](V2globaltables_HowItWorks.md#V2globaltables_HowItWorks.consistency-modes)リージョン (**us-east-2** など) を選択します。

1. ナビゲーションペインで、**[Table]** (テーブル) を選択します。

1. **[テーブルの作成]** を選択します。

1. **[テーブルの作成]** ページで、次の操作を行います。

   1. **[テーブル名]** に「**Music**」と入力します。

   1. **[パーティションキー]** に「**Artist**」と入力し、デフォルトの **[文字列]** タイプのままにします。

   1. **[ソートキー]** に「**SongTitle**」と入力し、デフォルトの **[文字列]** タイプのままにします。

   1. 他のデフォルトの設定はそのままにして、**[テーブルの作成]** を選択します

      この新しいテーブルは、新しいグローバルテーブルの最初のレプリカテーブルとして機能します。これは、後で追加する他のレプリカテーブルのプロトタイプです。

1. テーブルがアクティブになるまで待ってから、テーブルリストから選択します。

1. **[グローバルテーブル]** タブを選択して、**[レプリカを作成]** を選択します。

1. **[レプリカの作成]** ページで、次の操作を行います。

   1. **[マルチリージョン整合性]** で、**[強力な整合性]** を選択します。

   1. **[レプリケーションリージョン 1]** で、「**US East (N. Virginia) us-east-1**」を選択します。

   1. **[レプリケーションリージョン 2]** で、「**US West (Oregon) us-west-2**」を選択します。

   1. 米国西部 (オレゴン) リージョンの **[監視として設定する]** を確認します。

   1. **[レプリカの作成]** を選択します。

1. レプリカと監視の作成プロセスが完了するまで待ちます。テーブルが使用可能になると、レプリカのステータスは **[アクティブ]** と表示されます。

### AWS CLI または Java を使用した MRSC グローバルテーブルの作成
<a name="mrsc-cli-java"></a>

開始する前に、監視リージョンを持つ MRSC グローバルテーブルを作成するために必要なアクセス許可が IAM プリンシパルにあることを確認してください。

次のサンプル IAM ポリシーでは、米国東部 (オハイオ) に DynamoDB テーブル (`MusicTable`) を作成し、米国東部 (バージニア北部) にレプリカ、米国西部 (オレゴン) に監視リージョンを作成できます。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:CreateTable",
                "dynamodb:CreateTableReplica",
                "dynamodb:CreateGlobalTableWitness",
                "dynamodb:DescribeTable",
                "dynamodb:UpdateTable",
                "dynamodb:DeleteTable",
                "dynamodb:DeleteTableReplica",
                "dynamodb:DeleteGlobalTableWitness",
                "dynamodb:Scan",
                "dynamodb:Query",
                "dynamodb:UpdateItem",
                "dynamodb:PutItem",
                "dynamodb:GetItem",
                "dynamodb:DeleteItem",
                "dynamodb:BatchWriteItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:us-east-1:123456789012:table/MusicTable",
                "arn:aws:dynamodb:us-east-2:123456789012:table/MusicTable",
                "arn:aws:dynamodb:us-west-2:123456789012:table/MusicTable"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "iam:CreateServiceLinkedRole",
            "Resource": "arn:aws:iam::*:role/aws-service-role/replication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBReplication",
            "Condition": {
                "StringLike": {
                    "iam:AWSServiceName": "replication.dynamodb.amazonaws.com"
                }
            }
        }
    ]
}
```

------

次のコード例は、マルチリージョンの強力な整合性 (MRSC) を備えた DynamoDB グローバルテーブルを作成および管理する方法を示しています。
+ マルチリージョンの強力な整合性を備えたテーブルを作成します。
+ MRSC 設定とレプリカのステータスを確認します。
+ 即時読み取りでリージョン間の強力な整合性をテストします。
+ MRSC 保証を使用して条件付き書き込みを実行します。
+ MRSC グローバルテーブルリソースをクリーンアップします。

------
#### [ Bash ]

**Bash スクリプトを使用した AWS CLI**  
マルチリージョンの強力な整合性を備えたテーブルを作成します。  

```
# Step 1: Create a new table in us-east-2 (primary region for MRSC)
# Note: Table must be empty when enabling MRSC
aws dynamodb create-table \
    --table-name MusicTable \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --billing-mode PAY_PER_REQUEST \
    --region us-east-2

# Wait for table to become active
aws dynamodb wait table-exists --table-name MusicTable --region us-east-2

# Step 2: Add replica and witness with Multi-Region Strong Consistency
# MRSC requires exactly three replicas in supported regions
aws dynamodb update-table \
    --table-name MusicTable \
    --replica-updates '[{"Create": {"RegionName": "us-east-1"}}]' \
    --global-table-witness-updates '[{"Create": {"RegionName": "us-west-2"}}]' \
    --multi-region-consistency STRONG \
    --region us-east-2
```
MRSC 設定とレプリカのステータスを確認します。  

```
# Verify the global table configuration and MRSC setting
aws dynamodb describe-table \
    --table-name MusicTable \
    --region us-east-2 \
    --query 'Table.{TableName:TableName,TableStatus:TableStatus,MultiRegionConsistency:MultiRegionConsistency,Replicas:Replicas[*],GlobalTableWitnesses:GlobalTableWitnesses[*].{Region:RegionName,Status:ReplicaStatus}}'
```
リージョン間の即時読み取りの強力な整合性をテストします。  

```
# Write an item to the primary region
aws dynamodb put-item \
    --table-name MusicTable \
    --item '{"Artist": {"S":"The Beatles"},"SongTitle": {"S":"Hey Jude"},"Album": {"S":"The Beatles 1967-1970"},"Year": {"N":"1968"}}' \
    --region us-east-2

# Read the item from replica region to verify strong consistency (cannot read or write to witness)
# No wait time needed - MRSC provides immediate consistency
echo "Reading from us-east-1 (immediate consistency):"
aws dynamodb get-item \
    --table-name MusicTable \
    --key '{"Artist": {"S":"The Beatles"},"SongTitle": {"S":"Hey Jude"}}' \
    --consistent-read \
    --region us-east-1
```
MRSC 保証を使用して条件付き書き込みを実行します。  

```
# Perform a conditional update from a different region
# This demonstrates that conditions work consistently across all regions
aws dynamodb update-item \
    --table-name MusicTable \
    --key '{"Artist": {"S":"The Beatles"},"SongTitle": {"S":"Hey Jude"}}' \
    --update-expression "SET #rating = :rating" \
    --condition-expression "attribute_exists(Artist)" \
    --expression-attribute-names '{"#rating": "Rating"}' \
    --expression-attribute-values '{":rating": {"N":"5"}}' \
    --region us-east-1
```
MRSC グローバルテーブルリソースをクリーンアップします。  

```
# Remove replica tables (must be done before deleting the primary table)
aws dynamodb update-table \
    --table-name MusicTable \
    --replica-updates '[{"Delete": {"RegionName": "us-east-1"}}]' \
    --global-table-witness-updates '[{"Delete": {"RegionName": "us-west-2"}}]' \
    --region us-east-2

# Wait for replicas to be deleted
echo "Waiting for replicas to be deleted..."
sleep 30

# Delete the primary table
aws dynamodb delete-table \
    --table-name MusicTable \
    --region us-east-2
```
+ API の詳細については、*AWS CLI コマンドリファレンス*の以下のトピックを参照してください。
  + [CreateTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/CreateTable)
  + [DeleteTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/DeleteTable)
  + [DescribeTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/DescribeTable)
  + [GetItem](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/GetItem)
  + [PutItem](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/PutItem)
  + [UpdateItem](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/UpdateItem)
  + [UpdateTable](https://docs.aws.amazon.com/goto/aws-cli/dynamodb-2012-08-10/UpdateTable)

------
#### [ Java ]

**SDK for Java 2.x**  
MRSC 変換の準備ができたリージョンテーブルを AWS SDK for Java 2.x を使用して作成します。  

```
    public static CreateTableResponse createRegionalTable(final DynamoDbClient dynamoDbClient, final String tableName) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }

        try {
            LOGGER.info("Creating regional table: " + tableName + " (must be empty for MRSC)");

            CreateTableRequest createTableRequest = CreateTableRequest.builder()
                .tableName(tableName)
                .attributeDefinitions(
                    AttributeDefinition.builder()
                        .attributeName("Artist")
                        .attributeType(ScalarAttributeType.S)
                        .build(),
                    AttributeDefinition.builder()
                        .attributeName("SongTitle")
                        .attributeType(ScalarAttributeType.S)
                        .build())
                .keySchema(
                    KeySchemaElement.builder()
                        .attributeName("Artist")
                        .keyType(KeyType.HASH)
                        .build(),
                    KeySchemaElement.builder()
                        .attributeName("SongTitle")
                        .keyType(KeyType.RANGE)
                        .build())
                .billingMode(BillingMode.PAY_PER_REQUEST)
                .build();

            CreateTableResponse response = dynamoDbClient.createTable(createTableRequest);
            LOGGER.info("Regional table creation initiated. Status: "
                + response.tableDescription().tableStatus());

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to create regional table: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to create regional table: " + tableName)
                .cause(e)
                .build();
        }
    }
```
AWS SDK for Java 2.x を使用して、リージョンテーブルをレプリカと監視を備えた MRSC に変換します。  

```
    public static UpdateTableResponse convertToMRSCWithWitness(
        final DynamoDbClient dynamoDbClient,
        final String tableName,
        final Region replicaRegion,
        final Region witnessRegion) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (replicaRegion == null) {
            throw new IllegalArgumentException("Replica region cannot be null");
        }
        if (witnessRegion == null) {
            throw new IllegalArgumentException("Witness region cannot be null");
        }

        try {
            LOGGER.info("Converting table to MRSC with replica in " + replicaRegion.id() + " and witness in "
                + witnessRegion.id());

            // Create replica update using ReplicationGroupUpdate
            ReplicationGroupUpdate replicaUpdate = ReplicationGroupUpdate.builder()
                .create(CreateReplicationGroupMemberAction.builder()
                    .regionName(replicaRegion.id())
                    .build())
                .build();

            // Create witness update
            GlobalTableWitnessGroupUpdate witnessUpdate = GlobalTableWitnessGroupUpdate.builder()
                .create(CreateGlobalTableWitnessGroupMemberAction.builder()
                    .regionName(witnessRegion.id())
                    .build())
                .build();

            UpdateTableRequest updateTableRequest = UpdateTableRequest.builder()
                .tableName(tableName)
                .replicaUpdates(List.of(replicaUpdate))
                .globalTableWitnessUpdates(List.of(witnessUpdate))
                .multiRegionConsistency(MultiRegionConsistency.STRONG)
                .build();

            UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest);
            LOGGER.info("MRSC conversion initiated. Status: "
                + response.tableDescription().tableStatus());
            LOGGER.info("UpdateTableResponse full object: " + response);
            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to convert table to MRSC: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to convert table to MRSC: " + tableName)
                .cause(e)
                .build();
        }
    }
```
AWS SDK for Java 2.x を使用して、MRSC のグローバルテーブル設定を記述します。  

```
    public static DescribeTableResponse describeMRSCTable(final DynamoDbClient dynamoDbClient, final String tableName) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }

        try {
            LOGGER.info("Describing MRSC global table: " + tableName);

            DescribeTableRequest request =
                DescribeTableRequest.builder().tableName(tableName).build();

            DescribeTableResponse response = dynamoDbClient.describeTable(request);

            LOGGER.info("Table status: " + response.table().tableStatus());
            LOGGER.info("Multi-region consistency: " + response.table().multiRegionConsistency());

            if (response.table().replicas() != null
                && !response.table().replicas().isEmpty()) {
                LOGGER.info("Number of replicas: " + response.table().replicas().size());
                response.table()
                    .replicas()
                    .forEach(replica -> LOGGER.info(
                        "Replica region: " + replica.regionName() + ", Status: " + replica.replicaStatus()));
            }

            if (response.table().globalTableWitnesses() != null
                && !response.table().globalTableWitnesses().isEmpty()) {
                LOGGER.info("Number of witnesses: "
                    + response.table().globalTableWitnesses().size());
                response.table()
                    .globalTableWitnesses()
                    .forEach(witness -> LOGGER.info(
                        "Witness region: " + witness.regionName() + ", Status: " + witness.witnessStatus()));
            }

            return response;

        } catch (ResourceNotFoundException e) {
            LOGGER.severe("Table not found: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Table not found: " + tableName)
                .cause(e)
                .build();
        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to describe table: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to describe table: " + tableName)
                .cause(e)
                .build();
        }
    }
```
AWS SDK for Java 2.x を使用して、MRSC の強力な整合性を検証するためのテスト項目を追加します。  

```
    public static PutItemResponse putTestItem(
        final DynamoDbClient dynamoDbClient,
        final String tableName,
        final String artist,
        final String songTitle,
        final String album,
        final String year) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (artist == null || artist.trim().isEmpty()) {
            throw new IllegalArgumentException("Artist cannot be null or empty");
        }
        if (songTitle == null || songTitle.trim().isEmpty()) {
            throw new IllegalArgumentException("Song title cannot be null or empty");
        }

        try {
            LOGGER.info("Adding test item to MRSC global table: " + tableName);

            Map<String, AttributeValue> item = new HashMap<>();
            item.put("Artist", AttributeValue.builder().s(artist).build());
            item.put("SongTitle", AttributeValue.builder().s(songTitle).build());

            if (album != null && !album.trim().isEmpty()) {
                item.put("Album", AttributeValue.builder().s(album).build());
            }
            if (year != null && !year.trim().isEmpty()) {
                item.put("Year", AttributeValue.builder().n(year).build());
            }

            PutItemRequest putItemRequest =
                PutItemRequest.builder().tableName(tableName).item(item).build();

            PutItemResponse response = dynamoDbClient.putItem(putItemRequest);
            LOGGER.info("Test item added successfully with strong consistency");

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to add test item to table: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to add test item to table: " + tableName)
                .cause(e)
                .build();
        }
    }
```
AWS SDK for Java 2.x を使用して、MRSC レプリカから一貫した読み取りを持つ項目を読み取ります。  

```
    public static GetItemResponse getItemWithConsistentRead(
        final DynamoDbClient dynamoDbClient, final String tableName, final String artist, final String songTitle) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (artist == null || artist.trim().isEmpty()) {
            throw new IllegalArgumentException("Artist cannot be null or empty");
        }
        if (songTitle == null || songTitle.trim().isEmpty()) {
            throw new IllegalArgumentException("Song title cannot be null or empty");
        }

        try {
            LOGGER.info("Reading item from MRSC global table with consistent read: " + tableName);

            Map<String, AttributeValue> key = new HashMap<>();
            key.put("Artist", AttributeValue.builder().s(artist).build());
            key.put("SongTitle", AttributeValue.builder().s(songTitle).build());

            GetItemRequest getItemRequest = GetItemRequest.builder()
                .tableName(tableName)
                .key(key)
                .consistentRead(true)
                .build();

            GetItemResponse response = dynamoDbClient.getItem(getItemRequest);

            if (response.hasItem()) {
                LOGGER.info("Item found with strong consistency - no wait time needed");
            } else {
                LOGGER.info("Item not found");
            }

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to read item from table: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to read item from table: " + tableName)
                .cause(e)
                .build();
        }
    }
```
AWS SDK for Java 2.x を使用して、MRSC 保証を使用する条件付きの更新を実行します。  

```
    public static UpdateItemResponse performConditionalUpdate(
        final DynamoDbClient dynamoDbClient,
        final String tableName,
        final String artist,
        final String songTitle,
        final String rating) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (artist == null || artist.trim().isEmpty()) {
            throw new IllegalArgumentException("Artist cannot be null or empty");
        }
        if (songTitle == null || songTitle.trim().isEmpty()) {
            throw new IllegalArgumentException("Song title cannot be null or empty");
        }
        if (rating == null || rating.trim().isEmpty()) {
            throw new IllegalArgumentException("Rating cannot be null or empty");
        }

        try {
            LOGGER.info("Performing conditional update on MRSC global table: " + tableName);

            Map<String, AttributeValue> key = new HashMap<>();
            key.put("Artist", AttributeValue.builder().s(artist).build());
            key.put("SongTitle", AttributeValue.builder().s(songTitle).build());

            Map<String, String> expressionAttributeNames = new HashMap<>();
            expressionAttributeNames.put("#rating", "Rating");

            Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
            expressionAttributeValues.put(
                ":rating", AttributeValue.builder().n(rating).build());

            UpdateItemRequest updateItemRequest = UpdateItemRequest.builder()
                .tableName(tableName)
                .key(key)
                .updateExpression("SET #rating = :rating")
                .conditionExpression("attribute_exists(Artist)")
                .expressionAttributeNames(expressionAttributeNames)
                .expressionAttributeValues(expressionAttributeValues)
                .build();

            UpdateItemResponse response = dynamoDbClient.updateItem(updateItemRequest);
            LOGGER.info("Conditional update successful - demonstrates strong consistency");

            return response;

        } catch (ConditionalCheckFailedException e) {
            LOGGER.warning("Conditional check failed: " + e.getMessage());
            throw e;
        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to perform conditional update: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to perform conditional update: " + tableName)
                .cause(e)
                .build();
        }
    }
```
AWS SDK for Java 2.x を使用して、MRSC レプリカと監視がアクティブになるまで待ちます。  

```
    public static void waitForMRSCReplicasActive(
        final DynamoDbClient dynamoDbClient, final String tableName, final int maxWaitTimeSeconds)
        throws InterruptedException {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (maxWaitTimeSeconds <= 0) {
            throw new IllegalArgumentException("Max wait time must be positive");
        }

        try {
            LOGGER.info("Waiting for MRSC replicas and witnesses to become active: " + tableName);

            final long startTime = System.currentTimeMillis();
            final long maxWaitTimeMillis = maxWaitTimeSeconds * 1000L;
            int backoffSeconds = 5; // Start with 5 second intervals
            final int maxBackoffSeconds = 30; // Cap at 30 seconds

            while (System.currentTimeMillis() - startTime < maxWaitTimeMillis) {
                DescribeTableResponse response = describeMRSCTable(dynamoDbClient, tableName);

                boolean allActive = true;
                StringBuilder statusReport = new StringBuilder();

                if (response.table().multiRegionConsistency() == null
                    || !MultiRegionConsistency.STRONG
                        .toString()
                        .equals(response.table().multiRegionConsistency().toString())) {
                    allActive = false;
                    statusReport
                        .append("MultiRegionConsistency: ")
                        .append(response.table().multiRegionConsistency())
                        .append(" ");
                }
                if (response.table().replicas() == null
                    || response.table().replicas().isEmpty()) {
                    allActive = false;
                    statusReport.append("No replicas found. ");
                }
                if (response.table().globalTableWitnesses() == null
                    || response.table().globalTableWitnesses().isEmpty()) {
                    allActive = false;
                    statusReport.append("No witnesses found. ");
                }

                // Check table status
                if (!"ACTIVE".equals(response.table().tableStatus().toString())) {
                    allActive = false;
                    statusReport
                        .append("Table: ")
                        .append(response.table().tableStatus())
                        .append(" ");
                }

                // Check replica status
                if (response.table().replicas() != null) {
                    for (var replica : response.table().replicas()) {
                        if (!"ACTIVE".equals(replica.replicaStatus().toString())) {
                            allActive = false;
                            statusReport
                                .append("Replica(")
                                .append(replica.regionName())
                                .append("): ")
                                .append(replica.replicaStatus())
                                .append(" ");
                        }
                    }
                }

                // Check witness status
                if (response.table().globalTableWitnesses() != null) {
                    for (var witness : response.table().globalTableWitnesses()) {
                        if (!"ACTIVE".equals(witness.witnessStatus().toString())) {
                            allActive = false;
                            statusReport
                                .append("Witness(")
                                .append(witness.regionName())
                                .append("): ")
                                .append(witness.witnessStatus())
                                .append(" ");
                        }
                    }
                }

                if (allActive) {
                    LOGGER.info("All MRSC replicas and witnesses are now active: " + tableName);
                    return;
                }

                LOGGER.info("Waiting for MRSC components to become active. Status: " + statusReport.toString());
                LOGGER.info("Next check in " + backoffSeconds + " seconds...");

                tempWait(backoffSeconds);

                // Exponential backoff with cap
                backoffSeconds = Math.min(backoffSeconds * 2, maxBackoffSeconds);
            }

            throw DynamoDbException.builder()
                .message("Timeout waiting for MRSC replicas to become active after " + maxWaitTimeSeconds + " seconds")
                .build();

        } catch (DynamoDbException | InterruptedException e) {
            LOGGER.severe("Failed to wait for MRSC replicas to become active: " + tableName + " - " + e.getMessage());
            throw e;
        }
    }
```
AWS SDK for Java 2.x を使用して、MRSC レプリカと監視をクリーンアップします。  

```
    public static UpdateTableResponse cleanupMRSCReplicas(
        final DynamoDbClient dynamoDbClient,
        final String tableName,
        final Region replicaRegion,
        final Region witnessRegion) {

        if (dynamoDbClient == null) {
            throw new IllegalArgumentException("DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (replicaRegion == null) {
            throw new IllegalArgumentException("Replica region cannot be null");
        }
        if (witnessRegion == null) {
            throw new IllegalArgumentException("Witness region cannot be null");
        }

        try {
            LOGGER.info("Cleaning up MRSC replicas and witnesses for table: " + tableName);

            // Remove replica using ReplicationGroupUpdate
            ReplicationGroupUpdate replicaUpdate = ReplicationGroupUpdate.builder()
                .delete(DeleteReplicationGroupMemberAction.builder()
                    .regionName(replicaRegion.id())
                    .build())
                .build();

            // Remove witness
            GlobalTableWitnessGroupUpdate witnessUpdate = GlobalTableWitnessGroupUpdate.builder()
                .delete(DeleteGlobalTableWitnessGroupMemberAction.builder()
                    .regionName(witnessRegion.id())
                    .build())
                .build();

            UpdateTableRequest updateTableRequest = UpdateTableRequest.builder()
                .tableName(tableName)
                .replicaUpdates(List.of(replicaUpdate))
                .globalTableWitnessUpdates(List.of(witnessUpdate))
                .build();

            UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest);
            LOGGER.info("MRSC cleanup initiated - removing replica and witness. Response: " + response);

            return response;

        } catch (DynamoDbException e) {
            LOGGER.severe("Failed to cleanup MRSC replicas: " + tableName + " - " + e.getMessage());
            throw DynamoDbException.builder()
                .message("Failed to cleanup MRSC replicas: " + tableName)
                .cause(e)
                .build();
        }
    }
```
AWS SDK for Java 2.x を使用して、MRSC ワークフローのデモンストレーションを完了します。  

```
    public static void demonstrateCompleteMRSCWorkflow(
        final DynamoDbClient primaryClient,
        final DynamoDbClient replicaClient,
        final String tableName,
        final Region replicaRegion,
        final Region witnessRegion)
        throws InterruptedException {

        if (primaryClient == null) {
            throw new IllegalArgumentException("Primary DynamoDB client cannot be null");
        }
        if (replicaClient == null) {
            throw new IllegalArgumentException("Replica DynamoDB client cannot be null");
        }
        if (tableName == null || tableName.trim().isEmpty()) {
            throw new IllegalArgumentException("Table name cannot be null or empty");
        }
        if (replicaRegion == null) {
            throw new IllegalArgumentException("Replica region cannot be null");
        }
        if (witnessRegion == null) {
            throw new IllegalArgumentException("Witness region cannot be null");
        }

        try {
            LOGGER.info("=== Starting Complete MRSC Workflow Demonstration ===");

            // Step 1: Create an empty single-Region table
            LOGGER.info("Step 1: Creating empty single-Region table");
            createRegionalTable(primaryClient, tableName);

            // Use the existing GlobalTableOperations method for basic table waiting
            LOGGER.info("Intermediate step: Waiting for table [" + tableName + "] to become active before continuing");
            GlobalTableOperations.waitForTableActive(primaryClient, tableName);

            // Step 2: Convert to MRSC with replica and witness
            LOGGER.info("Step 2: Converting to MRSC with replica and witness");
            convertToMRSCWithWitness(primaryClient, tableName, replicaRegion, witnessRegion);

            // Wait for MRSC conversion to complete using MRSC-specific waiter
            LOGGER.info("Waiting for MRSC conversion to complete...");
            waitForMRSCReplicasActive(primaryClient, tableName);

            LOGGER.info("Intermediate step: Waiting for table [" + tableName + "] to become active before continuing");
            GlobalTableOperations.waitForTableActive(primaryClient, tableName);

            // Step 3: Verify MRSC configuration
            LOGGER.info("Step 3: Verifying MRSC configuration");
            describeMRSCTable(primaryClient, tableName);

            // Step 4: Test strong consistency with data operations
            LOGGER.info("Step 4: Testing strong consistency with data operations");

            // Add test item to primary region
            putTestItem(primaryClient, tableName, "The Beatles", "Hey Jude", "The Beatles 1967-1970", "1968");

            // Immediately read from replica region (no wait needed with MRSC)
            LOGGER.info("Reading from replica region immediately (strong consistency):");
            GetItemResponse getResponse =
                getItemWithConsistentRead(replicaClient, tableName, "The Beatles", "Hey Jude");

            if (getResponse.hasItem()) {
                LOGGER.info("✓ Strong consistency verified - item immediately available in replica region");
            } else {
                LOGGER.warning("✗ Item not found in replica region");
            }

            // Test conditional update from replica region
            LOGGER.info("Testing conditional update from replica region:");
            performConditionalUpdate(replicaClient, tableName, "The Beatles", "Hey Jude", "5");
            LOGGER.info("✓ Conditional update successful - demonstrates strong consistency");

            // Step 5: Cleanup
            LOGGER.info("Step 5: Cleaning up resources");
            cleanupMRSCReplicas(primaryClient, tableName, replicaRegion, witnessRegion);

            // Wait for cleanup to complete using basic table waiter
            LOGGER.info("Waiting for replica cleanup to complete...");
            GlobalTableOperations.waitForTableActive(primaryClient, tableName);

            // "Halt" until replica/witness cleanup is complete
            DescribeTableResponse cleanupVerification = describeMRSCTable(primaryClient, tableName);
            int backoffSeconds = 5; // Start with 5 second intervals
            while (cleanupVerification.table().multiRegionConsistency() != null) {
                LOGGER.info("Waiting additional time (" + backoffSeconds + " seconds) for MRSC cleanup to complete...");
                tempWait(backoffSeconds);

                // Exponential backoff with cap
                backoffSeconds = Math.min(backoffSeconds * 2, 30);
                cleanupVerification = describeMRSCTable(primaryClient, tableName);
            }

            // Delete the primary table
            deleteTable(primaryClient, tableName);

            LOGGER.info("=== MRSC Workflow Demonstration Complete ===");
            LOGGER.info("");
            LOGGER.info("Key benefits of Multi-Region Strong Consistency (MRSC):");
            LOGGER.info("- Immediate consistency across all regions (no eventual consistency delays)");
            LOGGER.info("- Simplified application logic (no need to handle eventual consistency)");
            LOGGER.info("- Support for conditional writes and transactions across regions");
            LOGGER.info("- Consistent read operations from any region without waiting");

        } catch (DynamoDbException | InterruptedException e) {
            LOGGER.severe("MRSC workflow failed: " + e.getMessage());
            throw e;
        }
    }
```
+ API の詳細についてはAWS SDK for Java 2.x API リファレンスの**以下のトピックを参照してください。
  + [CreateTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/CreateTable)
  + [DeleteTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/DeleteTable)
  + [DescribeTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/DescribeTable)
  + [GetItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/GetItem)
  + [PutItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/PutItem)
  + [UpdateItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/UpdateItem)
  + [UpdateTable](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/UpdateTable)

------

# DynamoDB グローバルテーブルのセキュリティ
<a name="globaltables-security"></a>

グローバルテーブルレプリカは DynamoDB テーブルであるため、AWS Identity and Access Management (IAM) ID ポリシーやリソースベースのポリシーなど、単一リージョンテーブルに対して行うレプリカへのアクセスを制御するのと同じ方法を使用します。

このトピックでは、IAM アクセス許可と AWS Key Management Service (AWS KMS) 暗号化を使用して DynamoDB グローバルテーブルを保護する方法について説明します。クロスリージョンレプリケーションと自動スケーリングを許可するサービスリンクロール (SLR)、グローバルテーブル の作成、更新、削除に必要な IAM アクセス許可、マルチリージョンの最終的な一貫性 (MREC) テーブルとマルチリージョンの強力な整合性 (MRSC) テーブルの違いについて説明します。また、クロスリージョンレプリケーションを安全に管理するための AWS KMS 暗号化キーについても説明します。

## グローバルテーブルのサービスリンクロール
<a name="globaltables-slr"></a>

DynamoDB グローバルテーブルは、クロスリージョンレプリケーションと自動スケーリング機能を管理するために、サービスリンクロール (SLR) に依存しています。

これらのロールは、AWS アカウントごとに 1 回だけ設定する必要があります。作成後、同じロールがアカウント内のすべてのグローバルテーブルに機能します。サービスリンクロールの詳細については、「*IAM ユーザーガイド*」の「[サービスリンクロールの使用](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html)」を参照してください。

### レプリケーションサービスにリンクされたロール
<a name="globaltables-replication-slr"></a>

最初のグローバルテーブルを作成するとき、Amazon DynamoDB によって、`AWSServiceRoleForDynamoDBReplication` サービスリンクロール (SLR) が自動的に作成されます。このロールは、クロスリージョンレプリケーションを管理します。

リソースベースのポリシーをレプリカに適用する際は、`AWSServiceRoleForDynamoDBReplicationPolicy` で定義されている SLR プリンシパルへのアクセス許可を拒否しないでください。拒否すると、レプリケーションが中断されます。必要な SLR アクセス許可を拒否すると、影響を受けるレプリカとの間のレプリケーションは停止し、レプリカテーブルのステータスは「`REPLICATION_NOT_AUTHORIZED`」に変わります。
+ マルチリージョンの結果整合性 (MREC) のグローバルテーブルについて、レプリカが 20 時間以上「`REPLICATION_NOT_AUTHORIZED`」ステータスのままである場合、レプリカは 1 つのリージョンの DynamoDB テーブルに変換され、元に戻せなくなります。
+ マルチリージョンの強力な整合性 (MRSC) のグローバルテーブルの場合、必要なアクセス許可を拒否すると、書き込みおよび強力な整合性のある読み込みオペレーションが `AccessDeniedException` になります。レプリカが 7 日以上「`REPLICATION_NOT_AUTHORIZED`」ステータスのままである場合、レプリカは永続的にアクセスできなくなり、書き込みオペレーションと強力な整合性のある読み込みオペレーションはエラーで失敗し続けます。レプリカの削除などの一部の管理オペレーションは成功します。

### 自動スケーリングサービスにリンクされたロール
<a name="globaltables-autoscaling-slr"></a>

プロビジョンドキャパシティモードのグローバルテーブルを設定する場合は、グローバルテーブルに自動スケーリングを設定する必要があります。DynamoDB 自動スケーリングは、AWS Application Auto Scaling Service を使用して、グローバルテーブルレプリカのプロビジョニングされたスループットキャパシティを動的に調整します。Application Auto Scaling サービスは、[https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html](https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html) という名前のサービスにリンクされたロール (SLR) を作成します。このサービスにリンクされたロールは、DynamoDB テーブルの自動スケーリングを初めて設定するときに、AWS アカウントで自動的に作成されます。これにより、Application Auto Scaling はプロビジョニングされたテーブル容量を管理でき、CloudWatch アラームを作成できます。

 リソースベースのポリシーをレプリカに適用する際は、[https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSApplicationAutoscalingDynamoDBTablePolicy.html](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSApplicationAutoscalingDynamoDBTablePolicy.html) で定義されている Application Auto Scaling SLR プリンシパルへのアクセス許可を拒否しないでください。拒否すると、自動スケーリング機能が中断されます。

### サービスにリンクされたロールの IAM ポリシーの例
<a name="globaltables-example-slr"></a>

以下の条件の IAM ポリシーは、DynamoDB レプリケーション SLR および AWS 自動スケーリング SLR に必要なアクセス許可には影響しません。この条件は、レプリケーションや自動スケーリングが意図せずに中断されないように、制限の広いポリシーに追加できます。

#### 拒否ポリシーから必要な SLR アクセス許可を除外する
<a name="example-exclude-slr-policy"></a>

次の例は、サービスにリンクされたロールプリンシパルを拒否ステートメントから除外する方法を示しています。

```
"Condition": {
    "StringNotEquals": {
        "aws:PrincipalArn": [
            "arn:aws::iam::111122223333:role/aws-service-role/replication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBReplication",
            "arn:aws::iam::111122223333:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable"
        ]
    }
}
```

## グローバルテーブルで AWS IAM を使用する方法
<a name="globaltables-iam"></a>

次のセクションでは、さまざまなグローバルテーブルのオペレーションに必要なアクセス許可について説明し、ユーザーとアプリケーションに適切なアクセスを設定するのに役立つポリシーの例を示します。

**注記**  
説明されているすべてのアクセス許可は、影響を受けるリージョンの特定のテーブルリソース ARN に適用する必要があります。テーブルリソース ARN は `arn:aws:dynamodb:region:account-id:table/table-name` の形式に従います。ここでは、実際のリージョン、アカウント ID、テーブル名の値を指定する必要があります。

**Topics**
+ [グローバルテーブルの作成とレプリカの追加](#globaltables-creation-iam)
+ [グローバルテーブルの更新](#globaltables-update-iam)
+ [グローバルテーブルとレプリカの削除](#globaltables-delete-iam)

### グローバルテーブルの作成とレプリカの追加
<a name="globaltables-creation-iam"></a>

DynamoDB グローバルテーブルは、マルチリージョンの結果整合性 (MREC) およびマルチリージョンの強力な整合性 (MRSC) の 2 つの整合性モードをサポートしています。MREC グローバルテーブルは、任意の数のリージョンにまたがって複数のレプリカを持つことができ、結果整合性を提供します。MRSC グローバルテーブルは、3 つのリージョン (3 つのレプリカまたは 2 つのレプリカと 1 つの監視) のみを必要とし、目標復旧時点 (RPO) との強力な整合性を提供します。

グローバルテーブルの作成に必要なアクセス許可は、監視の有無にかかわらず、グローバルテーブルを作成するかどうかによって異なります。

#### グローバルテーブルを作成するためのアクセス許可
<a name="globaltables-creation-iam-all-types"></a>

次のアクセス許可は、最初のグローバルテーブルの作成と、後でレプリカを追加するために必要です。これらのアクセス許可は、マルチリージョンの結果整合性 (MREC) とマルチリージョンの強力な整合性 (MRSC) の両方のグローバルテーブルに適用されます。
+ グローバルテーブルには、DynamoDB が [`AWSServiceRoleForDynamoDBReplication`](#globaltables-replication-slr) サービスリンクロール (SLR) を通じて管理するクロスリージョンレプリケーションが必要です。次のアクセス許可により、DynamoDB はグローバルテーブルを初めて作成するときに、このロールを自動的に作成できます。
  + `iam:CreateServiceLinkedRole`
+ [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html) API を使用してグローバルテーブルを作成するか、レプリカを追加するには、ソーステーブルリソースに対して次のアクセス許可が必要です。
  + `dynamodb:UpdateTable`
+ レプリカを追加するには、そのリージョンのテーブルリソースに対して次のアクセス許可が必要です。
  + `dynamodb:CreateTable`
  + `dynamodb:CreateTableReplica`
  + `dynamodb:Query`
  + `dynamodb:Scan`
  + `dynamodb:UpdateItem`
  + `dynamodb:PutItem`
  + `dynamodb:GetItem`
  + `dynamodb:DeleteItem`
  + `dynamodb:BatchWriteItem`

#### 監視を使用した MRSC グローバルテーブルの追加アクセス許可
<a name="globaltables-creation-iam-witness"></a>

監視リージョンを使用してマルチリージョンの強力な整合性 (MRSC) グローバルテーブルを作成する場合、すべての参加リージョン (レプリカリージョンと監視リージョンの両方を含む) のテーブルリソースに対して次のアクセス許可が必要です。
+ `dynamodb:CreateGlobalTableWitness`

#### グローバルテーブルの作成のための IAM ポリシーの例
<a name="globaltables-creation-iam-example"></a>

##### 3 つのリージョンにわたる MREC または MRSC グローバルテーブルの作成
<a name="globaltables-creation-iam-example-three-regions"></a>

次のアイデンティティベースのポリシーでは、必要な DynamoDB レプリケーションのサービスリンクロールの作成など、3 つのリージョンにまたがって「users」という名前の MREC または MRSC グローバルテーブルを作成できます。

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Sid": "AllowCreatingUsersGlobalTable",
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateTable",
        "dynamodb:CreateTableReplica",
        "dynamodb:UpdateTable",
        "dynamodb:Query",
        "dynamodb:Scan",
        "dynamodb:UpdateItem",
        "dynamodb:PutItem",
        "dynamodb:GetItem",
        "dynamodb:DeleteItem",
        "dynamodb:BatchWriteItem"
      ],
      "Resource": [
        "arn:aws:dynamodb:us-east-1:123456789012:table/users",
        "arn:aws:dynamodb:us-east-2:123456789012:table/users",
        "arn:aws:dynamodb:us-west-2:123456789012:table/users"
      ]
    },
    {
      "Sid": "AllowCreatingSLR",
      "Effect": "Allow",
      "Action": [
        "iam:CreateServiceLinkedRole"
      ],
      "Resource": [
        "arn:aws:iam::123456789012:role/aws-service-role/replication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBReplication"
      ]
    }
  ]
}
```

------

##### MREC または MRSC グローバルテーブルの作成を特定のリージョンに制限する
<a name="globaltables-creation-iam-example-restrict-regions"></a>

次のアイデンティティベースのポリシーでは、必要な DynamoDB レプリケーションサービスリンクロールの作成など、[aws:RequestedRegion](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-requestedregion) 条件キーを使用して、特定のリージョン間で DynamoDB グローバルテーブルのレプリカを作成できます。

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Sid": "AllowAddingReplicasToSourceTable",
      "Effect": "Allow",
      "Action": [
        "dynamodb:UpdateTable"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": [
            "us-east-1"
          ]
        }
      }
    },
    {
      "Sid": "AllowCreatingReplicas",
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateTable",
        "dynamodb:CreateTableReplica",
        "dynamodb:UpdateTable",
        "dynamodb:Query",
        "dynamodb:Scan",
        "dynamodb:UpdateItem",
        "dynamodb:PutItem",
        "dynamodb:GetItem",
        "dynamodb:DeleteItem",
        "dynamodb:BatchWriteItem"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": [
            "us-east-2",
            "us-west-2"
          ]
        }
      }
    },
    {
      "Sid": "AllowCreatingSLR",
      "Effect": "Allow",
      "Action": [
        "iam:CreateServiceLinkedRole"
      ],
      "Resource": [
        "arn:aws:iam::123456789012:role/aws-service-role/replication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBReplication"
      ]
    }
  ]
}
```

------

##### 監視付き MRSC グローバルテーブルの作成
<a name="globaltables-creation-iam-example-witness"></a>

次のアイデンティティベースのポリシーでは、us-east-1 と us-east-2 のレプリカと us-west-2 の監視のある「users」という名前の DynamoDB MRSC グローバルテーブルを作成できます。これには、必要な DynamoDB レプリケーションサービスリンクロールの作成が含まれます。

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Sid": "AllowCreatingUsersGlobalTableWithWitness",
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateTable",
        "dynamodb:CreateTableReplica",
        "dynamodb:CreateGlobalTableWitness",
        "dynamodb:UpdateTable",
        "dynamodb:Query",
        "dynamodb:Scan",
        "dynamodb:UpdateItem",
        "dynamodb:PutItem",
        "dynamodb:GetItem",
        "dynamodb:DeleteItem",
        "dynamodb:BatchWriteItem"
      ],
      "Resource": [
        "arn:aws:dynamodb:us-east-1:123456789012:table/users",
        "arn:aws:dynamodb:us-east-2:123456789012:table/users"
      ]
    },
    {
      "Sid": "AllowCreatingSLR",
      "Effect": "Allow",
      "Action": [
        "iam:CreateServiceLinkedRole"
      ],
      "Resource": [
        "arn:aws:iam::123456789012:role/aws-service-role/replication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBReplication"
      ]
    }
  ]
}
```

------

##### MRSC 監視の作成を特定のリージョンに制限する
<a name="globaltables-creation-iam-example-restrict-witness-regions"></a>

このアイデンティティベースのポリシーでは、必要な DynamoDB レプリケーションサービスリンクロールの作成など、[aws:RequestedRegion](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-requestedregion) 条件キーとすべてのリージョン間で制限されていない監視の作成を使用して、特定のリージョン間で MRSC グローバルテーブルとレプリカを作成できます。

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Sid": "AllowCreatingReplicas",
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateTable",
        "dynamodb:CreateTableReplica",
        "dynamodb:UpdateTable",
        "dynamodb:Query",
        "dynamodb:Scan",
        "dynamodb:UpdateItem",
        "dynamodb:PutItem",
        "dynamodb:GetItem",
        "dynamodb:DeleteItem",
        "dynamodb:BatchWriteItem"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": [
            "us-east-1",
            "us-east-2"
          ]
        }
      }
    },
    {
      "Sid": "AllowCreatingWitness",
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateGlobalTableWitness"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowCreatingSLR",
      "Effect": "Allow",
      "Action": [
        "iam:CreateServiceLinkedRole"
      ],
      "Resource": [
        "arn:aws:iam::123456789012:role/aws-service-role/replication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBReplication"
      ]
    }
  ]
}
```

------

### グローバルテーブルの更新
<a name="globaltables-update-iam"></a>

[https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html) API を使用して既存のグローバルテーブルのレプリカ設定を変更するには、API コールを行うリージョンのテーブルリソースに対して次のアクセス許可が必要です。
+ `dynamodb:UpdateTable`

自動スケーリングポリシーや有効期限設定など、他のグローバルテーブル設定も更新できます。これらの追加の更新オペレーションには、次のアクセス許可が必要です。
+ [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTableReplicaAutoScaling.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTableReplicaAutoScaling.html) API を使用してレプリカの自動スケーリングポリシーを更新するには、レプリカを含むすべてのリージョンのテーブルリソースに対して、次のアクセス許可が必要です。
  + `application-autoscaling:DeleteScalingPolicy`
  + `application-autoscaling:DeleteScheduledAction`
  + `application-autoscaling:DeregisterScalableTarget`
  + `application-autoscaling:DescribeScalableTargets`
  + `application-autoscaling:DescribeScalingActivities`
  + `application-autoscaling:DescribeScalingPolicies`
  + `application-autoscaling:DescribeScheduledActions`
  + `application-autoscaling:PutScalingPolicy`
  + `application-autoscaling:PutScheduledAction`
  + `application-autoscaling:RegisterScalableTarget`
+ [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTimeToLive.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTimeToLive.html) API を使用して有効期限設定を更新するには、レプリカを含むすべてのリージョンのテーブルリソースに対して次のアクセス許可が必要です。
  + `dynamodb:UpdateTimeToLive`

  有効期限 (TTL) は、マルチリージョンの結果整合性 (MREC) で設定されたグローバルテーブルでのみサポートされることに注意してください。グローバルテーブルの仕組みに関する詳細については、「[DynamoDB グローバルテーブルの仕組み](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/V2globaltables_HowItWorks.html)」を参照してください。

### グローバルテーブルとレプリカの削除
<a name="globaltables-delete-iam"></a>

グローバルテーブルを削除するには、すべてのレプリカを削除する必要があります。このオペレーションに必要なアクセス許可は、監視リージョンの有無にかかわらず、グローバルテーブルを削除するかどうかによって異なります。

#### グローバルテーブルとレプリカを削除するアクセス許可
<a name="globaltables-delete-iam-all-types"></a>

個々のレプリカを削除したり、グローバルテーブルを完全に削除したりするには、次のアクセス許可が必要です。グローバルテーブル設定を削除すると、異なるリージョンのテーブル間のレプリケーション関係のみが削除されます。最後に残っているリージョンの基盤となる DynamoDB テーブルは削除されません。最後のリージョンのテーブルは、同じデータと設定を持つ標準の DynamoDB テーブルとして引き続き存在し続けます。これらのアクセス許可は、マルチリージョンの結果整合性 (MREC) とマルチリージョンの強力な整合性 (MRSC) の両方のグローバルテーブルに適用されます。
+ [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html) API を使用して既存のグローバルテーブルからレプリカを削除するには、API コールを行う元のリージョンのテーブルリソースに対して次のアクセス許可が必要です。
  + `dynamodb:UpdateTable`
+ レプリカを削除する各リージョンのテーブルリソースには、次のアクセス許可が必要です。
  + `dynamodb:DeleteTable`
  + `dynamodb:DeleteTableReplica`

#### 監視を使用した MRSC グローバルテーブルの追加アクセス許可
<a name="globaltables-delete-iam-witness"></a>

監視を使用するマルチリージョンの強力な整合性 (MRSC) グローバルテーブルを削除する場合、すべての参加リージョン (レプリカリージョンと監視リージョンの両方を含む) のテーブルリソースに対して次のアクセス許可が必要です。
+ `dynamodb:DeleteGlobalTableWitness`

#### グローバルテーブルレプリカを削除する IAM ポリシーの例
<a name="globaltables-delete-iam-example"></a>

##### グローバルテーブルレプリカの削除
<a name="globaltables-delete-replicas-iam-example"></a>

このアイデンティティベースのポリシーでは、「users」という名前の DynamoDB グローバルテーブルとそのレプリカを 3 つのリージョン間で削除できます。

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:UpdateTable",
        "dynamodb:DeleteTable",
        "dynamodb:DeleteTableReplica"
      ],
      "Resource": [
        "arn:aws:dynamodb:us-east-1:123456789012:table/users",
        "arn:aws:dynamodb:us-east-2:123456789012:table/users",
        "arn:aws:dynamodb:us-west-2:123456789012:table/users"
      ]
    }
  ]
}
```

------

##### 監視を使用する MRSC グローバルテーブルの削除
<a name="globaltables-delete-witness-iam-example"></a>

このアイデンティティベースのポリシーでは、「users」という名前の MRSC グローバルテーブルのレプリカと監視を削除できます。

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:UpdateTable",
        "dynamodb:DeleteTable",
        "dynamodb:DeleteTableReplica",
        "dynamodb:DeleteGlobalTableWitness"
      ],
      "Resource": [
        "arn:aws:dynamodb:us-east-1:123456789012:table/users",
        "arn:aws:dynamodb:us-east-2:123456789012:table/users"
      ]
    }
  ]
}
```

------

## グローバルテーブルで AWS KMS を使用する方法
<a name="globaltables-kms"></a>

すべての DynamoDB テーブルと同様に、グローバルテーブルレプリカは常に AWS Key Management Service (AWS KMS) に保存されている暗号化キーを使用して保管中のデータを暗号化します。

グローバルテーブル内のすべてのレプリカは、同じタイプの KMS キー (AWS 所有キー、AWS マネージドキー、またはカスタマーマネージドキー) で設定する必要があります。

**重要**  
DynamoDB では、レプリカを削除するには、レプリカの暗号化キーにアクセスする必要があります。レプリカの暗号化に使用されるカスタマーマネージドキーを無効化または削除する場合、まずレプリカを削除し、残りのレプリカのいずれかのテーブルステータスが「`ACTIVE`」に変わるのを待ってから、キーを無効化または削除する必要があります。

マルチリージョンの結果整合性 (MREC) 用に設定されたグローバルテーブルの場合、レプリカの暗号化に使用されるカスタマーマネージドキーへの DynamoDB のアクセスを無効化または取り消すと、レプリカとの間のレプリケーションは停止し、レプリカのステータスは「`INACCESSIBLE_ENCRYPTION_CREDENTIALS`」に変わります。MREC グローバルテーブルのレプリカが 20 時間以上「`INACCESSIBLE_ENCRYPTION_CREDENTIALS`」ステータスのままである場合、レプリカは単一リージョンの DynamoDB テーブルに変換され、元に戻せなくなります。

マルチリージョンの強力な整合性 (MRSC) 用に設定されたグローバルテーブルの場合、レプリカの暗号化に使用されるカスタマーマネージドキーへの DynamoDB のアクセスを無効化または取り消すと、レプリカとの間のレプリケーションは停止し、レプリカへの書き込みまたは強力な整合性のある読み込みを実行しようとするとエラーが返され、レプリカのステータスは「`INACCESSIBLE_ENCRYPTION_CREDENTIALS`」に変わります。MRSC グローバルテーブルのレプリカが 7 日以上「`INACCESSIBLE_ENCRYPTION_CREDENTIALS`」ステータスのままである場合、取り消された特定のアクセス許可に応じて、レプリカはアーカイブされるか、永続的にアクセスできなくなります。

# DynamoDB マルチアカウントグローバルテーブル
<a name="globaltables-MultiAccount"></a>

マルチアカウントグローバルテーブルは、DynamoDB テーブルデータを複数の AWS リージョンと複数の AWS アカウントに自動的にレプリケートし、耐障害性を向上させ、アカウントレベルでワークロードを分離し、個別のセキュリティとガバナンスのコントロールを適用します。各レプリカテーブルは個別の AWS アカウントに存在するため、リージョンレベルとアカウントレベルの両方で障害分離できます。レプリカを AWS 組織構造に合わせて配置することもできます。マルチアカウントグローバルテーブルでは、同一アカウントグローバルテーブルと比較して、分離性、ガバナンス、およびセキュリティ上の利点がさらに高まります。

マルチアカウントグローバルテーブルには以下の利点があります。
+ 選択した AWS アカウントとリージョン間で DynamoDB テーブルデータを自動的にレプリケートします
+ 異なるポリシー、ガードレール、コンプライアンスの境界を持つアカウント間でデータをレプリケートすることで、セキュリティとガバナンスを強化します
+ レプリカを別々の AWS アカウントに配置することで、運用の耐障害性とアカウントレベルの障害分離を改善します
+ マルチアカウント戦略を使用する場合、ビジネスユニットまたは所有権ごとにワークロードを調整します
+ 各レプリカをそれぞれの AWS アカウントに請求することで、コスト帰属を簡素化します

詳細については、「[複数の AWS アカウントを使用するベネフィット](https://docs.aws.amazon.com/whitepapers/latest/organizing-your-aws-environment/benefits-of-using-multiple-aws-accounts.html)」を参照してください。ワークロードにマルチアカウントレプリケーションが必要ない場合、またはローカルオーバーライドによるレプリカ管理を簡素化したい場合は、引き続き同じアカウントのグローバルテーブルを使用できます。

[マルチリージョンの結果整合性 (MREC)](V2globaltables_HowItWorks.md#V2globaltables_HowItWorks.consistency-modes.mrec) を使用してマルチアカウントグローバルテーブルを設定できます。[マルチリージョンの強力な整合性 (MRSC)](V2globaltables_HowItWorks.md#V2globaltables_HowItWorks.consistency-modes.mrsc) 用に設定されたグローバルテーブルは、マルチアカウントモデルをサポートしていません。

**Topics**
+ [DynamoDB グローバルテーブルの仕組み](V2globaltables_MA_HowItWorks.md)
+ [チュートリアル: マルチアカウントグローバルテーブルの作成](V2globaltables_MA.tutorial.md)
+ [DynamoDB グローバルテーブルのセキュリティ](globaltables_MA_security.md)

# DynamoDB グローバルテーブルの仕組み
<a name="V2globaltables_MA_HowItWorks"></a>

マルチアカウントグローバルテーブルは、DynamoDB グローバルテーブルのフルマネージド、サーバーレス、マルチリージョン、マルチアクティブ機能を複数の AWS アカウントに拡張します。マルチアカウントグローバルテーブルは、AWS リージョンとアカウント間でデータをレプリケートし、同一アカウントグローバルテーブルと同じアクティブ/アクティブ機能を提供します。いずれかのレプリカに書き込むと、DynamoDB は他のすべてのレプリカにデータをレプリケートします。

同一アカウントグローバルテーブルとの主な違いは次のとおりです。
+ マルチアカウントレプリケーションは、マルチリージョンの結果整合性 (MREC) グローバルテーブルでサポートされています。
+ レプリカを追加できるのは、単一リージョンテーブルから始める場合のみです。既存の同一アカウントグローバルテーブルをマルチアカウント設定に変換することはサポートされていません。移行するには、新しいマルチアカウントグローバルテーブルを作成する前に、既存のレプリカを削除して単一リージョンテーブルに戻す必要があります。
+ 各レプリカは個別の AWS アカウントに存在する必要があります。*N* 個のレプリカを持つマルチアカウントグローバルテーブルの場合、*N* 個のアカウントが必要です。
+ マルチアカウントグローバルテーブルは、デフォルトですべてのレプリカにわたって統合されたテーブル設定を使用します。すべてのレプリカは自動的に同じ設定 (スループットモード、TTL など) を共有しますが、同一アカウントグローバルテーブルとは異なり、これらの設定はレプリカごとに上書きすることはできません。
+ 顧客は、リソースポリシーで DynamoDB グローバルテーブルサービスプリンシパルにレプリケーション許可を提供する必要があります。

マルチアカウントグローバルテーブルは、同一アカウントグローバルテーブルと同じ基盤となるレプリケーションテクノロジーを使用します。テーブル設定はすべてのリージョンレプリカに自動的にレプリケートされ、顧客はレプリカごとに設定を上書きまたはカスタマイズすることはできません。これにより、同じグローバルテーブルに参加している複数の AWS アカウント間で一貫した設定と予測可能な動作が保証されます。

DynamoDB グローバルテーブルの設定は、テーブルの動作と、リージョン間でのデータのレプリケート方法を定義します。これらの設定は、テーブルの作成時または新しいリージョンレプリカの追加時に DynamoDB コントロールプレーン API を介して設定されます。

マルチアカウントグローバルテーブルを作成する場合、顧客はリージョンレプリカごとに `GlobalTableSettingsReplicationMode = ENABLED` を設定する必要があります。これにより、1 つのリージョンで行われた設定変更は、グローバルテーブルに参加する他のすべてのリージョンに自動的に伝播されます。

テーブルの作成後に設定のレプリケーションを有効にできます。これは、テーブルがもともとリージョンテーブルとして作成され、後でマルチアカウントグローバルテーブルにアップグレードされるシナリオをサポートします。

**同期される設定**

次のテーブル設定は、マルチアカウントグローバルテーブル内のすべてのレプリカ間で常に同期されます。

**注記**  
同一アカウントグローバルテーブルとは異なり、マルチアカウントグローバルテーブルでは、これらの設定をリージョンごとに上書きすることはできません。唯一の例外は、読み取り自動スケーリングポリシー (テーブルと GSI) は個別の外部リソースであるため、上書きが許可されることです。
+ キャパシティモード (プロビジョンドキャパシティまたはオンデマンド)
+ テーブルのプロビジョニングされた読み込みおよび書き込みキャパシティ
+ テーブルの読み込みおよび書き込みの自動スケーリング
+ ローカルセカンダリインデックス (LSI) 定義
+ グローバルセカンダリインデックス (GSI) 定義
+ GSI プロビジョニングされた読み込みおよび書き込みキャパシティ
+ GSI の読み込みおよび書き込みの自動スケーリング
+ MREC モードでの Streams 定義
+ 有効期限 (TTL)
+ ウォームスループット
+ オンデマンドの最大読み取りおよび書き込みスループット

**同期されない設定**

以下の設定はレプリカ間で同期されないため、リージョンごとにレプリカテーブルごとに個別に設定する必要があります。
+ テーブルクラス
+ サーバー側の暗号化 (SSE)
+ ポイントインタイムリカバリ
+ サーバー側の暗号化 (SSE) KMS キー ID
+ 削除保護
+ Kinesis Data Streams (KDSD)
+ タグ
+ リソースポリシー
+ テーブル Cloudwatch-Contributor Insights (CCI)
+ GSI Cloudwatch-Contributor Insights (CCI)

## モニタリング
<a name="V2globaltables_MA_HowItWorks.monitoring"></a>

マルチリージョンの結果整合性 (MREC) 用に設定されたグローバルテーブルは [`ReplicationLatency`](metrics-dimensions.md#ReplicationLatency) メトリクスを CloudWatch に発行します。このメトリクスは、項目がレプリカテーブルに書き込まれてから、その項目がグローバルテーブルの別のレプリカに表示されるまでの経過時間を追跡します。`ReplicationLatency` はミリ秒単位で表し、グローバルテーブル内のすべての送信元と送信先のリージョンペアに対して出力されます。

一般的な `ReplicationLatency` 値は、選択した AWS リージョン間の距離と、ワークロードタイプやスループットなどの他の変数によって異なります。例えば、米国西部 (北カリフォルニア) (us-west-1) リージョンのソースレプリカは、アフリカ (ケープタウン) (af-south-1) リージョンと比較して、米国西部 (オレゴン) (us-west-2) リージョンへの `ReplicationLatency` が低くなります。

`ReplicationLatency` の値が上昇している場合、1 つのレプリカからの更新が他のレプリカテーブルにタイムリーに伝播されていないことを示している可能性があります。この場合、アプリケーションの読み込みおよび書き込みアクティビティを別の AWS リージョンに一時的にリダイレクトすることができます。

**マルチアカウントグローバルテーブルでのレプリケーションレイテンシーの問題の処理**

レプリカテーブルで顧客に起因する問題が原因で `ReplicationLatency` が 3 時間を超える場合、DynamoDB は顧客に根本的な問題に対処するように求める通知を送信します。レプリケーションを妨げる可能性がある一般的な顧客に起因する問題には、次のようなものがあります。
+ レプリカテーブルのリソースポリシーから必要なアクセス許可を削除する
+ マルチアカウントグローバルテーブルのレプリカをホストする AWS リージョンをオプトアウトする
+ データの復号に必要なテーブルの AWS KMS キーアクセス許可を拒否する

DynamoDB は、レプリケーションレイテンシーが増加してから 3 時間以内に最初の通知を送信し、問題が解決されない場合は 20 時間後に 2 回目の通知を送信します。必要な時間枠内に問題が修正されない場合、DynamoDB はグローバルテーブルからレプリカの関連付けを自動的に解除します。その後、影響を受けるレプリカはリージョンテーブルに変換されます。

# チュートリアル: マルチアカウントグローバルテーブルの作成
<a name="V2globaltables_MA.tutorial"></a>

このセクションでは、複数の AWS アカウントにまたがる DynamoDB グローバルテーブルを作成するための段階的な手順について説明します。

## DynamoDB コンソールを使用してマルチアカウントグローバルテーブルを作成する
<a name="create-ma-gt-console"></a>

AWS マネジメントコンソール を使用してマルチアカウントグローバルテーブルを作成するには、次の手順に従います。以下の例では、レプリカテーブルを持つグローバルテーブルを米国に作成します。

1. AWS マネジメントコンソール にサインインして、最初のアカウント (例: *111122223333*) の DynamoDB コンソール ([https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/)) を開きます。

1. この例では、ナビゲーションバーのリージョンセレクターから **[米国東部 (オハイオ)]** を選択します。

1. コンソールの左側のナビゲーションペインで、**[テーブル]** を選択します。

1. **[Create Table]** (テーブルの作成) を選択します。

1. **[テーブルの作成]** ページで、次の操作を行います。

   1. **[テーブル名]** に **MusicTable** と入力します。

   1. **[パーティションキー]** に **Artist** と入力します。

   1. **[ソートキー]** に **SongTitle** と入力します。

   1. 他のデフォルトの設定はそのままにして、**[テーブルの作成]** を選択します。

1. 次のリソースポリシーをテーブルに追加します

------
#### [ JSON ]

****  

   ```
   {
   "Version":"2012-10-17",		 	 	 
   "Statement": [
       {
           "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
           "Effect": "Allow",
           "Action": [
               "dynamodb:ReadDataForReplication",
               "dynamodb:WriteDataForReplication",
               "dynamodb:ReplicateSettings"
           ],
           "Resource": "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable",
           "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
           "Condition": {
               "StringEquals": {
                   "aws:SourceAccount": ["444455556666","111122223333"],
                   "aws:SourceArn": [
                       "arn:aws:dynamodb:us-east-1:444455556666:table/MusicTable",
                       "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable"
                   ]
               }
           }
       },
       {
           "Sid": "AllowTrustedAccountsToJoinThisGlobalTable",
           "Effect": "Allow",
           "Action": [
               "dynamodb:AssociateTableReplica"
           ],
           "Resource": "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable",
           "Principal": {"AWS": ["444455556666"]}
       }
   ]
   }
   ```

------

1. この新しいテーブルは、新しいグローバルテーブルの最初のレプリカテーブルとして機能します。これは、後で追加する他のレプリカテーブルのプロトタイプです。

1. テーブルが **[アクティブ]** になるまで待ちます。新しく作成したテーブルについては、**[グローバルテーブル]** タブから **[設定のレプリケーション]** に移動し、**[有効化]** をクリックします。

1. このアカウント (ここでは *111122223333*) からログアウトします。

1. AWS マネジメントコンソール にサインインし、2 番目のアカウント (例: *444455556666*) の DynamoDB コンソール [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/) を開きます。

1. この例では、ナビゲーションバーのリージョンセレクターから **[米国東部 (バージニア北部)]** を選択します。

1. コンソールは、選択したリージョンに同一名のテーブルが存在しないことを確認します。同一名のテーブルが存在する場合は、そのリージョンで新しいレプリカテーブルを作成する前に既存のテーブルを削除する必要があります。

1. **[テーブルの作成]** の近くのドロップダウンで、**[別のアカウントから作成]** を選択します。

1. **[別のアカウントからテーブルを作成する]** ページで次の操作を行います。

   1. ソーステーブルのテーブル ARN として **arn:aws:dynamodb:us-east-2:*111122223333*:table/MusicTable** を追加します。

   1. **[レプリカテーブル ARN]** で、ソーステーブルの ARN を再度追加します (**arn:aws:dynamodb:us-east-2:*111122223333*:table/MusicTable**)。マルチアカウントグローバルテーブルの一部として複数のレプリカが既に存在する場合は、既存のすべてのレプリカを ReplicaTableARN に追加する必要があります。

   1. 他のデフォルトの設定はそのままにして、**[送信]** を選択します。

1. Music テーブル (およびその他すべてのレプリカテーブル) の **[グローバルテーブル]** タブに、テーブルが複数のリージョンでレプリケートされたことが示されます。

1. レプリケーションをテストするには、次の操作を行います。

   1. このテーブルのレプリカが存在する任意のリージョンを使用できます。

   1. **[テーブルアイテムの探索]** を選択します。

   1. **[項目を作成]** を選択します。

   1. **[アーティスト]** に「**item\$11**」、**[曲名]** に「**Song Value 1**」と入力します。

   1. **[項目を作成]** を選択します。

   1. 他のリージョンに切り替えてレプリケーションを確認します。

   1. [ミュージック] テーブルに、作成した項目が含まれていることを確認します。

## AWS CLI を使用してマルチアカウントグローバルテーブルを作成する
<a name="ma-gt-cli"></a>

次の例は、AWS CLI を使用してマルチアカウントグローバルテーブルを作成する方法を示しています。これらの例は、クロスアカウントレプリケーションを設定するための完全なワークフローを示しています。

------
#### [ CLI ]

クロスアカウントレプリケーションでマルチアカウントグローバルテーブルを作成するには、次の AWS CLI コマンドを使用します。

```
# STEP 1: Setting resource policy for the table in account 111122223333

cat > /tmp/source-resource-policy.json << 'EOF'
{
    "Version": "2012-10-17", 		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": ["444455556666","111122223333"],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:us-east-1:444455556666:table/MusicTable",
                        "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable"
                    ]
                }
            }
        },
        {
            "Sid": "AllowTrustedAccountsToJoinThisGlobalTable",
            "Effect": "Allow",
            "Action": [
                "dynamodb:AssociateTableReplica"
            ],
            "Resource": "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable",
            "Principal": {"AWS": ["444455556666"]}
        }
    ]
}
EOF

# Step 2: Create a new table (MusicTable) in US East (Ohio), 
#   with DynamoDB Streams enabled (NEW_AND_OLD_IMAGES),
#   and Settings Replication ENABLED on the account 111122223333

aws dynamodb create-table \
    --table-name MusicTable \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --billing-mode PAY_PER_REQUEST \
    --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES \
    --global-table-settings-replication-mode ENABLED \
    --resource-policy file:///tmp/source-resource-policy.json \
    --region us-east-2 


# Step 3: Creating replica table in account 444455556666

# Resource policy for account 444455556666
cat > /tmp/dest-resource-policy.json << 'EOF'
{
    "Version": "2012-10-17", 		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:us-east-1:444455556666:table/MusicTable",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": ["444455556666","111122223333"],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:us-east-1:444455556666:table/MusicTable",
                        "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable"
                    ]
                }
            }
        }
    ]
}
EOF

# Execute the replica table creation
aws dynamodb create-table \
    --table-name MusicTable \
    --global-table-source-arn "arn:aws:dynamodb:us-east-2:111122223333:table/MusicTable" \
    --resource-policy file:///tmp/dest-resource-policy.json \
    --global-table-settings-replication-mode ENABLED \
    --region us-east-1

# Step 4: View the list of replicas created using describe-table
aws dynamodb describe-table \
    --table-name MusicTable \
    --region us-east-2 \
    --query 'Table.{TableName:TableName,TableStatus:TableStatus,MultiRegionConsistency:MultiRegionConsistency,Replicas:Replicas[*].{Region:RegionName,Status:ReplicaStatus}}'

# Step 5: To verify that replication is working, add a new item to the Music table in US East (Ohio)
aws dynamodb put-item \
    --table-name MusicTable \
    --item '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \
    --region us-east-2

# Step 6: Wait for a few seconds, and then check to see whether the item has been 
# successfully replicated to US East (N. Virginia) and Europe (Ireland)
aws dynamodb get-item \
    --table-name MusicTable \
    --key '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \
    --region us-east-1

aws dynamodb get-item \
    --table-name MusicTable \
    --key '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \
    --region us-east-2

# Step 7: Delete the replica table in US East (N. Virginia) Region
aws dynamodb delete-table \
    --table-name MusicTable \
    --region us-east-1

# Clean up: Delete the primary table
aws dynamodb delete-table \
    --table-name MusicTable \
    --region us-east-2
```

------

# DynamoDB グローバルテーブルのセキュリティ
<a name="globaltables_MA_security"></a>

グローバルテーブルレプリカは DynamoDB テーブルであるため、AWS Identity and Access Management (IAM) ID ポリシーやリソースベースのポリシーなど、単一リージョンテーブルに対して行うレプリカへのアクセスを制御するのと同じ方法を使用します。このトピックでは、IAM アクセス許可と AWS Key Management Service (AWS KMS) 暗号化を使用して DynamoDB マルチアカウントグローバルテーブルを保護する方法について説明します。クロスリージョンクロスアカウントレプリケーションと自動スケーリングを許可するリソースベースのポリシーとサービスリンクロール (SLR) について、また、マルチリージョンの結果整合性 (MREC) テーブルに対して、グローバルテーブルの作成、更新、削除に必要な IAM アクセス許可について説明します。また、クロスリージョンレプリケーションを安全に管理するための AWS KMS 暗号化キーについても説明します。

クロスアカウントおよびクロスリージョンテーブルレプリケーションを確立するために必要なリソースベースのポリシーとアクセス許可に関する詳細情報を提供します。このセキュリティモデルを理解することは、安全なクロスアカウントデータレプリケーションソリューションを実装する必要があるお客様にとって不可欠です。

## レプリケーションのサービスプリンシパル承認
<a name="globaltables_MA_service_principal"></a>

DynamoDB のマルチアカウントグローバルテーブルは、レプリケーションがアカウントの境界を越えて実行されるため、個別の承認アプローチを使用します。これは、DynamoDB のレプリケーションサービスプリンシパルを使用して行われます (`replication.dynamodb.amazonaws.com`)。各参加アカウントは、レプリカテーブルのリソースポリシーでそのプリンシパルを明示的に許可し、`aws:SourceAccount`、`aws:SourceArn` などのキーのソースコンテキスト条件によって特定のレプリカに制限できるアクセス許可を付与する必要があります。詳細については、「[AWS グローバル条件キー](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html)」を参照してください。アクセス許可は双方向です。つまり、レプリケーションを特定のレプリカのペア間で確立する前に、すべてのレプリカが相互にアクセス許可を明示的に付与する必要があります。

クロスアカウントレプリケーションには、次のサービスプリンシパルのアクセス許可が不可欠です。
+ `dynamodb:ReadDataForReplication` は、レプリケーション目的でデータを読み取る機能を付与します。このアクセス許可により、1 つのレプリカの変更を読み取って他のレプリカに伝播できます。
+ `dynamodb:WriteDataForReplication` は、レプリケート先テーブルへのレプリケートデータの書き込みを許可します。このアクセス許可により、グローバルテーブル内のすべてのレプリカ間で変更を同期できます。
+ `dynamodb:ReplicateSettings` を使用すると、レプリカ間でテーブル設定を同期できるため、すべての参加テーブルで一貫した設定が可能になります。

各レプリカは、上記のアクセス許可を他のすべてのレプリカとそれ自体に付与する必要があります。つまり、ソースコンテキスト条件には、グローバルテーブルを構成するレプリカの完全なセットが含まれている必要があります。これらのアクセス許可は、新しいレプリカがマルチアカウントグローバルテーブルに追加されるたびに検証されます。これにより、レプリケーションオペレーションが承認された DynamoDB サービスによってのみ実行され、目的のテーブル間でのみ実行されることが検証されます。

## マルチアカウントグローバルテーブルのサービスリンクロール
<a name="globaltables_MA_service_linked_roles"></a>

DynamoDB マルチアカウントグローバルテーブルは、すべてのレプリカ間で設定をレプリケートするため、各レプリカは一貫したスループットで同じようにセットアップされ、シームレスなフェイルオーバーエクスペリエンスを提供します。設定のレプリケーションは、サービスプリンシパルに対する `ReplicateSettings` アクセス許可によって制御されますが、特定のクロスアカウントクロスリージョンレプリケーションと自動スケーリング機能の管理には、サービスにリンクされたロール (SLR) も利用します。これらのロールは、AWS アカウントごとに 1 回だけ設定されます。作成後、同じロールがアカウント内のすべてのグローバルテーブルに機能します。サービスリンクロールの詳細については、IAM ユーザーガイドの「[サービスリンクロールの使用](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create-service-linked-role.html)」を参照してください。

### 設定管理サービスにリンクされたロール
<a name="globaltables_MA_settings_management_slr"></a>

Amazon DynamoDB は、アカウントで最初のマルチアカウントグローバルテーブルレプリカを作成すると、AWSServiceRoleForDynamoDBGlobalTableSettingsManagement サービスリンクロール (SLR) を自動的に作成します。このロールは、クロスアカウントクロスリージョンの設定のレプリケーションを管理します。

リソースベースのポリシーをレプリカに適用する場合は、`AWSServiceRoleForDynamoDBGlobalTableSettingsManagement` で定義されている SLR プリンシパルのアクセス許可をいずれも拒否しないことを確認してください。拒否すると、設定管理が妨げられ、レプリカ間または GSI 間でスループットが一致しない場合にレプリケーションに支障をきたす可能性があります。必要な SLR アクセス許可を拒否すると、影響を受けるレプリカとの間のレプリケーションが停止し、レプリカテーブルのステータスは「`REPLICATION_NOT_AUTHORIZED`」に変わります。マルチアカウントグローバルテーブルについて、レプリカが 20 時間以上「`REPLICATION_NOT_AUTHORIZED`」ステータスのままである場合、レプリカは 1 つのリージョンの DynamoDB テーブルに変換され、元に戻せなくなります。SLR には、以下のアクセス許可があります。
+ `application-autoscaling:DeleteScalingPolicy`
+ `application-autoscaling:DescribeScalableTargets`
+ `application-autoscaling:DescribeScalingPolicies`
+ `application-autoscaling:DeregisterScalableTarget`
+ `application-autoscaling:PutScalingPolicy`
+ `application-autoscaling:RegisterScalableTarget`

### 自動スケーリングサービスにリンクされたロール
<a name="globaltables_MA_autoscaling_slr"></a>

プロビジョンドキャパシティモードのグローバルテーブルを設定する場合は、グローバルテーブルに自動スケーリングを設定する必要があります。DynamoDB 自動スケーリングは、AWS Application Auto Scaling Service を使用して、グローバルテーブルレプリカのプロビジョニングされたスループットキャパシティを動的に調整します。Application Auto Scaling サービスは、[AWSServiceRoleForApplicationAutoScaling\$1DynamoDBTable](https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html) という名前のサービスにリンクされたロール (SLR) を作成します。このサービスにリンクされたロールは、DynamoDB テーブルの自動スケーリングを初めて設定するときに、AWS アカウントで自動的に作成されます。これにより、Application Auto Scaling はプロビジョニングされたテーブル容量を管理でき、CloudWatch アラームを作成できます。

リソースベースのポリシーをレプリカに適用する際は、[AWSApplicationAutoscalingDynamoDBTablePolicy](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSApplicationAutoscalingDynamoDBTablePolicy.html) で定義されている Application Auto Scaling SLR プリンシパルへのアクセス許可を拒否していないことを確認してください。拒否すると、自動スケーリング機能が中断されます。

## グローバルテーブルで AWS IAM を使用する方法
<a name="globaltables_MA_iam"></a>

次のセクションでは、さまざまなグローバルテーブルのオペレーションに必要なアクセス許可について説明し、ユーザーとアプリケーションに適切なアクセスを設定するのに役立つポリシーの例を示します。

**注記**  
説明されているすべてのアクセス許可は、影響を受けるリージョンの特定のテーブルリソース ARN に適用する必要があります。テーブルリソース ARN は `arn:aws:dynamodb:region:account-id:table/table-name` の形式に従います。ここでは、実際のリージョン、アカウント ID、テーブル名の値を指定する必要があります。

以下のセクションでは、順を追って以下のトピックを取り上げます。
+ マルチアカウントグローバルテーブルの作成とレプリカの追加
+ マルチアカウントグローバルテーブルの更新
+ グローバルテーブルとレプリカの削除

### グローバルテーブルの作成とレプリカの追加
<a name="globaltables_MA_creating"></a>

#### グローバルテーブルを作成するためのアクセス許可
<a name="globaltables_MA_creating_permissions"></a>

リージョンテーブルに新しいレプリカを追加してマルチアカウントグローバルテーブルを作成する場合、または既存のマルチアカウントグローバルテーブルに追加する場合、アクションを実行する IAM プリンシパルは既存のすべてのメンバーによって承認される必要があります。既存のメンバーはすべて、レプリカを正常に追加するために、テーブルポリシーで次のアクセス許可を付与する必要があります。
+ `dynamodb:AssociateTableReplica` - このアクセス許可により、テーブルをグローバルテーブル設定に結合できます。これは、レプリケーション関係の初期確立を可能にする基本的なアクセス許可です。

この正確なコントロールにより、承認されたアカウントのみがグローバルテーブルのセットアップに参加できるようになります。

#### グローバルテーブルの作成のための IAM ポリシーの例
<a name="globaltables_MA_creating_examples"></a>

##### 2 つのレプリカ設定の IAM ポリシーの例
<a name="globaltables_MA_2replica_example"></a>

マルチアカウントグローバルテーブルのセットアップは、安全なレプリケーションを提供する特定の認可フローに従います。顧客が 2 つのレプリカを持つグローバルテーブルを確立するという実践的なシナリオを順に見ていくことで、これが実際にどのように機能するかを確認してみましょう。最初のレプリカ (ReplicaA) は ap-east-1 リージョンのアカウント A に存在し、2 番目のレプリカ (ReplicaB) は eu-south-1 リージョンのアカウント B にあります。
+ ソースアカウント (アカウント A) では、プロセスはプライマリレプリカテーブルの作成から始まります。アカウント管理者は、関連付けを実行するために必要なアクセス許可を送信先アカウント (アカウント B) に明示的に付与するリソースベースのポリシーをこのテーブルにアタッチする必要があります。このポリシーは、DynamoDB レプリケーションサービスが重要なレプリケーションアクションを実行することも許可します。
+ 送信先アカウント (アカウント B) は、レプリカの作成時に対応するリソースベースのポリシーをアタッチし、レプリカの作成に使用するソーステーブル ARN を参照することで、同様のプロセスに従います。このポリシーは、アカウント A によって付与されたアクセス許可をミラーリングし、信頼された双方向の関係を作成します。レプリケーションを確立する前に、DynamoDB はこれらのクロスアカウントアクセス許可を検証して、適切な承認が設定されていることを確認します。

このセットアップを確立するには。
+ アカウント A の管理者は、まずリソースベースのポリシーを ReplicaA にアタッチする必要があります。このポリシーは、アカウント B と DynamoDB レプリケーションサービスに必要なアクセス許可を明示的に付与します。
+ 同様に、アカウント B の管理者は、一致するポリシーを ReplicaB にアタッチする必要があります。アカウント参照を逆にして、レプリカ A をソーステーブルとして参照するレプリカ B を作成するためのテーブル作成呼び出しで、対応するアクセス許可をアカウント A に付与します。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": [ "111122223333", "444455556666" ],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
                        "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB"
                    ]
                }
            }
        },
        {
            "Sid": "AllowTrustedAccountsToJoinThisGlobalTable",
            "Effect": "Allow",
            "Action": [
                "dynamodb:AssociateTableReplica"
            ],
            "Resource": "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
            "Principal": {"AWS": ["444455556666"]}
        }
    ]
}
```

------

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": [ "111122223333", "444455556666" ],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
                        "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB"
                    ]
                }
            }
        }
    ]
}
```

------

##### 3 つのレプリカ設定の IAM ポリシーの例
<a name="globaltables_MA_3replica_example"></a>

この設定では、アカウント A、アカウント B、アカウント C に、それぞれ 3 つのレプリカ ReplicaA、ReplicaB、および ReplicaC があります。ReplicaA は最初のレプリカであり、リージョンテーブルとして開始され、その後に ReplicaB と ReplicaC が追加されます。
+ アカウント A の管理者は、まず、すべてのメンバーとのレプリケーションを許可し、アカウント B とアカウント C の IAM プリンシパルがレプリカを追加できるように、リソースベースのポリシーを ReplicaA にアタッチする必要があります。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": [ "111122223333", "444455556666", "123456789012" ],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
                        "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB",
                        "arn:aws:dynamodb:us-east-1:123456789012:table/ReplicaC"
                    ]
                }
            }
        },
        {
            "Sid": "AllowTrustedAccountsToJoinThisGlobalTable",
            "Effect": "Allow",
            "Action": [
                "dynamodb:AssociateTableReplica"
            ],
            "Resource": "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
            "Principal": { "AWS": [ "444455556666", "123456789012" ] }
        }
    ]
}
```

------
+ アカウント B の管理者は、ReplicaA をソースとしてポイントするレプリカ (ReplicaB) を追加する必要があります。ReplicaB には、すべてのメンバー間のレプリケーションを許可し、アカウント C にレプリカの追加を許可する次のポリシーがあります。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": [ "111122223333", "444455556666", "123456789012" ],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
                        "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB",
                        "arn:aws:dynamodb:us-east-1:123456789012:table/ReplicaC"
                    ]
                }
            }
        },
        {
            "Sid": "AllowTrustedAccountsToJoinThisGlobalTable",
            "Effect": "Allow",
            "Action": [
                "dynamodb:AssociateTableReplica"
            ],
            "Resource": "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB",
            "Principal": { "AWS": [ "123456789012" ] }
        }
    ]
}
```

------
+ 最後に、アカウント C の管理者は、すべてのメンバー間のレプリケーションアクセス許可を許可する次のポリシーを使用してレプリカを作成します。ポリシーにより、これ以上のレプリカの追加は許可されません。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "DynamoDBActionsNeededForSteadyStateReplication",
            "Effect": "Allow",
            "Action": [
                "dynamodb:ReadDataForReplication",
                "dynamodb:WriteDataForReplication",
                "dynamodb:ReplicateSettings"
            ],
            "Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/ReplicaC",
            "Principal": {"Service": ["replication.dynamodb.amazonaws.com"]},
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": [ "111122223333", "444455556666" ],
                    "aws:SourceArn": [
                        "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
                        "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB"
                    ]
                }
            }
        }
    ]
}
```

------

### マルチアカウントグローバルテーブルの更新
<a name="globaltables_MA_updating"></a>

UpdateTable API を使用して既存のグローバルテーブルのレプリカ設定を変更するには、API コールを行うリージョンのテーブルリソースに対して次のアクセス許可が必要です。`dynamodb:UpdateTable`

自動スケーリングポリシーや有効期限設定など、他のグローバルテーブル設定も更新できます。これらの追加の更新オペレーションには、次のアクセス許可が必要です。

`UpdateTimeToLive` API を使用して有効期限設定を更新するには、レプリカを含むすべてのリージョンのテーブルリソースに対して次のアクセス許可が必要です。`dynamodb:UpdateTimeToLive`

`UpdateTableReplicaAutoScaling` API を使用してレプリカの自動スケーリングポリシーを更新するには、レプリカを含むすべてのリージョンのテーブルリソースに対して、次のアクセス許可が必要です。
+ `application-autoscaling:DeleteScalingPolicy`
+ `application-autoscaling:DeleteScheduledAction`
+ `application-autoscaling:DeregisterScalableTarget`
+ `application-autoscaling:DescribeScalableTargets`
+ `application-autoscaling:DescribeScalingActivities`
+ `application-autoscaling:DescribeScalingPolicies`
+ `application-autoscaling:DescribeScheduledActions`
+ `application-autoscaling:PutScalingPolicy`
+ `application-autoscaling:PutScheduledAction`
+ `application-autoscaling:RegisterScalableTarget`

**注記**  
テーブルの更新を成功させるには、すべてのレプリカリージョンとアカウントで `dynamodb:ReplicateSettings` アクセス許可を付与する必要があります。いずれかのレプリカがマルチアカウントグローバルテーブル内のいずれかのレプリカに設定をレプリケートするアクセス許可を付与していない場合、アクセス許可が修正されるまで、すべてのレプリカのすべての更新オペレーションは `AccessDeniedException` で失敗します。

### グローバルテーブルとレプリカの削除
<a name="globaltables_MA_deleting"></a>

グローバルテーブルを削除するには、すべてのレプリカを削除する必要があります。同一アカウントグローバルテーブルとは異なり、`UpdateTable` を使用してリモートリージョン内のレプリカテーブルを削除することはできません。各レプリカはそのテーブルを制御するアカウントから `DeleteTable` API を介して削除する必要があります。

#### グローバルテーブルとレプリカを削除するアクセス許可
<a name="globaltables_MA_deleting_permissions"></a>

個々のレプリカを削除したり、グローバルテーブルを完全に削除したりするには、次のアクセス許可が必要です。グローバルテーブル設定を削除すると、異なるリージョンのテーブル間のレプリケーション関係のみが削除されます。最後に残っているリージョンの基盤となる DynamoDB テーブルは削除されません。最後のリージョンのテーブルは、同じデータと設定を持つ標準の DynamoDB テーブルとして引き続き存在し続けます。

レプリカを削除する各リージョンのテーブルリソースには、次のアクセス許可が必要です。
+ `dynamodb:DeleteTable`
+ `dynamodb:DeleteTableReplica`

## グローバルテーブルで AWS KMS を使用する方法
<a name="globaltables_MA_kms"></a>

すべての DynamoDB テーブルと同様に、グローバルテーブルレプリカは常に AWS Key Management Service (AWS KMS) に保存されている暗号化キーを使用して保管中のデータを暗号化します。

**注記**  
同一アカウントグローバルテーブルとは異なり、マルチアカウントグローバルテーブル内の異なるレプリカは、異なるタイプの AWS KMS キー (AWS 所有キー、またはカスタマーマネージドキー) を使用して設定できます。マルチアカウントグローバルテーブルは AWS マネージドキーをサポートしていません。

CMK を使用するマルチアカウントグローバルテーブルでは、各レプリカのキーポリシーで、レプリケーションと設定管理のためのキーにアクセスするためのアクセス許可を DynamoDB レプリケーションサービスプリンシパル (`replication.dynamodb.amazonaws.com`) に付与する必要があります。以下のアクセス権限が必要です。
+ `kms:Decrypt`
+ `kms:ReEncrypt*`
+ `kms:GenerateDataKey*`
+ `kms:DescribeKey`

**重要**

DynamoDB では、レプリカを削除するには、レプリカの暗号化キーにアクセスする必要があります。レプリカを削除するため、レプリカの暗号化に使用されるカスタマーマネージドキーを無効化または削除する場合、まずレプリカを削除し、残りのレプリカの 1 つで記述を呼び出してレプリケーショングループからテーブルが削除されるまで待機してから、キーを無効化または削除する必要があります。

レプリカの暗号化に使用されるカスタマーマネージドキーへの DynamoDB のアクセスを無効化または取り消すと、レプリカとの間のレプリケーションは停止し、レプリカのステータスは「`INACCESSIBLE_ENCRYPTION_CREDENTIALS`」に変わります。レプリカが 20 時間以上「`INACCESSIBLE_ENCRYPTION_CREDENTIALS`」ステータスのままである場合、レプリカは単一リージョンの DynamoDB テーブルに変換され、元に戻せなくなります。

### AWS KMS ポリシーの例
<a name="globaltables_MA_kms_example"></a>

この AWS KMS ポリシーにより、DynamoDB はレプリカ A と B 間のレプリケーションのために両方の AWS KMS キーにアクセスできます。各アカウントの DynamoDB レプリカにアタッチされた AWS KMS キーは、次のポリシーで更新する必要があります。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": { "Service": "replication.dynamodb.amazonaws.com" },
        "Action": [
            "kms:Decrypt",
            "kms:ReEncrypt*",
            "kms:GenerateDataKey*",
            "kms:DescribeKey"
        ],
        "Resource": "*",
        "Condition": {
            "StringEquals": {
                "aws:SourceAccount": [ "111122223333", "444455556666" ],
                "aws:SourceArn": [
                    "arn:aws:dynamodb:ap-east-1:111122223333:table/ReplicaA",
                    "arn:aws:dynamodb:eu-south-1:444455556666:table/ReplicaB"
                ]
            }
        }
      }
   ]
 }
```

------

# Amazon DynamoDB のグローバルテーブルの請求について
<a name="global-tables-billing"></a>

このガイドでは、グローバルテーブルに対する DynamoDB の請求の仕組みを説明し、グローバルテーブルのコストに寄与するコンポーネントを実用的な例を含めて特定します。

[Amazon DynamoDB グローバルテーブル](GlobalTables.md)は、完全マネージド型の、サーバーレス、マルチリージョン、マルチアクティブデータベースです。グローバルテーブルは、[99.999% の可用性](https://aws.amazon.com/dynamodb/sla/)を提供するように設計されており、アプリケーションの耐障害性の向上、事業継続性の向上を実現します。グローバルテーブルは、選択した AWS リージョン間で DynamoDB テーブルを自動的にレプリケートするため、ローカルな読み取りおよび書き込みパフォーマンスを高速化できます。

## 仕組み
<a name="global-tables-billing-how-it-works"></a>

グローバルテーブルの請求モデルは、単一リージョンの DynamoDB テーブルとは異なります。単一リージョンの DynamoDB テーブルの書き込みオペレーションは、次の単位を使用して請求されます。
+ オンデマンドキャパシティモードの場合は、書き込みリクエストユニット (WRU)。最大 1 KB の書き込みごとに 1 WRU が課金されます。
+ プロビジョンドキャパシティモードの場合は、書き込みキャパシティユニット (WCU)。1 WCU で、1 秒あたり最大 1 KB の書き込みが 1 回提供されます。

既存の単一リージョンのテーブルにレプリカテーブルを追加してグローバルテーブルを作成すると、その単一リージョンテーブルはレプリカテーブルになります。つまり、テーブルへの書き込みの請求に使用されるユニットも変更されます。レプリカテーブルへの書き込みオペレーションは、次の単位を使用して請求されます。
+ オンデマンドキャパシティモードの場合は、レプリケート書き込みリクエストユニット (rWRU)。最大 1KB の書き込みごとにレプリカテーブルあたり 1 rWRU が課金されます。
+ プロビジョンドキャパシティモードの場合は、レプリケート書き込みキャパシティユニット (rWCU)。レプリカテーブルごとに 1 WCU で、1 秒あたり最大 1 KB の書き込みが 1 回提供されます。

グローバルセカンダリインデックス (GSI) の更新は、GSI のベーステーブルがレプリカテーブルであっても、単一リージョンの DynamoDB テーブルと同じユニットを使用して請求されます。GSI の更新オペレーションは、次の単位を使用して請求されます。
+ オンデマンドキャパシティモードの場合は、書き込みリクエストユニット (WRU)。最大 1 KB の書き込みごとに 1 WRU が課金されます。
+ プロビジョンドキャパシティモードの場合は、書き込みキャパシティユニット (WCU)。1 WCU で、1 秒あたり最大 1 KB の書き込みが 1 回提供されます。

レプリケート書き込みユニット (rWCU および rWRU) の料金は、単一リージョンの書き込みユニット (WCU および WRU) と同じです。データがリージョン間でレプリケートされるため、グローバルテーブルにはリージョン間のデータ転送料金が適用されます。レプリケート書き込み (rWCU または rWRU) の料金は、グローバルテーブルのレプリカテーブルを含むすべてのリージョンで発生します。

単一リージョンテーブルとレプリカテーブルからの読み取りオペレーションでは、次の単位を使用します。
+ オンデマンドキャパシティモードの場合は、読み込みリクエストユニット (RRU)。最大 4 KB の強力な整合性のある読み込みごとに 1 RRU が課金されます。
+ プロビジョニングされたテーブルの場合は、読み込みキャパシティユニット (RCU)。1 RCU で、1 秒あたり最大 4 KB の強力な整合性のある読み込みが 1 回提供されます。

## 整合性モードと請求
<a name="global-tables-billing-consistency-modes"></a>

書き込みオペレーションの請求に使用されるレプリケートされた書き込みユニット (rWCU と rWRU) は、マルチリージョンの強力な整合性 (MRSC) モードとマルチリージョンの結果整合性 (MREC) モードの両方で同一です。監視で設定されたマルチリージョンの強力な整合性 (MRSC) モードを使用するグローバルテーブルでは、レプリケートされた書き込みユニットコスト (rWCU と rWRU)、ストレージコスト、または監視へのレプリケーションのデータ転送コストは発生しません。

## DynamoDB グローバルテーブルの請求例
<a name="global-tables-billing-example"></a>

グローバルテーブル書き込みリクエストの請求の実際の仕組みについては、複数日間のシナリオ例を順を追って説明します (この例では書き込みリクエストのみを考慮します。この例で発生するテーブルの復元料金とクロスリージョンデータ転送料金は含まれていないことに注意してください)。

**1 日目 - 単一リージョンテーブル: **us-west-2 リージョンに Table\$1A という単一リージョンのオンデマンド DynamoDB テーブルがあります。100 個の 1 KB 項目を Table\$1A に書き込みます。単一リージョンの書き込みオペレーションの場合、書き込み 1 KB あたり 1 書き込みリクエストユニット (WRU) が課金されます。1 日目の料金は次のとおりです。
+ us-west-2 リージョンで単一リージョン書き込み用に 100 WRU

1 日目に請求される合計リクエストユニット: **100 WRU**。

**2 日目 - グローバルテーブルの作成: **us-east-2 リージョンに Table\$1A のレプリカを追加してグローバルテーブルを作成します。Table\$1A は、us-west-2 リージョンと us-east-2 リージョンの 2 つのレプリカテーブルを持つグローバルテーブルになりました。us-west-2 リージョンのレプリカテーブルに 150 個の 1 KB 項目を書き込みます。2 日目の料金は次のとおりです。
+ us-west-2 リージョンでレプリケートされた書き込み用に 150 rWRU
+ us-east-2 リージョンでレプリケートされた書き込み用に 150 rWRU

2 日目に請求されるリクエストユニットの合計: **300 rWRU**。

**3 日目 - グローバルセカンダリインデックスの追加: **us-east-2 リージョンのレプリカテーブルに、ベース (レプリカ) テーブルからすべての属性を射影するグローバルセカンダリインデックス (GSI) を追加します。グローバルテーブルは、us-west-2 リージョンのレプリカテーブルに GSI を自動的に作成します。us-west-2 リージョンのレプリカテーブルに 200 個の新しい 1 KB レコードを書き込みます。3 日目の料金は次のとおりです。
+ • us-west-2 リージョンでレプリケートされた書き込み用に 200 rWRU
+ • us-west-2 リージョンで GSI 更新用に 200 WRU
+ • us-east-2 リージョンでレプリケートされた書き込み用に 200 rWRU
+ • us-east-2 リージョンで GSI 更新用に 200 WRU

3 日目に請求される書き込みリクエストユニットの合計: **400 WRU と 400 rWRU**。

3 日間すべての書き込みユニット合計で、500 WRU (1 日目に 100 WRU \$1 3 日目に 400 WRU) と 700 rWRU (2 日目に 300 rWRU \$1 3 日目に 400 rWRU) の料金が発生します。

まとめとして、レプリカテーブルの書き込みオペレーションは、レプリカテーブルを含むすべてのリージョンで、レプリケートされた書き込みユニットとして請求されます。グローバルセカンダリインデックスがある場合、GSI を含むすべてのリージョン (グローバルテーブルではレプリカテーブルを含むすべてのリージョン) で GSI の更新に対して書き込みユニットが課金されます。

# DynamoDB グローバルテーブルのバージョン
<a name="V2globaltables_versions"></a>

DynamoDB グローバルテーブルには、グローバルテーブルバージョン 2019.11.21 (現行) とグローバルテーブルバージョン 2017.11.29 (レガシー) の 2 つのバージョンがあります。グローバルテーブルバージョン 2019.11.21 (現行) を使用することをお勧めします。これは、バージョン 2017.11.29 (レガシー) と比較して、使いやすく、より多くのリージョンでサポートされ、ほとんどのワークロードでコストが低いためです。

## グローバルテーブルのバージョンを確認する
<a name="globaltables.DetermineVersion"></a>

### AWS CLI を使用したバージョンの確認
<a name="globaltables.CLI"></a>

#### バージョン 2019.11.21 (現行) グローバルテーブルレプリカの特定
<a name="globaltables.CLI.current"></a>

テーブルがグローバルテーブルバージョン 2019.11.21 (現行) レプリカかどうかを判断するには、テーブルの `describe-table` コマンドを呼び出します。出力に「2019.11.21」の値を持つ `GlobalTableVersion` 属性が含まれている場合、テーブルはバージョン 2019.11.21 (現行) グローバルテーブルレプリカです。

`describe-table` の CLI コマンドの例:

```
aws dynamodb describe-table \
--table-name users \
--region us-east-2
```

(省略された) 出力には、値が「2019.11.21」の `GlobalTableVersion` 属性が含まれているため、このテーブルはバージョン 2019.11.21 (現行) グローバルテーブルレプリカです。

```
{
    "Table": {
        "AttributeDefinitions": [
            {
                "AttributeName": "id",
                "AttributeType": "S"
            },
            {
                "AttributeName": "name",
                "AttributeType": "S"
            }
        ],
        "TableName": "users",
        ...
        "GlobalTableVersion": "2019.11.21",
        "Replicas": [
            {
                "RegionName": "us-west-2",
                "ReplicaStatus": "ACTIVE",
            }
        ],
        ...
    }
}
```

#### バージョン 2017.11.29 (レガシー) グローバルテーブルレプリカの識別
<a name="globaltables.CLI.legacy"></a>

グローバルテーブルバージョン 2017.11.29 (レガシー) では、グローバルテーブル管理専用のコマンドセットが使用されます。テーブルがグローバルテーブルバージョン 2017.11.29 (レガシー) レプリカであるかどうかを確認するには、テーブルの `describe-global-table` コマンドを呼び出します。正常なレスポンスを受け取った場合、テーブルはバージョン 2017.11.29 (レガシー) グローバルテーブルレプリカです。`describe-global-table` コマンドが `GlobalTableNotFoundException` エラーを返した場合、テーブルはバージョン 2017.11.29 (レガシー) レプリカではありません。

`describe-global-table` の CLI コマンドの例:

```
aws dynamodb describe-global-table \
--table-name users \
--region us-east-2
```

コマンドは正常なレスポンスを返すため、このテーブルはバージョン 2017.11.29 (レガシー) グローバルテーブルレプリカです。

```
{
    "GlobalTableDescription": {
        "ReplicationGroup": [
            {
                "RegionName": "us-west-2"
            },
            {
                "RegionName": "us-east-2"
            }
        ],
        "GlobalTableArn": "arn:aws:dynamodb::123456789012:global-table/users",
        "CreationDateTime": "2025-06-10T13:55:53.630000-04:00",
        "GlobalTableStatus": "ACTIVE",
        "GlobalTableName": "users"
    }
}
```

### DynamoDB コンソールを使用したバージョンの確認
<a name="globaltables.console"></a>

グローバルテーブルレプリカのバージョンを特定するには、以下を実行します。

1. DynamoDB コンソール ([https://console.aws.amazon.com/dynamodb/home](https://console.aws.amazon.com/dynamodb/home)) を開きます。

1. コンソールの左側のナビゲーションペインで、[**テーブル**] を選択します。

1. グローバルテーブルのバージョンを識別するテーブルを選択します。

1. [**グローバルテーブル**] タブを選択します。

   *[概要]* セクションには、使用中のグローバルテーブルのバージョンが表示されます。

## レガシーバージョンと現行バージョンの動作の違い
<a name="DiffLegacyVsCurrent"></a>

以下のリストは、グローバルテーブルのレガシーバージョンと現行バージョンの動作の違いを示しています。
+ いくつかの DynamoDB オペレーションにおいて、バージョン 2019.11.21 (現行) は、バージョン 2017.11.29 (レガシー) よりも書き込みキャパシティの消費が少ないため、ほとんどのユーザーにとってコスト効率が向上します。DynamoDB オペレーションの違いは次のとおりです。
  + あるリージョンの 1KB アイテムに対して [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) を呼び出し、他のリージョンにレプリケートする場合、2017.11.29 (レガシー) ではリージョンごとに 2 rWRU が必要ですが、2019.11.21 (現行) では 1 rWRU のみ必要です。
  + 1KB アイテムに対して [UpdateItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html) を呼び出す場合、2017.11.29 (レガシー) ではソースリージョンで 2 rWRU、送信先リージョンで 1 rWRU が必要ですが、2019.11.21 (現行) ではソースリージョンおよび送信先リージョンでそれぞれ 1 rWRU のみ必要です。
  + 1KB アイテムに対して [DeleteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DeleteItem.html) を呼び出す場合、2017.11.29 (レガシー) ではソースリージョンで 1 rWRU、送信先リージョンで 2 rWRU が必要ですが、2019.11.21 (現行) ではソースリージョンおよび送信先リージョンでそれぞれ 1 rWRU のみ必要です。

  次の表は、2 つのリージョンの 1 KB 項目に対する 2017.11.29 (レガシー) テーブルと 2019.11.21 (現行) テーブルの rWRU 消費量を示しています。    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/V2globaltables_versions.html)
+ バージョン 2017.11.29 (レガシー) は、11 か所の AWS リージョンでのみ利用可能です。一方、バージョン 2019.11.21 (現行) は、すべての AWS リージョンで使用可能です。
+ バージョン 2017.11.29 (レガシー) グローバルテーブルを作成するには、まず空のリージョンテーブルのセットを作成し、次に [CreateGlobalTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateGlobalTable.html) API を呼び出してグローバルテーブルを作成します。バージョン 2019.11.21 (現行) グローバルテーブルを作成するには、[UpdateTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html) API を呼び出して、既存のリージョンテーブルにレプリカを追加します。
+ バージョン 2017.11.29 (レガシー) では、新しいリージョンにレプリカを追加する前 (作成時を含む) に、テーブル内のすべてのレプリカを空にする必要があります。バージョン 2019.11.21 (現行) では、データが既に含まれているリージョンのテーブルに対してレプリカを追加および削除できます。
+ バージョン 2017.11.29 (レガシー) では、以下の専用コントロールプレーン API のセットを使用してレプリカを管理します。
  + [CreateGlobalTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateGlobalTable.html)
  + [DescribeGlobalTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeGlobalTable.html)
  + [DescribeGlobalTableSettings](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeGlobalTableSettings.html)
  + [ListGlobalTables](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ListGlobalTables.html)
  + [UpdateGlobalTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateGlobalTable.html)
  + [UpdateGlobalTableSettings](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateGlobalTableSettings.html)

  バージョン 2019.11.21 (現行) は、[DescribeTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeTable.html) API および [UpdateTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateTable.html) API を使用してレプリカを管理します。
+ バージョン 2017.11.29 (レガシー) は、書き込みごとに 2 つの DynamoDB Streams レコードを発行します。バージョン 2019.11.21 (現行) は、書き込みごとに 1 つの DynamoDB Streams レコードのみを発行します。
+ バージョン 2017.11.29 (レガシー) は、`aws:rep:deleting`、`aws:rep:updateregion`、`aws:rep:updatetime` の各属性を入力および更新します。バージョン 2019.11.21 (現行) は、これらの属性を入力または更新しません。
+ バージョン 2017.11.29 (レガシー) は、レプリカ間で [DynamoDB での Time to Live (TTL) の使用](TTL.md) 設定を同期しません。バージョン 2019.11.21 (現行) は、レプリカ間で TTL 設定を同期します。
+ バージョン 2017.11.29 (レガシー) は、TTL 削除を他のレプリカにレプリケートしません。バージョン 2019.11.21 (現行) は、TTL 削除をすべてのレプリカにレプリケートします。
+ バージョン 2017.11.29 (レガシー) は、レプリカ間で[自動スケーリング](AutoScaling.md)設定を同期しません。バージョン 2019.11.21 (現行) は、レプリカ間で自動スケーリング設定を同期します。
+ バージョン 2017.11.29 (レガシー) は、レプリカ間で[グローバルセカンダリインデックス (GSI)](GSI.md) 設定を同期しません。バージョン 2019.11.21 (現行) は、レプリカ間で GSI 設定を同期します。
+ バージョン 2017.11.29 (レガシー) は、レプリカ間で[保管中の暗号化](encryption.usagenotes.md)設定を同期しません。バージョン 2019.11.21 (現行) は、レプリカ間で保管中の暗号化設定を同期します。
+ バージョン 2017.11.29 (レガシー) は `PendingReplicationCount` メトリクスを発行します。バージョン 2019.11.21 (現行) は同メトリクスを発行しません。

## 現行バージョンへのアップグレード
<a name="upgrading-to-current-version"></a>

### グローバルテーブルのアップグレードに必要なアクセス許可
<a name="V2globaltables_versions.Notes-permissions"></a>

バージョン 2019.11.21 (現行) にアップグレードするには、レプリカがあるすべてのリージョンで `dynamodb:UpdateGlobalTableversion` アクセス許可が必要です。DynamoDB コンソールへのアクセス許可、およびテーブル表示に必要なアクセス許可に加えて、このアクセス許可が必要です。

次の IAM ポリシーは、グローバルテーブルをバージョン 2019.11.21 (現行) にアップグレードするアクセス許可を付与します。

```
{
    "version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "dynamodb:UpdateGlobalTableversion",
            "Resource": "*"
        }
    ]
}
```

次の IAM ポリシーは、2 つのリージョンにレプリカがある `Music` グローバルテーブルのみをバージョン 2019.11.21 (現行) にアップグレードするアクセス許可を付与します。

```
{
    "version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "dynamodb:UpdateGlobalTableversion",
            "Resource": [
                "arn:aws:dynamodb::123456789012:global-table/Music",
                "arn:aws:dynamodb:ap-southeast-1:123456789012:table/Music",
                "arn:aws:dynamodb:us-east-2:123456789012:table/Music"
            ]
        }
    ]
}
```

### アップグレード中の注意点
<a name="V2GlobalTablesUpgradeExpectations"></a>
+ すべてのグローバルテーブルレプリカは、アップグレード中も読み取りと書き込みのトラフィックを処理します。
+ テーブルのサイズとレプリカの数にもよりますが、アップグレードプロセスには、数分から数時間かかります。
+ アップグレードプロセス中、[TableStatus](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TableDescription.html#DDB-Type-TableDescription-TableStatus) の値は `ACTIVE` から `UPDATING` に変わります。テーブルのステータスを表示するには、[DescribeTable](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeTable.html) API を呼び出すか、DynamoDB コンソールの **[テーブル]** ビューを使用します。
+ テーブルのアップグレード中、自動スケーリングはグローバルテーブルのプロビジョニング済のキャパシティ設定を調整しません。アップグレード中は、テーブルを[オンデマンド](capacity-mode.md#capacity-mode-on-demand)キャパシティモードに設定することを強くお勧めします。
+ アップグレード中に自動スケーリングで[プロビジョニングされた](provisioned-capacity-mode.md)容量を使用する場合は、アップグレード中に予想されるトラフィックの増加に対応しスロットリングを回避するために、ポリシーの最小読み取りスループットと最小書き込みスループットを増やす必要があります。
+ `ReplicationLatency` メトリクスは、アップグレードプロセス中にレイテンシーのスパイクを一時的に報告したり、メトリクスデータのレポートを停止したりする場合があります。詳細については、「[ReplicationLatency](metrics-dimensions.md#ReplicationLatency)」を参照してください。
+ アップグレードプロセスが完了すると、テーブルのステータスは `ACTIVE` に変わります。

### アップグレード前、アップグレード中、アップグレード後の DynamoDB Streams の動作
<a name="V2GlobalTablesUpgradeDDBStreamsBehavior"></a>

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/V2globaltables_versions.html)

### バージョン 2019.11.21 (現行) へのアップグレード
<a name="V2globaltables_versions.upgrade"></a>

AWS マネジメントコンソール を使用して DynamoDB グローバルテーブルのバージョンをアップグレードするには、次の手順に従います。

**グローバルテーブルをバージョン 2019.11.21 (現行) にアップグレードするには**

1. DynamoDB コンソール ([https://console.aws.amazon.com/dynamodb/home](https://console.aws.amazon.com/dynamodb/home)) を開きます。

1. コンソールの左側のナビゲーションペインで、**[テーブル]** を選択し、バージョン 2019.11.21 (現行) にアップグレードするグローバルテーブルを選択します。

1. [**グローバルテーブル**] タブを選択します。

1. [**Update version (バージョンの更新)**] を選択します。  
![\[[Update version (バージョンの更新)] ボタンを示すコンソールのスクリーンショット。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/GlobalTables-upgrade.png)

1. 新しい要件を読んで同意してから、[**バージョンを更新**] を選択します。

1. アップグレードプロセスが完了すると、コンソールに表示されるグローバルテーブルのバージョンが **2019.11.21** に変更されます。

# グローバルテーブルのベストプラクティス
<a name="globaltables-bestpractices"></a>

以下のセクションでは、グローバルテーブルをデプロイして使用するためのベストプラクティスについて説明します。

## バージョン
<a name="globaltables-bestpractices-version"></a>

DynamoDB グローバルテーブルには、バージョン 2019.11.21 (現行) と[バージョン 2017.11.29 (レガシー)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/globaltables.V1.html) の 2 つのバージョンがあります。可能な限り、バージョン 2019.11.21 (現行) を使用する必要があります。

## 削除保護
<a name="globaltables-bestpractices-deletionprotection"></a>

誤って削除されないように保護するグローバルテーブルレプリカの削除保護を有効にする必要があります。レプリカごとに削除保護を有効にする必要があります。

## AWS CloudFormationの使用
<a name="globaltables-bestpractices-cloudformation"></a>

CloudFormation は現在、スタック間のグローバルテーブルなどのマルチリージョンリソースの調整をサポートしていません。別のリージョンのスタックでグローバルテーブルの各レプリカを定義すると、レプリカの更新の実行時にスタック間で検出されたドリフトが原因でエラーが発生します。この問題を回避するには、グローバルテーブルをデプロイするためのリファレンスリージョンとして 1 つのリージョンを選択し、そのリージョンのスタックでグローバルテーブルのすべてのレプリカを定義する必要があります。

**重要**  
タイプ `AWS::DynamoDB::Table` のリソースは、テンプレート内でタイプを変更してタイプ `AWS::DynamoDB::GlobalTable` のリソースに変換できません。CloudFormation リソースタイプを変更して単一リージョンテーブルをグローバルテーブルに変換しようとすると、DynamoDB テーブルが削除される可能性があります。

`AWS::DynamoDB::GlobalTable` リソースを使用して、単一のリージョンでテーブルを作成できます。このテーブルは、他の単一リージョンテーブルと同様にデプロイされます。後でスタックを更新して他のリージョンをリソースに追加すると、レプリカがテーブルに追加され、グローバルテーブルに安全に変換されます。

既存の `AWS::DynamoDB::Table` リソースを `AWS::DynamoDB::GlobalTable` リソースに変換する場合、リソースタイプを変換するための推奨ステップは次のとおりです。

1. `AWS::DynamoDB::Table` 削除ポリシーを保持するように設定します。

1. テーブルをスタック定義から削除します。

1. AWS コンソールの単一リージョンテーブルにレプリカを追加し、グローバルテーブルに変換します。

1. 新しいグローバルテーブルを新しい `AWS::DynamoDB::GlobalTable` リソースとしてスタックにインポートします。

## バックアップとポイントインタイムリカバリ
<a name="globaltables-bestpractices-backups"></a>

グローバルテーブル内の 1 つのレプリカの自動バックアップとポイントインタイムリカバリ (PITR) を有効にすることで、ディザスタリカバリの目標を達成できる場合があります。AWS-Backup で作成されたレプリカバックアップは、リージョン間で自動的にレプリケートできるため、耐障害性が向上します。バックアップと PITR 有効化戦略を選択する際は、マルチリージョンの高可用性の観点からディザスタリカバリ計画の目標を検討してください。

## マルチリージョンの高可用性のための設計
<a name="globaltables-bestpractices-multiregion"></a>

グローバルテーブルのデプロイに関する規範的ガイダンスについては、「[DynamoDB グローバルテーブル設計のベストプラクティス](bp-global-table-design.md)」を参照してください。

# DynamoDB での項目と属性の操作
<a name="WorkingWithItems"></a>

Amazon DynamoDB では、*項目*は属性の集まりです。各属性には名前と値があります。属性値はスカラー型、セット型、ドキュメント型のいずれかです。詳細については、「[Amazon DynamoDB: 仕組み](HowItWorks.md)」を参照してください。

DynamoDB では、作成、読み込み、更新、および削除 (CRUD) の 4 つの基本的な操作機能を使用できます。これらの操作はすべてアトミックです。
+ `PutItem` — 項目を作成します。
+ `GetItem` — 項目を読み込みます。
+ `UpdateItem` — 項目を更新します。
+ `DeleteItem` — 項目を削除します。

これらの各オペレーションでは、作業対象の項目のプライマリキーを指定する必要があります。たとえば、`GetItem` を使用して項目を読み込むには、その項目のパーティションキーとソートキー (該当する場合) を指定する必要があります。

4 つの基本的な CRUD オペレーションに加えて、DynamoDB は以下も提供します。
+ `BatchGetItem` — 1 つ以上のテーブルから最大 100 個の項目を読み込みます。
+ `BatchWriteItem` — 1 つ以上のテーブルから最大 25 個の項目を作成または削除します。

これらのバッチ操作は、複数の CRUD オペレーションを単一のリクエストにまとめます。さらに、応答のレイテンシーを最小限に抑えるため、バッチオペレーションは項目を並列で読み書きします。

このセクションには、これらのオペレーションを使用する方法の説明、および、条件付き更新やアトミックカウンターなどの関連するトピックが含まれています。このセクションには、AWS SDK を使用するサンプルコードも含まれています。

**Topics**
+ [DynamoDB 項目のサイズと形式](CapacityUnitCalculations.md)
+ [項目の読み込み](#WorkingWithItems.ReadingData)
+ [項目を書き込みます](#WorkingWithItems.WritingData)
+ [戻り値](#WorkingWithItems.ReturnValues)
+ [バッチオペレーション](#WorkingWithItems.BatchOperations)
+ [アトミックカウンタ](#WorkingWithItems.AtomicCounters)
+ [条件付きの書き込み](#WorkingWithItems.ConditionalUpdate)
+ [DynamoDB での式の使用](Expressions.md)
+ [DynamoDB での Time to Live (TTL) の使用](TTL.md)
+ [DynamoDB のテーブルに対するクエリの実行](Query.md)
+ [DynamoDB でのテーブルのスキャン](Scan.md)
+ [PartiQL: Amazon DynamoDB 用の SQL 互換クエリ言語](ql-reference.md)
+ [項目の操作: Java](JavaDocumentAPIItemCRUD.md)
+ [項目の操作: .NET](LowLevelDotNetItemCRUD.md)

# DynamoDB 項目のサイズと形式
<a name="CapacityUnitCalculations"></a>

DynamoDB テーブルはプライマリキーを除いてスキーマレスです。そのため、テーブルの項目の属性、サイズ、データ型はすべて異なる場合があります。

項目の合計サイズは、属性名と属性値の文字列の長さの合計、および以下に説明するように該当するオーバーヘッドが追加されます。次のガイドラインを使用して属性サイズを予測することができます。
+ 文字列は、UTF-8 バイナリエンコードの Unicode です。文字列のサイズは、**(属性名の UTF-8 でエンコードされたバイト数) \$1 (UTF-8 でエンコードされたバイト数) です。
+ 数値は、有効桁数が最大 38 の可変長です。先頭と末尾の 0 は切り捨てられます。数値のおおよそのサイズは、**(属性名の UTF-8 でエンコードされたバイト数) \$1 (有効桁数 2 あたり 1 バイト) \$1 (1 バイト) です。
+ バイナリ値を DynamoDB に送信するには base64 形式でエンコードする必要がありますが、サイズの計算には値の実際のバイト長が使用されます。バイナリ属性のサイズは、**(属性名の UTF-8 でエンコードされたバイト数) \$1 (raw バイト数) です。
+ null 属性または Boolean 属性のサイズは、**(属性名の UTF-8 でエンコードされたバイト数) \$1 (1 バイト) です。
+ `List` 型または `Map` 型の属性は、その内容にかかわらず、余分な 3 バイトが必要です。`List` または `Map` のサイズは、**(属性名の UTF-8 でエンコードされたバイト数) \$1 (入れ子要素のサイズの合計) \$1 (3 バイト) です。空の `List` または `Map` のサイズは、**(属性名の UTF-8 でエンコードされたバイト数) \$1 (3 バイト) です。
+ `List` または `Map` の各要素には、余分な 1 バイトが必要です。

**注記**  
属性名は長いものよりも短いものにすることをお勧めします。これにより、必要なストレージの量を減らすことができ、使用する RCU/WCU の量を減らすこともできます。

ストレージの請求において、各項目には、有効にした機能に応じて、項目あたりのストレージオーバーヘッドが含まれます。
+ DynamoDB のすべての項目は、インデックス作成に 100 バイトのストレージオーバーヘッドを必要とします。
+ 一部の DynamoDB 機能 (グローバルテーブル、トランザクション、DynamoDB を使用した Kinesis Data Streams の変更データキャプチャ) では、これらの機能を有効にすることでシステムが作成した属性を考慮するために、追加のストレージオーバーヘッドが必要になります。例えば、グローバルテーブルでは、48 バイトのストレージオーバーヘッドがさらに必要になります。

## 項目の読み込み
<a name="WorkingWithItems.ReadingData"></a>

DynamoDB テーブルから項目を読み込むには、`GetItem` オペレーションを使用します。必要な項目のプライマリキーと共にテーブルの名前を指定する必要があります。

**Example**  
次の AWS CLI の例は、`ProductCatalog` テーブルから項目を読み込む方法を示しています。  

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"1"}}'
```

**注記**  
`GetItem` では、プライマリキーの一部だけではなく*全体*を指定する必要があります。たとえば、テーブルに複合プライマリキー (パーティションキーおよびソートキー) がある場合、パーティションキーおよびソートキーの値を指定する必要があります。

デフォルトでは、`GetItem` リクエストは結果的に整合性のある読み込みを行います。代わりに、`ConsistentRead` パラメータを使用して、強い整合性のある読み込みをリクエストできます。(これは、追加の読み込みキャパシティーユニットを消費しますが、項目の最新バージョンを返します)。

`GetItem` は、項目のすべての属性を返します。一部の属性のみが返されるように、*プロジェクション式*を使用できます。詳細については、「[DynamoDB でのプロジェクション式の使用](Expressions.ProjectionExpressions.md)」を参照してください。

`GetItem` で消費される読み込みキャパシティーユニットの数を返すには、`ReturnConsumedCapacity` パラメータを `TOTAL` に設定します。

**Example**  
次の AWS Command Line Interface (AWS CLI) の例は、オプションの `GetItem` パラメータを示しています。  

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"1"}}' \
    --consistent-read \
    --projection-expression "Description, Price, RelatedItems" \
    --return-consumed-capacity TOTAL
```

## 項目を書き込みます
<a name="WorkingWithItems.WritingData"></a>

DynamoDB テーブルの項目を作成、更新、または削除するには、次のオペレーションのいずれかを使用します。
+ `PutItem`
+ `UpdateItem`
+ `DeleteItem`

これらの各オペレーションで、プライマリキーの一部の属性ではなくすべての属性を指定する必要があります。たとえば、テーブルに複合プライマリキー (パーティションキーおよびソートキー) がある場合、パーティションキーおよびソートキーの値を提供する必要があります。

これらのオペレーションのいずれかで消費される書き込みキャパシティーユニットの数を返すには、`ReturnConsumedCapacity` パラメータを次のいずれかに設定します。
+ `TOTAL` — 消費された書き込み容量単位の総数を返します。
+ `INDEXES` — 消費された書き込み容量単位の総数とともに、テーブルおよびオペレーションに影響を受けたセカンダリインデックスの小計を返します。
+ `NONE` — 書き込み容量の詳細は返されません。(これがデフォルトです)

### PutItem
<a name="WorkingWithItems.WritingData.PutItem"></a>

`PutItem` は新しい項目を作成します。同じキーを持つ項目がテーブルにすでに存在する場合は、新しい項目に置き換えられます。

**Example**  
`Thread` テーブルに新しい項目を書き込みます。`Thread` のプライマリキーは、`ForumName` (パーティションキー) と `Subject` (ソートキー) で構成されます。  

```
aws dynamodb put-item \
    --table-name Thread \
    --item file://item.json
```
`--item` の引数は、ファイル `item.json` に保存されます。  

```
{
    "ForumName": {"S": "Amazon DynamoDB"},
    "Subject": {"S": "New discussion thread"},
    "Message": {"S": "First post in this thread"},
    "LastPostedBy": {"S": "fred@example.com"},
    "LastPostDateTime": {"S": "201603190422"}
}
```

### UpdateItem
<a name="WorkingWithItems.WritingData.UpdateItem"></a>

指定されたキーを持つ項目が存在しない場合は、`UpdateItem` により新しい項目が作成されます。または、既存の項目の属性が変更されます。

*更新式*を使用して、変更する属性と新しい値を指定します。詳細については、「[DynamoDB での更新式の使用](Expressions.UpdateExpressions.md)」を参照してください。

更新式では、実際の値にプレースホルダーとして式の属性値を使用します。詳細については、「[DynamoDB での式属性値の使用](Expressions.ExpressionAttributeValues.md)」を参照してください。

**Example**  
`Thread` 項目のさまざまな属性を変更します。オプションの `ReturnValues` パラメータは、更新後に表示されるように項目を表示します。詳細については、「[戻り値](#WorkingWithItems.ReturnValues)」を参照してください。  

```
aws dynamodb update-item \
    --table-name Thread \
    --key file://key.json \
    --update-expression "SET Answered = :zero, Replies = :zero, LastPostedBy = :lastpostedby" \
    --expression-attribute-values file://expression-attribute-values.json \
    --return-values ALL_NEW
```

`--key` の引数は、ファイル `key.json` に保存されます。

```
{
    "ForumName": {"S": "Amazon DynamoDB"},
    "Subject": {"S": "New discussion thread"}
}
```

`--expression-attribute-values` の引数は、ファイル `expression-attribute-values.json` に保存されます。

```
{
    ":zero": {"N":"0"},
    ":lastpostedby": {"S":"barney@example.com"}
}
```

### DeleteItem
<a name="WorkingWithItems.WritingData.DeleteItem"></a>

`DeleteItem` は指定されたキーを持つ項目を削除します。

**Example**  
次の AWS CLI の例で、`Thread` 項目を削除する方法について説明します。  

```
aws dynamodb delete-item \
    --table-name Thread \
    --key file://key.json
```

## 戻り値
<a name="WorkingWithItems.ReturnValues"></a>

場合によっては、ある属性値を変更する前後にその属性値が表示されるように DynamoDB で返すこともできます。`PutItem`、`UpdateItem`、および `DeleteItem` オペレーションには、`ReturnValues` パラメータがあり、これを使用することで、属性の変更前後に、その属性値を返すことができます。

`ReturnValues` のデフォルトの値は `NONE` で、DynamoDB は変更された属性の情報を返しません。

以下は、DynamoDB API オペレーションごとに整理された `ReturnValues` のその他の有効な設定です。

### PutItem
<a name="WorkingWithItems.ReturnValues.PutItem"></a>
+ `ReturnValues`: `ALL_OLD`
  + 既存の項目に上書きする場合、`ALL_OLD` は上書きの前に表示されたように、項目全体を返します。
  + 存在しない項目を書き込んだ場合は、`ALL_OLD` による影響はありません。

### UpdateItem
<a name="WorkingWithItems.ReturnValues.UpdateItem"></a>

`UpdateItem` の最も一般的な使用方法は、既存の項目の更新です。ただし、`UpdateItem` は実際には*アップサート*を実行します。つまり、項目が存在しない場合は、自動的に作成します。
+ `ReturnValues`: `ALL_OLD`
  + 既存の項目を更新する場合、`ALL_OLD` は更新前に表示されたように、項目全体を返します。
  + 存在しない項目を更新 (アップサート) すると、`ALL_OLD` による影響はありません。
+ `ReturnValues`: `ALL_NEW`
  + 既存の項目を更新する場合、`ALL_NEW` は更新後に表示されるように、項目全体が返されます。
  + 存在しない項目を更新 (アップサート) すると、`ALL_NEW` は項目全体を返します。
+ `ReturnValues`: `UPDATED_OLD`
  + 既存の項目を更新した場合、`UPDATED_OLD` は、更新前に表示されたように、更新された属性だけを返します。
  + 存在しない項目を更新 (アップサート) すると、`UPDATED_OLD` による影響はありません。
+ `ReturnValues`: `UPDATED_NEW`
  + 既存の項目を更新した場合、`UPDATED_NEW` は、更新後に表示されるように、影響のある属性だけを返します。
  + 存在しない項目を更新 (アップサート) する場合、`UPDATED_NEW` は更新後に表示される、更新された属性だけを返します。

### DeleteItem
<a name="WorkingWithItems.ReturnValues.DeleteItem"></a>
+ `ReturnValues`: `ALL_OLD`
  + 既存の項目を削除すると、`ALL_OLD` は削除の前に表示されたように、項目全体を返します。
  + 存在しない項目を削除すると、`ALL_OLD` はデータを返しません。

## バッチオペレーション
<a name="WorkingWithItems.BatchOperations"></a>

複数の項目の読み込みや書き込みを必要とするアプリケーションのために、DynamoDB は `BatchGetItem` および `BatchWriteItem` オペレーションを提供します。これらのオペレーションを使用すると、アプリケーションから DynamoDB へのネットワークラウンドトリップの数を減らすことができます。さらに、DynamoDB は個別の読み込みまたは書き込みオペレーションを並行して実行します。同時実行またはスレッディングの管理をする必要がないので、アプリケーションにはこの並列処理が役立ちます。

バッチオペレーションは、基本的に複数の読み込みまたは書き込みリクエストをまとめます。たとえば、`BatchGetItem` リクエストに 5 つの項目が含まれている場合、DynamoDB によって 5 回の `GetItem` オペレーションが実行されます。同様に、`BatchWriteItem` リクエストに 2 つの PUT リクエストと 4 つの DELETE リクエストが含まれている場合、DynamoDB によって 2 つの `PutItem` リクエストと 4 つの `DeleteItem` リクエストが実行されます。

一般的に、バッチの*すべて*のリクエストが失敗しない限り、バッチオペレーションは失敗しません。たとえば、`BatchGetItem` オペレーションを実行する際、バッチの個々の `GetItem` リクエストが失敗したとします。この場合、`BatchGetItem` は失敗した `GetItem` リクエストからキーとデータを返します。バッチのその他の `GetItem` リクエストは影響を受けません。

### BatchGetItem
<a name="WorkingWithItems.BatchOperations.BatchGetItem"></a>

1 回の `BatchGetItem` オペレーションには、最大 100 の個々の `GetItem` リクエストが含まれていて、最大 16 MB のデータを取得できます。さらに、`BatchGetItem` オペレーションで、複数のテーブルから項目を取得できます。

**Example**  
一部の属性のみが返されるようにプロジェクション式を使用して `Thread` テーブルから 2 つの項目を取得します。  

```
aws dynamodb batch-get-item \
    --request-items file://request-items.json
```
`--request-items` の引数は、ファイル `request-items.json` に保存されます。  

```
{
    "Thread": {
        "Keys": [
            {
                "ForumName":{"S": "Amazon DynamoDB"},
                "Subject":{"S": "DynamoDB Thread 1"}
            },
            {
                "ForumName":{"S": "Amazon S3"},
                "Subject":{"S": "S3 Thread 1"}
            }
        ],
        "ProjectionExpression":"ForumName, Subject, LastPostedDateTime, Replies"
    }
}
```

### BatchWriteItem
<a name="WorkingWithItems.BatchOperations.BatchWriteItem"></a>

`BatchWriteItem` オペレーションには最大 25 の個々の `PutItem` リクエストと `DeleteItem` リクエストを含むことができ、最大 16 MB のデータを書き込めます。(個々の項目の最大サイズは 400 KB です。) さらに、`BatchWriteItem` オペレーションで、複数のテーブルの項目を入力したり削除したりできます。

**注記**  
`BatchWriteItem` では `UpdateItem` リクエストはサポートされません。

**Example**  
`ProductCatalog` テーブルに 2 つの項目を書き込みます。  

```
aws dynamodb batch-write-item \
    --request-items file://request-items.json
```
`--request-items` の引数は、ファイル `request-items.json` に保存されます。  

```
{
    "ProductCatalog": [
        {
            "PutRequest": {
                "Item": {
                    "Id": { "N": "601" },
                    "Description": { "S": "Snowboard" },
                    "QuantityOnHand": { "N": "5" },
                    "Price": { "N": "100" }
                }
            }
        },
        {
            "PutRequest": {
                "Item": {
                    "Id": { "N": "602" },
                    "Description": { "S": "Snow shovel" }
                }
            }
        }
    ]
}
```

## アトミックカウンタ
<a name="WorkingWithItems.AtomicCounters"></a>

この `UpdateItem` オペレーションを使用して、*アトミックカウンター* (他の書き込みリクエストに干渉することなく無条件に増分される数値属性) を実装できます。（すべての書き込みリクエストは、受信された順に適用されます）。アトミックカウンターでは、更新はべき等ではありません。つまり、`UpdateItem` を呼び出すたびに数値はインクリメントまたはデクリメントされます。アトミックカウンタの更新に使用されるインクリメント値が正の場合、オーバーカウントが発生する可能性があります。インクリメント値が負の場合、アンダーカウントの原因となります。

ウェブサイトの訪問者数を追跡するためにアトミックカウンターを使用できます。この場合、アプリケーションでは、現在値に関係なく、数値はインクリメントされます。`UpdateItem` オペレーションが失敗した場合、アプリケーションはオペレーションを再試行します。これには、カウンターを 2 度更新する恐れがありますが、ウェブサイトの訪問者数のカウントに多少の誤差があっても許容できるでしょう。

アトミックカウンターはカウントの誤差が許容されない場合にはふさわしくありません (銀行業務用のアプリケーションなど)。この場合は、アトミックカウンターの代わりに条件付き更新を使用する方が安全です。

詳細については、「[数値属性の増減](Expressions.UpdateExpressions.md#Expressions.UpdateExpressions.SET.IncrementAndDecrement)」を参照してください。

**Example**  
次の AWS CLI の例では、商品の `Price` が 5 でインクリメントされます。この例では、カウンタが更新される前に項目が存在することがわかっていました。(`UpdateItem` は冪等性ではないので、`Price` はこのコードを実行するたびに増えていきます)。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id": { "N": "601" }}' \
    --update-expression "SET Price = Price + :incr" \
    --expression-attribute-values '{":incr":{"N":"5"}}' \
    --return-values UPDATED_NEW
```

## 条件付きの書き込み
<a name="WorkingWithItems.ConditionalUpdate"></a>

デフォルトでは、DynamoDB 書き込みオペレーション (`PutItem`、`DeleteItem`) は*無条件*です。つまり、これらの各オペレーションでは、指定されたプライマリキーを持つ既存の項目が上書きされます。

DynamoDB はオプションでこれらのオペレーションの条件付き書き込みをサポートしています。条件付き書き込みが成功するのは、項目の属性が 1 つ以上の想定条件を満たす場合のみです。それ以外の場合は、エラーが返されます。

条件付き書き込みでは、その条件について項目の最新更新バージョンと照合します。なお、項目が以前に存在しなかった場合や、その項目に対して最後に成功した操作が削除であった場合、条件付き書き込みでは以前の項目は検出されません。

 条件付き書き込みは多くの状況で役立ちます。たとえば、同じプライマリキーを持つ既存の項目がない場合にのみ、`PutItem` オペレーションが成功するようにできます。または、属性の 1 つに特定の値がある場合に `UpdateItem` オペレーションが項目を変更することを防ぐことができます。

条件付き書き込みは、複数のユーザーが同じ項目を変更しようとする場合に役立ちます。2 人のユーザー (Alice と Bob) が DynamoDB テーブルから同じ項目を処理している以下の図を考慮します。

![\[Alice と Bob は、ID 1 の項目を変更しようとし、条件付き書き込みの必要性を示します。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/update-no-condition.png)


Alice が AWS CLI を使用して `Price` 属性を 8 に更新するとします。

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"1"}}' \
    --update-expression "SET Price = :newval" \
    --expression-attribute-values file://expression-attribute-values.json
```

`--expression-attribute-values` の引数は、ファイル `expression-attribute-values.json` に保存されます。

```
{
    ":newval":{"N":"8"}
}
```

次に、Bob が後で同様の `UpdateItem` リクエストを発行しますが、`Price` を 12 に変更します。Bob には、`--expression-attribute-values` パラメータは次のように見えます。

```
{
    ":newval":{"N":"12"}
}
```

Bob のリクエストは成功しますが、その前の Alice の更新は失われます。

条件付き `PutItem`、`DeleteItem`、または `UpdateItem` をリクエストするには、条件式を指定します。*条件式*は、属性名、条件付き演算子および組み込み関数を含む文字列です。式全体の評価が true になる必要があります。それ以外の場合は、このオペレーションは失敗します。

次は、条件付き書き込みにより Alice の更新が上書きされるのを防ぐ方法について、以下の図を考慮します。

![\[条件付き書き込みにより、ユーザー Bob の更新によってユーザー Alice の変更が同じ項目に上書きされるのを防ぐことができます。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/update-yes-condition.png)


Alice はまず `Price` を 8 に更新を試行しますが、現在の `Price` が 10 である場合にのみ、という条件でそうします。

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"1"}}' \
    --update-expression "SET Price = :newval" \
    --condition-expression "Price = :currval" \
    --expression-attribute-values file://expression-attribute-values.json
```

`--expression-attribute-values` の引数は、ファイル `expression-attribute-values.json` に保存されます。

```
{
    ":newval":{"N":"8"},
    ":currval":{"N":"10"}
}
```

条件が true に評価されるため、Alice の更新は成功します。

次に、Bob は、`Price` を 12 に 更新しますが、現在の `Price` が 10 である場合にのみ、という条件でそうします。Bob には、`--expression-attribute-values` パラメータは次のように見えます。

```
{
    ":newval":{"N":"12"},
    ":currval":{"N":"10"}
}
```

Alice がすでに `Price` を 8 に変更していたので、条件式は false 評価され Bob の更新は失敗します。

詳細については、「[DynamoDB 条件式 CLI の例](Expressions.ConditionExpressions.md)」を参照してください。

### 条件付き書き込みの冪等性
<a name="WorkingWithItems.ConditionalWrites.Idempotence"></a>

条件付き書き込みは、条件チェックが更新される同じ属性で行われる場合に、*べき等性*が保たれます。つまり、リクエスト時に項目の特定の属性値が想定されたものである場合にのみ、DynamoDB によって指定された書き込みリクエストが実行されます。

たとえば、`UpdateItem` リクエストを発行し、項目の `Price` を、現在の `Price` が 20 である場合のみ、3 増加させるとします。リクエストを送信した後、その結果を得る前にネットワークエラーが発生したため、リクエストが成功したかどうか不明です。この条件付き書き込みはべき等のオペレーションであるため、同じ `UpdateItem` リクエストを再試行できます。すると、DynamoDB は、現在の `Price` が 20 である場合のみ項目を更新します。

### 条件付き書き込みで消費されるキャパシティユニット
<a name="WorkingWithItems.ConditionalWrites.ReturnConsumedCapacity"></a>

条件付き書き込み中に `ConditionExpression` が false と評価された場合でも、DynamoDB はテーブルの書き込みキャパシティを消費します。消費量は、既存の項目のサイズ（または最低 1）によって異なります。例えば、既存の項目が 300 KB で、作成または更新しようとしている新しい項目が 310 KB の場合、消費される書き込みキャパシティーユニットは、条件が満たされないかったら 300、満たされたら 310 になります。これが新しい項目 (既存の項目なし) の場合、消費される書き込みキャパシティーユニットは条件が満たされれば 1、条件が満たされなかったら 310 になります。

**注記**  
書き込みオペレーションでは、*書き込み*キャパシティーユニットのみが消費されます。*読み込み*キャパシティーユニットは消費されません。

失敗した条件付き書き込みは `ConditionalCheckFailedException` を返します。これが起きると、消費された書き込みキャパシティに関する情報はレスポンスで返されません。

条件付き書き込みの際に消費された書き込みキャパシティーユニットの数を返すには、`ReturnConsumedCapacity` パラメータを使用します。
+ `TOTAL` — 消費された書き込み容量単位の総数を返します。
+ `INDEXES` — 消費された書き込み容量単位の総数とともに、テーブルおよびオペレーションに影響を受けたセカンダリインデックスの小計を返します。
+ `NONE` — 書き込み容量の詳細は返されません。(これがデフォルトです)

  

**注記**  
グローバルセカンダリインデックスとは異なり、ローカルセカンダリインデックスは、プロビジョンドスループット性能をそのテーブルと共有します。ローカルセカンダリインデックスでの読み込みと書き込みのアクティビティは、テーブルからプロビジョンドスループット性能を消費します。

# DynamoDB での式の使用
<a name="Expressions"></a>

Amazon DynamoDB では、式を使用して、項目から読み取る属性の指定、条件が満たされたときのデータの書き込み、項目の更新方法の指定、クエリの定義、クエリ結果のフィルタリングを行うことができます。**

このテーブルでは、式の基本的な文法と利用可能な式の種類について説明します。


| 式タイプ | 説明 | 
| --- | --- | 
| プロジェクション式 | プロジェクション式は、GetItem、Query、Scan などのオペレーションを使用するときに項目から取得する属性を特定します。 | 
| 条件式 | 条件式は、PutItem、UpdateItem、DeleteItem の各オペレーションを使用したときに、どの項目を変更すべきかを決定します。 | 
| 更新式 | 更新式は、UpdateItem で項目の属性を変更する方法を指定します。例えば、スカラー値を設定したり、リストまたはマップから要素を削除したりします。 | 
| キー条件式 | キー条件式は、クエリがテーブルやインデックスから読み取る項目を決定します。 | 
| フィルター式 | フィルター式は、クエリ結果のうち、どの項目を返すべきかを決定します。他のすべての結果は破棄されます。 | 

式の構文と各タイプの式の詳細については、以下のセクションを参照してください。

**Topics**
+ [DynamoDB で式を使用する場合の項目属性の参照](Expressions.Attributes.md)
+ [DynamoDB の式の属性名 (エイリアス)](Expressions.ExpressionAttributeNames.md)
+ [DynamoDB での式属性値の使用](Expressions.ExpressionAttributeValues.md)
+ [DynamoDB でのプロジェクション式の使用](Expressions.ProjectionExpressions.md)
+ [DynamoDB での更新式の使用](Expressions.UpdateExpressions.md)
+ [DynamoDB の条件式とフィルター式、演算子、関数](Expressions.OperatorsAndFunctions.md)
+ [DynamoDB 条件式 CLI の例](Expressions.ConditionExpressions.md)

**注記**  
下位互換性のために、DynamoDB は式を使用しない条件パラメータもサポートします。詳細については、「[レガシー DynamoDB 条件パラメータ](LegacyConditionalParameters.md)」を参照してください。  
新しいアプリケーションでは、レガシーパラメータではなく式を使用する必要があります。

# DynamoDB で式を使用する場合の項目属性の参照
<a name="Expressions.Attributes"></a>

このセクションでは、Amazon DynamoDB で式の項目属性を参照する方法を説明します。複数のリストやマップ内で深くネストされている場合でも、属性を使用できます。

**Topics**
+ [最上位属性](#Expressions.Attributes.TopLevelAttributes)
+ [入れ子の属性](#Expressions.Attributes.NestedAttributes)
+ [ドキュメントパス](#Expressions.Attributes.NestedElements.DocumentPathExamples)

**サンプル項目: ProductCatalog**  
このページの例では、`ProductCatalog` テーブルで次のサンプル項目を使用しています (このテーブルについては、「[DynamoDB で使用するテーブルとデータの例](AppendixSampleTables.md)」で説明されています)。

```
{
    "Id": 123,
    "Title": "Bicycle 123",
    "Description": "123 description",
    "BicycleType": "Hybrid",
    "Brand": "Brand-Company C",
    "Price": 500,
    "Color": ["Red", "Black"],
    "ProductCategory": "Bicycle",
    "InStock": true,
    "QuantityOnHand": null,
    "RelatedItems": [
        341,
        472,
        649
    ],
    "Pictures": {
        "FrontView": "http://example.com/products/123_front.jpg",
        "RearView": "http://example.com/products/123_rear.jpg",
        "SideView": "http://example.com/products/123_left_side.jpg"
    },
    "ProductReviews": {
	    "FiveStar": [
	    		"Excellent! Can't recommend it highly enough! Buy it!",
	    		"Do yourself a favor and buy this."
	    ],
	    "OneStar": [
	    		"Terrible product! Do not buy this."
	    ]
    },
    "Comment": "This product sells out quickly during the summer",
    "Safety.Warning": "Always wear a helmet"
 }
```

次の点に注意してください。
+ パーティションキー値 (`Id`) は `123` です。ソートキーはありません。
+ ほとんどの属性に、`String`、`Number`、`Boolean`、`Null` などのスカラーデータ型があります。
+ 1 つの属性 (`Color`) は `String Set` です。
+ 次の属性はドキュメントデータ型です。
  + `RelatedItems` のリスト。各要素は関連製品の `Id` です。
  + `Pictures` のマップ。各要素は対応するイメージファイルの URL と、写真の短い説明です。
  + `ProductReviews` のマップ。各要素は、レーティングと、そのレーティングに対応する評価のリストを表します。最初に、このマップには 5 つ星と 1 つ星の評価が入力されます。

## 最上位属性
<a name="Expressions.Attributes.TopLevelAttributes"></a>

属性が別の属性に組み込まれていない場合、*最上位属性*と呼ばれます。`ProductCatalog` 項目の最上位属性は次のようになります。
+ `Id`
+ `Title`
+ `Description`
+ `BicycleType`
+ `Brand`
+ `Price`
+ `Color`
+ `ProductCategory`
+ `InStock`
+ `QuantityOnHand`
+ `RelatedItems`
+ `Pictures`
+ `ProductReviews`
+ `Comment`
+ `Safety.Warning`

`Color` (リスト)、`RelatedItems` (リスト)、`Pictures` (マップ)、および `ProductReviews` (マップ) を除くすべての最上位属性はスカラーです。

## 入れ子の属性
<a name="Expressions.Attributes.NestedAttributes"></a>

属性が別の属性に組み込まれている場合、*入れ子の属性*と呼ばれます。入れ子の属性にアクセスするには、*間接参照演算子*を使用します。
+ `[n]` — リストの要素
+ `.` (ドット) — マップの要素

### リスト要素へのアクセス
<a name="Expressions.Attributes.NestedElements.AccessingListElements"></a>

リスト要素の間接参照演算子は **[*n*]** で、*n* は要素数です。リストの要素はゼロベースであるため、[0] はリスト内の最初の要素、[1] は 2 番目の要素、という順番で表されます。次に例を示します。
+ `MyList[0]`
+ `AnotherList[12]`
+ `ThisList[5][11]`

要素 `ThisList[5]` は、それ自体がネストされたリストです。したがって、`ThisList[5][11]` は、そのリストの 12 番目の要素を参照します。

角括弧内の数は、負以外の整数である必要があります。そのため、次の式は無効です。
+ `MyList[-1]`
+ `MyList[0.4]`

### マップ要素へのアクセス
<a name="Expressions.Attributes.NestedElements.AccessingMapElements"></a>

マップ要素の間接参照演算子は **.** (ドット) です。マップの要素間の区切り文字として、ドットを使用します。
+ `MyMap.nestedField`
+ `MyMap.nestedField.deeplyNestedField`

## ドキュメントパス
<a name="Expressions.Attributes.NestedElements.DocumentPathExamples"></a>

式では、*ドキュメントパス*を使用して、属性の場所を DynamoDB に伝えます。最上位属性の場合、ドキュメントパスは単純に属性名になります。ネストされた属性の場合は、間接参照演算子を使用してドキュメントパスを構築します。

ドキュメントパスのいくつかの例を次に示します。([DynamoDB で式を使用する場合の項目属性の参照](#Expressions.Attributes) に示された項目を参照してください)
+ 最上位のスカラー属性。

   `Description`
+ 最上位のリスト属性。(これはいくつかの要素だけではなく、すべてを示すリストを返します)

  `RelatedItems`
+ `RelatedItems` リストの 3 番目の要素 （このリスト要素はゼロベースであることに注意してください)。

  `RelatedItems[2]`
+ 製品の正面の写真。

  `Pictures.FrontView`
+ すべての 5 つ星の評価。

  `ProductReviews.FiveStar`
+ 最初の 5 つ星の評価。

  `ProductReviews.FiveStar[0]`

**注記**  
ドキュメントパスの最大深度は 32 です。したがって、任意のパスの間接参照演算子の数はこの制限を超えることはできません。

次の要件を満たしている限り、ドキュメントパスには任意の属性名を使用できます。
+ 最初の文字は `a-z`、`A-Z`、または `0-9`
+ 2 番目の文字 (存在する場合) は `a-z`、`A-Z`

**注記**  
属性名がこの要件を満たさない場合は、式の属性名をプレースホルダーとして定義する必要があります。

詳細については、「[DynamoDB の式の属性名 (エイリアス)](Expressions.ExpressionAttributeNames.md)」を参照してください。

# DynamoDB の式の属性名 (エイリアス)
<a name="Expressions.ExpressionAttributeNames"></a>

式の属性名は、実際の属性名の代わりとして Amazon DynamoDB の式で使用するエイリアス (またはプレースホルダー) です。**式の属性名は、シャープ記号 (`#`) で始める必要があり、1 つ以上の英数字が続きます。アンダースコア (`_`) 文字も使用できます。

このセクションでは、式の属性名を使用する必要があるいくつかの状況について説明します。

**注記**  
このセクションの例では AWS Command Line Interface (AWS CLI) を使用します。

**Topics**
+ [予約語](#Expressions.ExpressionAttributeNames.ReservedWords)
+ [特殊文字を含む属性名](#Expressions.ExpressionAttributeNames.AttributeNamesContainingSpecialCharacters)
+ [入れ子の属性](#Expressions.ExpressionAttributeNames.NestedAttributes)
+ [属性名を繰り返し参照する](#Expressions.ExpressionAttributeNames.RepeatingAttributeNames)

## 予約語
<a name="Expressions.ExpressionAttributeNames.ReservedWords"></a>

DynamoDB 予約語と競合する属性名を含む式を書く必要が生じることもあります。予約語の一覧については、「[DynamoDB の予約語](ReservedWords.md)」を参照してください。

たとえば、AWS CLI は予約語であるため、次の `COMMENT` の例は失敗します。

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"123"}}' \
    --projection-expression "Comment"
```

この問題に対処するには、`Comment` を、`#c` などの式の属性名で置き換えることができます。`#`（シャープ記号）は必須であり、これが属性名のプレースホルダーであることを示します。AWS CLI の例は次のようになります。

```
aws dynamodb get-item \
     --table-name ProductCatalog \
     --key '{"Id":{"N":"123"}}' \
     --projection-expression "#c" \
     --expression-attribute-names '{"#c":"Comment"}'
```

**注記**  
属性名が数値で始まるか、スペースまたは予約語を含む場合、式の属性名を使用して式のその属性名を置き換える*必要があります*。

## 特殊文字を含む属性名
<a name="Expressions.ExpressionAttributeNames.AttributeNamesContainingSpecialCharacters"></a>

式では、ドット (".") はドキュメントパスの区切り文字として解釈されます。ただし、DynamoDB では属性名の一部としてドット文字や、ハイフン (「-」) などの特殊文字を使用することもできます。これは、あいまいな意味を持つことがあります。例として、`ProductCatalog` 項目から `Safety.Warning` 属性を取得するとします ([DynamoDB で式を使用する場合の項目属性の参照](Expressions.Attributes.md) を参照してください)。

プロジェクション式を使用して、`Safety.Warning` にアクセスするとします。

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"123"}}' \
    --projection-expression "Safety.Warning"
```

DynamoDB は、予想される文字列 ("`Always wear a helmet`") ではなく空の結果を返します。これは、DynamoDB が式のドットをドキュメントパスの区切り文字として解釈するためです。この場合、`#sw` の置換として式の属性名 (`Safety.Warning` など) を定義する必要があります。その後、次のプロジェクション式を使用します。

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"123"}}' \
    --projection-expression "#sw" \
    --expression-attribute-names '{"#sw":"Safety.Warning"}'
```

次に、DynamoDB が正しい結果を返します。

**注記**  
属性名にドット (「.」) またはハイフン (「-」) が含まれている場合は、式の属性名を使用して、式でその属性名を置き換える*必要があります*。

## 入れ子の属性
<a name="Expressions.ExpressionAttributeNames.NestedAttributes"></a>

ネストされた属性 `ProductReviews.OneStar` にアクセスするとします。式の属性名において、DynamoDB はドット (".") を属性名内の文字として扱います。ネストされた属性を参照するには、ドキュメントパス内の要素ごとに式の属性名を定義します。
+ `#pr — ProductReviews`
+ `#1star — OneStar`

その後、プロジェクション式として `#pr.#1star` を使用します。

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"123"}}' \
    --projection-expression "#pr.#1star"  \
    --expression-attribute-names '{"#pr":"ProductReviews", "#1star":"OneStar"}'
```

次に、DynamoDB が正しい結果を返します。

## 属性名を繰り返し参照する
<a name="Expressions.ExpressionAttributeNames.RepeatingAttributeNames"></a>

式の属性名は、同じ属性名を繰り返し参照する必要がある場合に役立ちます。たとえば、`ProductCatalog` 項目からいくつかの評価を取得する次の式を考えてみます。

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"123"}}' \
    --projection-expression "ProductReviews.FiveStar, ProductReviews.ThreeStar, ProductReviews.OneStar"
```

これをより簡潔にするために、`ProductReviews` を、`#pr` などの式の属性名で置き換えることができます。変更された式は次のようになります。
+  `#pr.FiveStar, #pr.ThreeStar, #pr.OneStar` 

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"123"}}' \
    --projection-expression "#pr.FiveStar, #pr.ThreeStar, #pr.OneStar" \
    --expression-attribute-names '{"#pr":"ProductReviews"}'
```

式属性名を定義した場合、式全体で一貫して使用する必要があります。さらに、`#` 記号を省略することはできません。

# DynamoDB での式属性値の使用
<a name="Expressions.ExpressionAttributeValues"></a>

Amazon DynamoDB の式の属性値は変数として機能します。**式の属性値は、比較する実際の値 (ランタイムまでは不明である可能性のある値) の代わりになります。式の属性値はコロン (`:`) で始まり、1 つ以上の英数字が続きます。

たとえば、`ProductCatalog` で入手でき、費用が `Black` 以下の `500` 項目のすべてを返すとします。この `Scan` (AWS Command Line Interface) の例のように AWS CLI オペレーションでフィルタ式を使用できます。

```
aws dynamodb scan \
    --table-name ProductCatalog \
    --filter-expression "contains(Color, :c) and Price <= :p" \
    --expression-attribute-values file://values.json
```

`--expression-attribute-values` の引数は、ファイル `values.json` に保存されます。

```
{
    ":c": { "S": "Black" },
    ":p": { "N": "500" }
}
```

式属性値を定義した場合、式全体で一貫して使用する必要があります。さらに、`:` 記号を省略することはできません。

式の属性値は、主な条件式、条件式、更新式、およびフィルター式で使用されます。

# DynamoDB でのプロジェクション式の使用
<a name="Expressions.ProjectionExpressions"></a>

テーブルからデータを読み込むには、次のような `GetItem`、`Query`、または `Scan` のオペレーションを使用します。Amazon DynamoDB は、デフォルトですべての項目属性を返します。すべての属性ではなく、一部の属性のみを取得するには、プロジェクション式を使用します。

*プロジェクション式*は、任意の属性を識別する文字列です。1 つの属性を取得するには、名前を指定します。複数の属性の場合、名前をカンマで区切る必要があります。

`ProductCatalog` からの [DynamoDB で式を使用する場合の項目属性の参照](Expressions.Attributes.md) 項目に基づく、プロジェクション式のいくつかの例を次に示します。
+ 1 つの最上位属性。

  `Title `
+ 3 つの最上位属性。DynamoDB は `Color` セット全体を取得します。

  `Title, Price, Color`
+ 4 つの最上位属性。DynamoDB は `RelatedItems` および `ProductReviews` のコンテンツ全体を返します。

  `Title, Description, RelatedItems, ProductReviews`

**注記**  
プロジェクション式は、プロビジョンドスループットの消費には影響しません。DynamoDB は、アプリケーションに返すデータの量ではなく、項目のサイズに基づいて、キャパシティユニットの消費数を決定します。

**予約語と特殊文字**

DynamoDB には予約語と特殊文字があります。DynamoDB では、これらの予約語と特殊文字を名前に使用できますが、お勧めしません。これらの名前を式で使用するたびに、代わりにエイリアスを使用する必要があるためです。詳細な一覧については、「[DynamoDB の予約語](ReservedWords.md)」を参照してください。

次の場合は、実際の名前の代わりに式の属性名を使用する必要があります。
+ 属性名が DynamoDB の予約語リストにある場合。
+ 最初の文字が `a-z` または `A-Z` で、2 番目の文字 (存在する場合) が `a-Z`、`A-Z`、`0-9` のいずれかでなければならないという要件を属性名が満たしていない場合。
+ 属性名に、**\$1** (ハッシュ) または **:** (コロン) が含まれている場合。

以下の AWS CLI の例に示しているのは、`GetItem` オペレーションでプロジェクション式を使用する方法です。この式は最上位スカラー射影内の属性 (`Description`)、リスト内の最初の要素 (`RelatedItems[0]`)、およびマップ内に入れ子にするリスト (`ProductReviews.FiveStar`) を取得します。

```
aws dynamodb get-item \
    --table-name ProductCatalog \
    --key '"Id": { "N": "123" } \
    --projection-expression "Description, RelatedItems[0], ProductReviews.FiveStar"
```

この例では、次の JSON が返されます。

```
{
    "Item": {
        "Description": {
            "S": "123 description"
        },
        "ProductReviews": {
            "M": {
                "FiveStar": {
                    "L": [
                        {
                            "S": "Excellent! Can't recommend it highly enough! Buy it!"
                        },
                        {
                            "S": "Do yourself a favor and buy this."
                        }
                    ]
                }
            }
        },
        "RelatedItems": {
            "L": [
                {
                    "N": "341"
                }
            ]
        }
    }
}
```

# DynamoDB での更新式の使用
<a name="Expressions.UpdateExpressions"></a>

`UpdateItem` オペレーションは、既存項目を更新します。また、存在しない場合は新しい項目をテーブルに追加します。更新する項目のキーを指定する必要があります。また、変更する属性を示す更新式と、その式に割り当てる値も指定する必要があります。

*更新式*は、`UpdateItem` が項目の属性を変更する方法を指定します。たとえば、スカラー値を設定したり、リストまたはマップから要素を削除したりします。

更新式の構文の概要を次に示します。

```
update-expression ::=
    [ SET action [, action] ... ]
    [ REMOVE action [, action] ...]
    [ ADD action [, action] ... ]
    [ DELETE action [, action] ...]
```

更新式は、1 つ以上の句で構成されます。各句は、`SET`、`REMOVE`、`ADD`、または `DELETE` キーワードで始まります。これらのいずれの句も、任意の順序で更新式に含めることができます。ただし、各アクションキーワードは 1 回のみ表示できます。

各句内には、カンマで区切った 1 つ以上のアクションがあります。各アクションはデータ変更を表します。

このセクションの例は、`ProductCatalog` の [DynamoDB でのプロジェクション式の使用](Expressions.ProjectionExpressions.md) 項目に基づいています。

以下のトピックでは、`SET` アクションのさまざまなユースケースについて説明します。

**Topics**
+ [SET — 項目属性の変更または追加](#Expressions.UpdateExpressions.SET)
+ [REMOVE — 項目から属性を削除](#Expressions.UpdateExpressions.REMOVE)
+ [ADD — 数値とセットの更新](#Expressions.UpdateExpressions.ADD)
+ [DELETE — セットから要素を削除](#Expressions.UpdateExpressions.DELETE)
+ [複数の更新式を使用する](#Expressions.UpdateExpressions.Multiple)

## SET — 項目属性の変更または追加
<a name="Expressions.UpdateExpressions.SET"></a>

1 つ以上の属性を項目に追加するには、更新式で `SET` アクションを使用します。これらのいずれかの属性が既に存在する場合、新しい値で上書きされます。既存の属性の上書きを回避するには、`SET` で `if_not_exists` 関数を使用できます。`if_not_exists` 関数は `SET` アクションに固有で、更新式でのみ使用できます。

`SET` を使用してリスト要素を更新すると、その要素のコンテンツは、指定した新しいデータで置き換えられます。要素が既に存在していない場合、`SET` はリストの末尾に新しい要素を追加します。

1 つの `SET` オペレーションに複数の要素を追加すると、要素は要素番号で順にソートされます。

`SET` を使用して、`Number` 型である属性を増減することもできます。複数の `SET` アクションを実行するには、オペレーションをカンマで区切ります。

次の構文の概要について説明します。
+ *path* 要素は、項目へのドキュメントパスです。
+ **operand** 要素は、項目へのドキュメントパスまたは関数とすることができます。

```
set-action ::=
    path = value

value ::=
    operand
    | operand '+' operand
    | operand '-' operand

operand ::=
    path | function

function ::=
    if_not_exists (path, value)
```

項目の指定したパスに属性が含まれていない場合、`if_not_exists` は `value` に評価されます。それ以外の場合は、`path` に評価されます。

次の `PutItem` オペレーションは、例で参照するサンプル項目を作成します。

```
aws dynamodb put-item \
    --table-name ProductCatalog \
    --item file://item.json
```

`--item` の引数は、ファイル `item.json` に保存されます。(分かりやすいように、いくつかの項目属性のみが使用されます)

```
{
    "Id": {"N": "789"},
    "ProductCategory": {"S": "Home Improvement"},
    "Price": {"N": "52"},
    "InStock": {"BOOL": true},
    "Brand": {"S": "Acme"}
}
```

**Topics**
+ [属性の変更](#Expressions.UpdateExpressions.SET.ModifyingAttributes)
+ [リストおよびマップの追加](#Expressions.UpdateExpressions.SET.AddingListsAndMaps)
+ [リストに要素を追加](#Expressions.UpdateExpressions.SET.AddingListElements)
+ [ネストされたマップ属性の追加](#Expressions.UpdateExpressions.SET.AddingNestedMapAttributes)
+ [数値属性の増減](#Expressions.UpdateExpressions.SET.IncrementAndDecrement)
+ [リストに要素を追加](#Expressions.UpdateExpressions.SET.UpdatingListElements)
+ [既存の属性の上書きを防止](#Expressions.UpdateExpressions.SET.PreventingAttributeOverwrites)

### 属性の変更
<a name="Expressions.UpdateExpressions.SET.ModifyingAttributes"></a>

**Example**  
`ProductCategory` 属性および `Price` 属性を更新します。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET ProductCategory = :c, Price = :p" \
    --expression-attribute-values file://values.json \
    --return-values ALL_NEW
```
`--expression-attribute-values` の引数は、ファイル `values.json` に保存されます。  

```
{
    ":c": { "S": "Hardware" },
    ":p": { "N": "60" }
}
```

**注記**  
`UpdateItem` オペレーションでは、`--return-values ALL_NEW` を指定すると、DynamoDB は更新後に表示される項目を返します。

### リストおよびマップの追加
<a name="Expressions.UpdateExpressions.SET.AddingListsAndMaps"></a>

**Example**  
新しいリストおよび新しいマップを追加します。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET RelatedItems = :ri, ProductReviews = :pr" \
    --expression-attribute-values file://values.json \
    --return-values ALL_NEW
```
`--expression-attribute-values` の引数は、ファイル `values.json` に保存されます。  

```
{
    ":ri": {
        "L": [
            { "S": "Hammer" }
        ]
    },
    ":pr": {
        "M": {
            "FiveStar": {
                "L": [
                    { "S": "Best product ever!" }
                ]
            }
        }
    }
}
```

### リストに要素を追加
<a name="Expressions.UpdateExpressions.SET.AddingListElements"></a>

**Example**  
新しい属性を `RelatedItems` リストに追加します。リストの要素はゼロベースであるため、[0] はリスト内の最初の要素、[1] は 2 番目の要素、という順番で表されることに注意してください。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET RelatedItems[1] = :ri" \
    --expression-attribute-values file://values.json \
    --return-values ALL_NEW
```
`--expression-attribute-values` の引数は、ファイル `values.json` に保存されます。  

```
{
    ":ri": { "S": "Nails" }
}
```

**注記**  
`SET` を使用してリスト要素を更新すると、その要素のコンテンツは、指定した新しいデータで置き換えられます。要素が既に存在していない場合、`SET` はリストの末尾に新しい要素を追加します。  
1 つの `SET` オペレーションに複数の要素を追加すると、要素は要素番号で順にソートされます。

### ネストされたマップ属性の追加
<a name="Expressions.UpdateExpressions.SET.AddingNestedMapAttributes"></a>

**Example**  
ネストされたマップ属性を追加します。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET #pr.#5star[1] = :r5, #pr.#3star = :r3" \
    --expression-attribute-names file://names.json \
    --expression-attribute-values file://values.json \
    --return-values ALL_NEW
```
`--expression-attribute-names` の引数は、ファイル `names.json` に保存されます。  

```
{
    "#pr": "ProductReviews",
    "#5star": "FiveStar",
    "#3star": "ThreeStar"
}
```
`--expression-attribute-values` の引数は、ファイル `values.json` に保存されます。  

```
{
    ":r5": { "S": "Very happy with my purchase" },
    ":r3": {
        "L": [
            { "S": "Just OK - not that great" }
        ]
    }
}
```

**重要**  
親マップが存在しない場合、ネストされたマップ属性を更新することはできません。親マップ (`ProductReviews.FiveStar`) が存在しない場合にネストされた属性 (`ProductReviews` など) を更新しようとすると、DynamoDB は*「更新式で指定されたドキュメントパスは更新に無効です」*というメッセージで `ValidationException` を返します。  
ネストされたマップ属性を後で更新する項目を作成する場合は、親属性の空のマップを初期化します。例えば、次のようになります。  

```
{
    "Id": {"N": "789"},
    "ProductReviews": {"M": {}},
    "Metadata": {"M": {}}
}
```
これにより、`ProductReviews.FiveStar` のようなネストされた属性をエラーなしで更新できます。

### 数値属性の増減
<a name="Expressions.UpdateExpressions.SET.IncrementAndDecrement"></a>

既存の数値属性は増減することができます。これを行うには、`+`（プラス）および `-`（マイナス）演算子を使用します。

**Example**  
項目の `Price` を下げます。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET Price = Price - :p" \
    --expression-attribute-values '{":p": {"N":"15"}}' \
    --return-values ALL_NEW
```
`Price` を上げるために、更新式で `+` 演算子を使用します。

### リストに要素を追加
<a name="Expressions.UpdateExpressions.SET.UpdatingListElements"></a>

リストの最後に要素を追加できます。これを行うには、`SET` 関数の `list_append`を使用します。(関数名では、大文字と小文字が区別されます)。`list_append` 関数は `SET` アクションに固有で、更新式でのみ使用できます。構文は次のとおりです。
+ `list_append (list1, list2)`

この関数は、入力として 2 つのリストを受け取り、`list2` のすべての要素を ` list1` に追加します。

**Example**  
[リストに要素を追加](#Expressions.UpdateExpressions.SET.AddingListElements) で、`RelatedItems` リストを追加し `Hammer` と `Nails` の 2 つの要素を追加します。次に、`RelatedItems` の末尾にさらに 2 つの要素を追加します。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET #ri = list_append(#ri, :vals)" \
    --expression-attribute-names '{"#ri": "RelatedItems"}' \
    --expression-attribute-values file://values.json  \
    --return-values ALL_NEW
```
`--expression-attribute-values` の引数は、ファイル `values.json` に保存されます。  

```
{
    ":vals": {
        "L": [
            { "S": "Screwdriver" },
            {"S": "Hacksaw" }
        ]
    }
}
```
最後に、`RelatedItems` の*先頭*にもう 1 つの要素を追加します。これを実行するには、`list_append` 要素の順序を入れ替えます。(`list_append` は 2 つのリストを入力し、1 番目のリストに 2 番目のリストを追加することに注意してください)。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET #ri = list_append(:vals, #ri)" \
    --expression-attribute-names '{"#ri": "RelatedItems"}' \
    --expression-attribute-values '{":vals": {"L": [ { "S": "Chisel" }]}}' \
    --return-values ALL_NEW
```
生成される `RelatedItems` 属性には次の順序で 5 つの要素が含まれます。`Chisel`、`Hammer`、`Nails`、`Screwdriver`、 `Hacksaw`。

### 既存の属性の上書きを防止
<a name="Expressions.UpdateExpressions.SET.PreventingAttributeOverwrites"></a>

**Example**  
項目にすでに `Price` 属性がない場合にのみ、項目の `Price` を設定します (`Price` がすでに存在する場合は何も起こりません)。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET Price = if_not_exists(Price, :p)" \
    --expression-attribute-values '{":p": {"N": "100"}}' \
    --return-values ALL_NEW
```

## REMOVE — 項目から属性を削除
<a name="Expressions.UpdateExpressions.REMOVE"></a>

1 つ以上の属性を Amazon DynamoDB の項目から削除するには、更新式で `REMOVE` アクションを使用します。複数の `REMOVE` アクションを実行するには、オペレーションをカンマで区切ります。

更新式の `REMOVE` の構文の概要を次に示します。唯一のオペランドは、削除する属性のドキュメントパスです。

```
remove-action ::=
    path
```

**Example**  
項目から属性を削除します。(属性が存在しない場合は、何も起こりません)。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "REMOVE Brand, InStock, QuantityOnHand" \
    --return-values ALL_NEW
```

### リストから要素を削除
<a name="Expressions.UpdateExpressions.REMOVE.RemovingListElements"></a>

`REMOVE` を使用して、リストから個別要素を削除できます。

**Example**  
[リストに要素を追加](#Expressions.UpdateExpressions.SET.UpdatingListElements) で、5 つの要素を含むようにリスト属性 (`RelatedItems`) を変更します。  
+ `[0]`—`Chisel`
+ `[1]`—`Hammer`
+ `[2]`—`Nails`
+ `[3]`—`Screwdriver`
+ `[4]`—`Hacksaw`
次の AWS Command Line Interface (AWS CLI) の例では、リストから `Hammer` と `Nails` を削除します。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "REMOVE RelatedItems[1], RelatedItems[2]" \
    --return-values ALL_NEW
```
`Hammer` と `Nails` を削除すると、残りの要素は変わります。リストには以下が含まれます。  
+ `[0]`—`Chisel`
+ `[1]`—`Screwdriver`
+ `[2]`—`Hacksaw`

## ADD — 数値とセットの更新
<a name="Expressions.UpdateExpressions.ADD"></a>

**注記**  
一般的に、べき等なオペレーションを確保するために `ADD` ではなく `SET` を使用することをお勧めします。

項目に新しい属性および値を追加するには、更新式で `ADD` アクションを使用します。

属性が既に存在する場合、`ADD` の動作は属性のデータ型によって決まります。
+ 属性が数値で、追加する値も数値である場合、値は既存の属性に数学的に追加されます (値が負の数値である場合は、既存の属性から減算されます)。
+ 属性が設定され、追加する値も設定された場合、値は既存のセットに付加されます。

**注記**  
`ADD` アクションでは、数値とセットデータ型のみがサポートされます。

複数の `ADD` アクションを実行するには、オペレーションをカンマで区切ります。

次の構文の概要について説明します。
+ *path* 要素は、属性へのドキュメントパスです。属性は `Number` またはセットデータ型である必要があります。
+ *value* 要素は、属性に追加する数値 (`Number` データ型の場合)、または属性に付加するセット (セット型の場合) です。

```
add-action ::=
    path value
```

以下のトピックでは、`ADD` アクションのさまざまなユースケースについて説明します。

**Topics**
+ [数値の追加](#Expressions.UpdateExpressions.ADD.Number)
+ [セットに要素を追加](#Expressions.UpdateExpressions.ADD.Set)

### 数値の追加
<a name="Expressions.UpdateExpressions.ADD.Number"></a>

`QuantityOnHand` 属性が存在しないと想定します。次の AWS CLI の例では、`QuantityOnHand` を 5 に設定します。

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "ADD QuantityOnHand :q" \
    --expression-attribute-values '{":q": {"N": "5"}}' \
    --return-values ALL_NEW
```

これで `QuantityOnHand` が存在するようになったので、`QuantityOnHand` が毎回 5 増分するように例を再実行できます。

### セットに要素を追加
<a name="Expressions.UpdateExpressions.ADD.Set"></a>

`Color` 属性が存在しないと想定します。次の AWS CLI の例では、`Color` を 2 つの要素を持つ文字列セットに設定します。

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "ADD Color :c" \
    --expression-attribute-values '{":c": {"SS":["Orange", "Purple"]}}' \
    --return-values ALL_NEW
```

これで `Color` が存在するので、さらに要素を追加できます。

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "ADD Color :c" \
    --expression-attribute-values '{":c": {"SS":["Yellow", "Green", "Blue"]}}' \
    --return-values ALL_NEW
```

## DELETE — セットから要素を削除
<a name="Expressions.UpdateExpressions.DELETE"></a>

**重要**  
`DELETE` は `Set` データ型のみをサポートします。

1 つ以上の要素をセットから削除するには、更新式で `DELETE` アクションを使用します。複数の `DELETE` アクションを実行するには、オペレーションをカンマで区切ります。

次の構文の概要について説明します。
+ *path* 要素は、属性へのドキュメントパスです。属性はセットデータ型である必要があります。
+ *サブセット*は *path* から削除する 1 つ以上の要素です。*サブセット*はセット型として指定する必要があります。

```
delete-action ::=
    path subset
```

**Example**  
[セットに要素を追加](#Expressions.UpdateExpressions.ADD.Set) で、`Color` 文字列セットを作成します。この例では、セットから要素の一部を削除します。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "DELETE Color :p" \
    --expression-attribute-values '{":p": {"SS": ["Yellow", "Purple"]}}' \
    --return-values ALL_NEW
```

## 複数の更新式を使用する
<a name="Expressions.UpdateExpressions.Multiple"></a>

単一の更新式で複数のアクションを使用できます。すべての属性参照は、いずれかのアクションが適用される前に、項目の状態に対して解決されます。

**Example**  
項目 `{"id": "1", "a": 1, "b": 2, "c": 3}` を指定すると、次の式は `a` を削除し、`b` と `c` の値をシフトします。  

```
aws dynamodb update-item \
    --table-name test \
    --key '{"id":{"S":"1"}}' \
    --update-expression "REMOVE a SET b = a, c = b" \
    --return-values ALL_NEW
```
結果は、`{"id": "1", "b": 1, "c": 2}` です。同じ式で `a` が削除され、`b` が再割り当てされても、両方の参照は元の値に解決されます。

**Example**  
属性の値を変更して別の属性を完全に削除したい場合は、SET アクションと REMOVE アクションを 1 つのステートメントで使用できます。このオペレーションでは、`Price` 値を 15 に減らし、項目から `InStock` 属性も削除します。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET Price = Price - :p REMOVE InStock" \
    --expression-attribute-values '{":p": {"N":"15"}}' \
    --return-values ALL_NEW
```

**Example**  
リストに追加しつつ別の属性の値も変更したい場合は、1 つのステートメントで 2 つの SET アクションを使用できます。このオペレーションでは、`RelatedItems` リスト属性に「Nails」が追加され、`Price` 値も 21 に設定されます。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET RelatedItems[1] = :newValue, Price = :newPrice" \
    --expression-attribute-values '{":newValue": {"S":"Nails"}, ":newPrice": {"N":"21"}}'  \
    --return-values ALL_NEW
```

# DynamoDB の条件式とフィルター式、演算子、関数
<a name="Expressions.OperatorsAndFunctions"></a>

Amazon DynamoDB テーブルのデータを操作するには、`PutItem`、`UpdateItem`、`DeleteItem` の各オペレーションを使用します。これらのデータ操作オペレーションでは、どの項目を修正する必要があるかを判断するために、条件式を指定できます。条件式が true と評価される場合、オペレーションは成功します。それ以外の場合は、このオペレーションは失敗します。

このセクションでは、Amazon DynamoDB の書き込みフィルター式と条件式の組み込み関数およびキーワードについて説明します。DynamoDB で使用する関数とプログラミングの詳細については、「[DynamoDB と AWS SDK を使用したプログラミング](Programming.md)」と「[DynamoDB API リファレンス](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/)」を参照してください。

**Topics**
+ [フィルター式と条件式の構文](#Expressions.OperatorsAndFunctions.Syntax)
+ [比較の実行](#Expressions.OperatorsAndFunctions.Comparators)
+ [関数](#Expressions.OperatorsAndFunctions.Functions)
+ [論理評価](#Expressions.OperatorsAndFunctions.LogicalEvaluations)
+ [括弧](#Expressions.OperatorsAndFunctions.Parentheses)
+ [条件の優先順位](#Expressions.OperatorsAndFunctions.Precedence)

## フィルター式と条件式の構文
<a name="Expressions.OperatorsAndFunctions.Syntax"></a>

以下の構文の概要で、*operand* は次のいずれかです。
+ 最上位の属性名 (`Id`、`Title`、`Description`、`ProductCategory` など)
+ 入れ子の属性を参照するドキュメントパス

```
condition-expression ::=
      operand comparator operand
    | operand BETWEEN operand AND operand
    | operand IN ( operand (',' operand (, ...) ))
    | function
    | condition AND condition
    | condition OR condition
    | NOT condition
    | ( condition )

comparator ::=
    =
    | <>
    | <
    | <=
    | >
    | >=

function ::=
    attribute_exists (path)
    | attribute_not_exists (path)
    | attribute_type (path, type)
    | begins_with (path, substr)
    | contains (path, operand)
    | size (path)
```

## 比較の実行
<a name="Expressions.OperatorsAndFunctions.Comparators"></a>

これらのコンパレータを使用して、オペランドを単一の値と比較します。
+ `a = b` — *a* が *b* と等しい場合、True
+ `a <> b` — *a* が *b* と等しくない場合、True
+ `a < b` — *a* が *b* 未満の場合、True
+ `a <= b` — *a* が *b* 以下である場合、True
+ `a > b` — *a* が *b* より大きい場合、True
+ `a >= b` — *a* が *b* 以上である場合、True

値の範囲または値の列挙リストに対してオペランドを比較するには、`BETWEEN` および `IN` キーワードを使用します。
+ `a BETWEEN b AND c` - *a* が *b* 以上で、*c* 以下である場合、True。
+ `a IN (b, c, d) ` — *a* がリスト内の任意の値と等しい場合、True。例では、*b*、*c*、*d* のいずれかと等しい場合。リストには、コンマで区切って最大 100 個の値を指定できます。

## 関数
<a name="Expressions.OperatorsAndFunctions.Functions"></a>

以下の関数を使用して、ある属性が項目に存在するか判定したり、属性の値を評価したりします。これらの関数名では大文字と小文字が区別されます。入れ子の属性では、完全ドキュメントパスを指定する必要があります。


****  

| 関数 | 説明 | 
| --- | --- | 
|  `attribute_exists (path)`  | 項目に、`path` で指定した属性が含まれる場合、true。 例: `Product` テーブルの項目に側面図があるかどうかを確認します。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)  | 
|  `attribute_not_exists (path)`  | `path` で指定した属性が項目に存在しない場合、true。 例: 項目に `Manufacturer` 属性があるかどうかを確認します。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)  | 
|  `attribute_type (path, type)`  |  指定したパスの属性が、特定のデータ型のものである場合、true。`type` パラメータは次のいずれかである必要があります。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) `type` パラメータには、式の属性値を使用する必要があります。 例: `QuantityOnHand` 属性がリスト型のものであるかどうかを確認します。この例では、`:v_sub` は文字列 `L` のプレースホルダーです。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) `type` パラメータには、式の属性値を使用する必要があります。  | 
|  `begins_with (path, substr)`  |  `path` で指定された属性が特定のサブ文字列から始まる場合、true。 例: 正面図 URL の最初の数文字が `http://` かどうかを確認します。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) 式の属性値 `:v_sub` は、`http://` のプレースホルダーです。  | 
|  `contains (path, operand)`  | `path` で指定された属性が次の属性である場合、true。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) `path` で指定された属性が `String` である場合、`operand` は `String` である必要があります。`path` で指定された属性が `Set` の場合、`operand` は一連の要素タイプである必要があります。 パスとオペランドは区別する必要があります。つまり、`contains (a, a)` がエラーを返します。 例: `Brand` 属性にサブ文字列 `Company` が含まれているかどうかを確認します。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) 式の属性値 `:v_sub` は、`Company` のプレースホルダーです。 例: 製品が赤で入手可能かどうかを確認します。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html) 式の属性値 `:v_sub` は、`Red` のプレースホルダーです。 | 
|  `size (path)`  | 属性のサイズを表す数値を返します。以下は、`size` で使用できる有効なデータ型です。  属性は `String` 型で、`size` は文字列の長さを返します。 例: 文字列 `Brand` が 20 文字以下であるかどうかを確認します。式の属性値 `:v_sub` は、`20` のプレースホルダーです。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)  属性が `Binary` バイナリ型の場合、`size` は属性値のバイト数を返します。 例: `ProductCatalog` 項目に `VideoClip` という名前のバイナリ属性があるとします。この属性には使用中の製品の短いビデオが含まれます。次の式は、`VideoClip` が 64,000 バイトを超えるかどうかを確認します。式の属性値 `:v_sub` は、`64000` のプレースホルダーです。[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)  属性が `Set` データ型の場合、`size` は設定の要素数を返します。 例: 製品が複数の色で入手可能かどうかを確認します。式の属性値 `:v_sub` は、`1` のプレースホルダーです。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)  属性が `List` 型または `Map` のものである場合、`size` は子要素の数を返します。 例: `OneStar` のレビューの数が特定のしきい値を超えたかどうかを確認します。式の属性値 `:v_sub` は、`3` のプレースホルダーです。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)  | 

## 論理評価
<a name="Expressions.OperatorsAndFunctions.LogicalEvaluations"></a>

論理評価を実行するには、`AND`、`OR`、`NOT` キーワードを使用します。以下のリストでは、*a* と *b* は評価される条件を示しています。
+ `a AND b` — *a* と *b* の両方が true である場合、True。
+ `a OR b` — *a* または *b* のどちらか (または両方) が true の場合、True。
+ `NOT a`— *a* が false の場合は True。*a* が true の場合は Falsee。

以下は、オペレーションの AND のコード例です。

`dynamodb-local (*)> select * from exprtest where a > 3 and a < 5;`

## 括弧
<a name="Expressions.OperatorsAndFunctions.Parentheses"></a>

論理評価の優先順位を変更するには括弧を使用します。たとえば、条件 *a* と *b* が true で、条件 *c* が false であるとします。次の式は true と評価されます。
+ `a OR b AND c`

しかし、条件を括弧で囲むと、それが最初に評価されます。たとえば、次の式は false と評価されます。
+  `(a OR b) AND c`

**注記**  
式では括弧を入れ子にできます。最も内側の括弧が最初に評価されます。

以下は、論理評価で括弧を付けたコード例です。

`dynamodb-local (*)> select * from exprtest where attribute_type(b, string) or ( a = 5 and c = “coffee”);`

## 条件の優先順位
<a name="Expressions.OperatorsAndFunctions.Precedence"></a>

 DynamoDB では、条件は次の優先順位ルールを使用して左から右に評価されます。
+ `= <> < <= > >=`
+ `IN`
+ `BETWEEN`
+ `attribute_exists attribute_not_exists begins_with contains`
+ 括弧
+ `NOT`
+ `AND`
+ `OR`

# DynamoDB 条件式 CLI の例
<a name="Expressions.ConditionExpressions"></a>

条件式を使用する AWS Command Line Interface (AWS CLI) の例のいくつかを次に示します。これらの例は、「`ProductCatalog`」で紹介されている [DynamoDB で式を使用する場合の項目属性の参照](Expressions.Attributes.md) テーブルに基づいています。このテーブルのパーティションキーは `Id` です。ソートキーはありません。次の `PutItem` オペレーションは、例で参照するサンプル `ProductCatalog` 項目を作成します。

```
aws dynamodb put-item \
    --table-name ProductCatalog \
    --item file://item.json
```

`--item` の引数は、ファイル `item.json` に保存されます。(分かりやすいように、いくつかの項目属性のみが使用されます)

```
{
    "Id": {"N": "456" },
    "ProductCategory": {"S": "Sporting Goods" },
    "Price": {"N": "650" }
}
```

**Topics**
+ [条件付き配置](#Expressions.ConditionExpressions.PreventingOverwrites)
+ [条件付き削除](#Expressions.ConditionExpressions.AdvancedComparisons)
+ [条件付き更新](#Expressions.ConditionExpressions.SimpleComparisons)
+ [条件式の例](#Expressions.ConditionExpressions.ConditionalExamples)

## 条件付き配置
<a name="Expressions.ConditionExpressions.PreventingOverwrites"></a>

`PutItem` オペレーションはプライマリキーが同じ項目を上書きします (存在する場合)。これを回避するには、条件式を使用します。これにより、問題の項目が同じプライマリキーを持っていない場合にのみ書き込みが続行されます。

次の例では、`attribute_not_exists()` を使用して、書き込み操作を試みる前に、プライマリキーがテーブルに存在するかどうかを確認しています。

**注記**  
プライマリキーがパーティションキー (pk) とソートキー (sk) の両方で構成されている場合、このパラメータは、書き込みオペレーションを試みる前に `attribute_not_exists(pk)` と `attribute_not_exists(sk)` の両方がステートメント全体として true または false のどちらに評価されるのかを確認します。

```
aws dynamodb put-item \
    --table-name ProductCatalog \
    --item file://item.json \
    --condition-expression "attribute_not_exists(Id)"
```

条件式が false と評価し、DynamoDB は次のエラーメッセージを返します。条件付きリクエストが失敗しました。

**注記**  
`attribute_not_exists` および他の関数についての詳細は、「[DynamoDB の条件式とフィルター式、演算子、関数](Expressions.OperatorsAndFunctions.md)」を参照してください。

## 条件付き削除
<a name="Expressions.ConditionExpressions.AdvancedComparisons"></a>

条件付き削除を実行するには、条件式とともに `DeleteItem` オペレーションを使用します。条件式は、オペレーションが成功するためには true に評価される必要があります。それ以外の場合、オペレーションは失敗します。

上で定義した項目について考えてみます。

次の条件を満たしている場合にだけ項目を削除するとします。
+  `ProductCategory` は「Sporting Goods」または「Gardening Supplies」のどちらかです。
+  `Price` は 500～600 です。

次の例では、項目を削除しようとしています。

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"456"}}' \
    --condition-expression "(ProductCategory IN (:cat1, :cat2)) and (Price between :lo and :hi)" \
    --expression-attribute-values file://values.json
```

`--expression-attribute-values` の引数は、ファイル `values.json` に保存されます。

```
{
    ":cat1": {"S": "Sporting Goods"},
    ":cat2": {"S": "Gardening Supplies"},
    ":lo": {"N": "500"},
    ":hi": {"N": "600"}
}
```

**注記**  
条件式では、`:` (コロン文字) は*式の属性値* (実際の値のプレースホルダー) を示します。詳細については、「」を参照してください[DynamoDB での式属性値の使用](Expressions.ExpressionAttributeValues.md)  
`IN`、`AND` およびその他のキーワードについての詳細は、「[DynamoDB の条件式とフィルター式、演算子、関数](Expressions.OperatorsAndFunctions.md)」を参照してください。

この例では、`ProductCategory` 比較の評価結果は true になりますが、`Price` 比較は false と評価されます。これにより、条件式は false と評価され、`DeleteItem` オペレーションは失敗します。

## 条件付き更新
<a name="Expressions.ConditionExpressions.SimpleComparisons"></a>

条件付き更新を実行するには、条件式とともに `UpdateItem` オペレーションを使用します。条件式は、オペレーションが成功するためには true に評価される必要があります。それ以外の場合、オペレーションは失敗します。

**注記**  
`UpdateItem` は*更新式*もサポートします。項目に加える変更を指定する変更内容を指定します。詳細については、「[DynamoDB での更新式の使用](Expressions.UpdateExpressions.md)」を参照してください。

上で定義した項目から開始するとします。

次の例では、`UpdateItem` オペレーションを実行します。製品の `Price` を 75 減らしようとしていますが、現在の `Price` が 500 以下の場合、条件式が更新を防ぎます。

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --update-expression "SET Price = Price - :discount" \
    --condition-expression "Price > :limit" \
    --expression-attribute-values file://values.json
```

`--expression-attribute-values` の引数は、ファイル `values.json` に保存されます。

```
{
    ":discount": { "N": "75"},
    ":limit": {"N": "500"}
}
```

開始 `Price` が 650 の場合、`UpdateItem` オペレーションによって `Price` は 575 に下げられます。`UpdateItem` アクションを再度実行すると、`Price` は 500 に減らされます。3 度目に実行した場合、条件式は false と評価されて、更新は失敗します。

**注記**  
条件式では、`:` (コロン文字) は*式の属性値* (実際の値のプレースホルダー) を示します。詳細については、「[DynamoDB での式属性値の使用](Expressions.ExpressionAttributeValues.md)」を参照してください。  
「*>*」およびその他の演算子についての詳細は、「[DynamoDB の条件式とフィルター式、演算子、関数](Expressions.OperatorsAndFunctions.md)」を参照してください。

## 条件式の例
<a name="Expressions.ConditionExpressions.ConditionalExamples"></a>

次の例で使用される関数の詳細については、「[DynamoDB の条件式とフィルター式、演算子、関数](Expressions.OperatorsAndFunctions.md)」を参照してください。式で異なる属性タイプを指定する方法の詳細については、「[DynamoDB で式を使用する場合の項目属性の参照](Expressions.Attributes.md)」を参照してください。

### 項目の属性を確認
<a name="Expressions.ConditionExpressions.CheckingForAttributes"></a>

属性が存在するか (または存在しないか) を確認できます。条件式が true と評価される場合、オペレーションは成功で、それ以外の場合、オペレーションは失敗です。

次の例では、`attribute_not_exists` 属性がない場合にのみ製品を削除するために `Price` を使用します。

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --condition-expression "attribute_not_exists(Price)"
```

DynamoDB は `attribute_exists` 関数も提供します。次の例では、悪いレビューを受け取った場合のみ製品を削除します。

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --condition-expression "attribute_exists(ProductReviews.OneStar)"
```

### 属性タイプの確認
<a name="Expressions.ConditionExpressions.CheckingForAttributeType"></a>

属性値のデータ型は、`attribute_type` 関数を使用して確認できます。条件式が true と評価される場合、オペレーションは成功で、それ以外の場合、オペレーションは失敗です。

次の例では、`attribute_type` を使用して、文字列セットタイプの `Color` 属性がある場合にのみ、製品を削除します。

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --condition-expression "attribute_type(Color, :v_sub)" \
    --expression-attribute-values file://expression-attribute-values.json
```

`--expression-attribute-values` の引数は、expression-attribute-values.json ファイルに格納されます。

```
{
    ":v_sub":{"S":"SS"}
}
```

### 文字列開始値の確認
<a name="Expressions.ConditionExpressions.CheckingBeginsWith"></a>

文字列属性値が特定のサブ文字列で始まるかどうかを確認するには、`begins_with` 関数を使用します。条件式が true と評価される場合、オペレーションは成功で、それ以外の場合、オペレーションは失敗です。

次の例では、`begins_with` マップの `FrontView` 要素が特定の値で始まる場合にのみ、`Pictures` を使用して製品を削除します。

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --condition-expression "begins_with(Pictures.FrontView, :v_sub)" \
    --expression-attribute-values file://expression-attribute-values.json
```

`--expression-attribute-values` の引数は、expression-attribute-values.json ファイルに格納されます。

```
{
    ":v_sub":{"S":"http://"}
}
```

### セット内の要素の確認
<a name="Expressions.ConditionExpressions.CheckingForContains"></a>

`contains` 関数を使用して、セット内の要素を確認したり、文字列内のサブ文字列を検索したりできます。条件式が true と評価される場合、オペレーションは成功で、それ以外の場合、オペレーションは失敗です。

次の例では、`contains` 文字列セットに特定の値を持つ要素がある場合にのみ、`Color` を使用して製品を削除します。

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --condition-expression "contains(Color, :v_sub)" \
    --expression-attribute-values file://expression-attribute-values.json
```

`--expression-attribute-values` の引数は、expression-attribute-values.json ファイルに格納されます。

```
{
    ":v_sub":{"S":"Red"}
}
```

### 属性値のサイズの確認
<a name="Expressions.ConditionExpressions.CheckingForSize"></a>

属性値のサイズを確認するには、`size` 関数を使用します。条件式が true と評価される場合、オペレーションは成功で、それ以外の場合、オペレーションは失敗です。

次の例では、`size` バイナリ属性のサイズが `VideoClip` バイトより大きい場合にのみ `64000` を使用して、製品を削除します。

```
aws dynamodb delete-item \
    --table-name ProductCatalog \
    --key '{"Id": {"N": "456"}}' \
    --condition-expression "size(VideoClip) > :v_sub" \
    --expression-attribute-values file://expression-attribute-values.json
```

`--expression-attribute-values` の引数は、expression-attribute-values.json ファイルに格納されます。

```
{
    ":v_sub":{"N":"64000"}
}
```

# DynamoDB での Time to Live (TTL) の使用
<a name="TTL"></a>

DynamoDB の Time to Live (TTL) は、不要になった項目を削除するためのコスト効率に優れた方法です。TTL では、項目がいつ不要になるかを示す有効期限タイムスタンプを項目ごとに定義できます。DynamoDB は、書き込みスループットを消費することなく、有効期限が切れてから数日以内に期限切れの項目を自動的に削除します。

TTL を使用するには、まずテーブルで TTL を有効にし、次に TTL の有効期限タイムスタンプを格納する特定の属性を定義します。タイムスタンプは、秒単位の [UNIX エポック時間形式](https://en.wikipedia.org/wiki/Unix_time)の[数値](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes)データ型として保存する必要があります。数値型ではない TTL 属性を持つ項目は、TTL プロセスによって無視されます。項目が作成または更新されるたびに、有効期限を計算して TTL 属性に保存できます。

期限切れの有効な TTL 属性を持つ項目は、随時システムによって削除される可能性があります。削除は、通常は有効期限が切れてから数日以内に行われます。削除待ちの期限切れ項目は、TTL 属性の変更や削除を含め、引き続き更新できます。期限切れの項目を更新する際には、その項目がその後削除されないように条件式を使用することをおすすめします。フィルター式を使用して、期限切れの項目を [Scan](Scan.md#Scan.FilterExpression) 結果と [Query](Query.FilterExpression.md) 結果から削除します。

削除済みの項目は、通常の削除操作で削除された項目と同様に機能します。削除すると、項目はユーザーによる削除ではなくサービスによる削除として DynamoDB ストリームに入り、他の削除操作と同様にローカルセカンダリインデックスとグローバルセカンダリインデックスから削除されます。

[グローバルテーブルバージョン 2019.11.21 (現行)](GlobalTables.md) を使用しており、TTL 機能も使用している場合、DynamoDB は TTL による削除をすべてのレプリカテーブルにレプリケートします。最初の TTL による削除の場合、TTL の有効期限切れが発生したリージョンでは、書き込みキャパシティユニット (WCU) は消費されません。ただし、レプリカテーブルにレプリケートされた TTL 削除では、プロビジョンドキャパシティを使用する場合はレプリケートされた書き込みキャパシティユニットが消費され、オンデマンドのキャパシティモードを使用する場合はレプリケートされた書き込みユニットが各レプリカリージョンで消費され、該当する料金が適用されます。

TTL の詳細については、次のトピックを参照してください。

**Topics**
+ [DynamoDB での Time to Live (TTL) の有効化](time-to-live-ttl-how-to.md)
+ [DynamoDB での Time to Live (TTL) の計算](time-to-live-ttl-before-you-start.md)
+ [期限切れのアイテムと Time to Live (TTL) の使用](ttl-expired-items.md)

# DynamoDB での Time to Live (TTL) の有効化
<a name="time-to-live-ttl-how-to"></a>

**注記**  
TTL 機能の適切なオペレーションのデバッグと検証を支援するために、項目 TTL に指定した値は DynamoDB 診断ログにプレーンテキストとして記録されます。

TTL を有効にするには、Amazon DynamoDB コンソールまたは AWS Command Line Interface (AWS CLI) を使用するか、「[Amazon DynamoDB API リファレンス](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/)」の該当する AWS SDK のいずれかを使用できます。すべてのパーティションで TTL を有効にするには約 1 時間かかります。

## AWS コンソールを使用して DynamoDB TTL を有効にする
<a name="time-to-live-ttl-how-to-enable-console"></a>

1. AWS マネジメントコンソール にサインインして DynamoDB コンソール ([https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/)) を開きます。

1. [**テーブル**] を選択し、変更するテーブルを選択します。

1. **[追加の設定]** タブの **[Time to Live (TTL)]** セクションで、**[オンにする]** を選択します。

1. テーブルで TTL を有効にする場合、DynamoDB では、項目が失効可能かどうかを判断するときにサービスが検索する、特定の属性名を識別する必要があります。以下に示す TTL 属性名では大文字と小文字が区別されます。また、TTL 属性名は読み取り/書き込み操作で定義された属性と一致する必要があります。不一致があると、期限切れの項目は削除されなくなります。TTL 属性の名前を変更するには、TTL を無効にした後で、新しい属性で TTL を再度有効にする必要があります。TTL は、無効化されてから約 30 分間、削除処理を続けます。TTL は復元されたテーブルで再設定する必要があります。  
![\[DynamoDB が項目の有効期間を決定するために使用する、大文字と小文字が区別される TTL 属性名。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/EnableTTL-Settings.png)

1. (オプション) 有効期限の日付と時刻をシミュレートし、いくつかの項目を照合することでテストを実行できます。これにより、項目のサンプルリストが提供され、指定した TTL 属性名と有効期限を含む項目があることが確認されます。

TTL を有効にすると、DynamoDB コンソールで項目を表示するときに TTL 属性に **TTL** のマークが付けられます。属性の上にポインターを合わせると、項目が期限切れになる日時を表示できます。

## API を使用して DynamoDB TTL を有効にする
<a name="time-to-live-ttl-how-to-enable-api"></a>

------
#### [ Python ]

コードで [UpdateTimeToLive](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/client/update_time_to_live.html) オペレーションを使用して TTL を有効にすることができます。

```
import boto3


def enable_ttl(table_name, ttl_attribute_name):
    """
    Enables TTL on DynamoDB table for a given attribute name
        on success, returns a status code of 200
        on error, throws an exception

    :param table_name: Name of the DynamoDB table
    :param ttl_attribute_name: The name of the TTL attribute being provided to the table.
    """
    try:
        dynamodb = boto3.client('dynamodb')

        # Enable TTL on an existing DynamoDB table
        response = dynamodb.update_time_to_live(
            TableName=table_name,
            TimeToLiveSpecification={
                'Enabled': True,
                'AttributeName': ttl_attribute_name
            }
        )

        # In the returned response, check for a successful status code.
        if response['ResponseMetadata']['HTTPStatusCode'] == 200:
            print("TTL has been enabled successfully.")
        else:
            print(f"Failed to enable TTL, status code {response['ResponseMetadata']['HTTPStatusCode']}")
    except Exception as ex:
        print("Couldn't enable TTL in table %s. Here's why: %s" % (table_name, ex))
        raise


# your values
enable_ttl('your-table-name', 'expirationDate')
```

TTL が有効になっていることを確認するには、[DescribeTimeToLive](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/client/describe_time_to_live.html) オペレーションを使用してテーブルの TTL ステータスを記述します。`TimeToLive` ステータスは `ENABLED` または `DISABLED` です。

```
# create a DynamoDB client
dynamodb = boto3.client('dynamodb')

# set the table name
table_name = 'YourTable'

# describe TTL
response = dynamodb.describe_time_to_live(TableName=table_name)
```

------
#### [ JavaScript ]

コードで [UpdateTimeToLiveCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-dynamodb/Class/UpdateTimeToLiveCommand/) オペレーションを使用して TTL を有効にすることができます。

```
import { DynamoDBClient, UpdateTimeToLiveCommand } from "@aws-sdk/client-dynamodb";

const enableTTL = async (tableName, ttlAttribute) => {

    const client = new DynamoDBClient({});

    const params = {
        TableName: tableName,
        TimeToLiveSpecification: {
            Enabled: true,
            AttributeName: ttlAttribute
        }
    };

    try {
        const response = await client.send(new UpdateTimeToLiveCommand(params));
        if (response.$metadata.httpStatusCode === 200) {
            console.log(`TTL enabled successfully for table ${tableName}, using attribute name ${ttlAttribute}.`);
        } else {
            console.log(`Failed to enable TTL for table ${tableName}, response object: ${response}`);
        }
        return response;
    } catch (e) {
        console.error(`Error enabling TTL: ${e}`);
        throw e;
    }
};

// call with your own values
enableTTL('ExampleTable', 'exampleTtlAttribute');
```

------

## AWS CLI を使用して Time to Live を有効にする
<a name="time-to-live-ttl-how-to-enable-cli-sdk"></a>

1. `TTLExample` テーブルで TTL を有効にします。

   ```
   aws dynamodb update-time-to-live --table-name TTLExample --time-to-live-specification "Enabled=true, AttributeName=ttl"
   ```

1. `TTLExample` テーブルで TTL を無効にします。

   ```
   aws dynamodb describe-time-to-live --table-name TTLExample
   {
       "TimeToLiveDescription": {
           "AttributeName": "ttl",
           "TimeToLiveStatus": "ENABLED"
       }
   }
   ```

1. BASH シェルおよび AWS CLI を使用して 有効期限 (TTL) 属性が設定された項目を `TTLExample` テーブルに追加するには: 

   ```
   EXP=`date -d '+5 days' +%s`
   aws dynamodb put-item --table-name "TTLExample" --item '{"id": {"N": "1"}, "ttl": {"N": "'$EXP'"}}'
   ```

この例では、現在の日付から開始し、有効期限を作成するために 5 日を追加します。次に、有効期限をエポック時間形式に変換してから、最後に項目を「`TTLExample`」テーブルに追加します。

**注記**  
 有効期限 (TTL) の有効期限切れの値を設定する 1 つの方法は、有効期限に追加する秒数を計算することです。たとえば、5 日間は 432,000 秒です。ただし、多くの場合は最初に日付で開始し、そこから作業を開始することをお勧めします。

次の例のように、現在の時間をエポック時間形式で取得するのは非常に簡単です。
+ Linux ターミナル: `date +%s`
+ Python: `import time; int(time.time())`
+ Java: `System.currentTimeMillis() / 1000L`
+ JavaScript: `Math.floor(Date.now() / 1000)`

## CloudFormation を使用して DynamoDB TTL を有効にします。
<a name="time-to-live-ttl-how-to-enable-cf"></a>

```
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  TTLExampleTable:
    Type: AWS::DynamoDB::Table
    Description: "A DynamoDB table with TTL Specification enabled"
    Properties:
      AttributeDefinitions:
        - AttributeName: "Album"
          AttributeType: "S"
        - AttributeName: "Artist"
          AttributeType: "S"
      KeySchema:
        - AttributeName: "Album"
          KeyType: "HASH"
        - AttributeName: "Artist"
          KeyType: "RANGE"
      ProvisionedThroughput:
        ReadCapacityUnits: "5"
        WriteCapacityUnits: "5"
      TimeToLiveSpecification:
        AttributeName: "TTLExampleAttribute"
        Enabled: true
```

CloudFormation テンプレート内で TTL を使用する方法の詳細については、[こちら](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-table-timetolivespecification.html)を参照してください。

# DynamoDB での Time to Live (TTL) の計算
<a name="time-to-live-ttl-before-you-start"></a>

TTL を実装する一般的な方法は、作成日または最終更新日に基づいて項目の有効期限を設定することです。これは、`createdAt` タイムスタンプと `updatedAt` タイムスタンプに時間を追加することで実現できます。例えば、新しく作成されたアイテムの TTL を `createdAt` \$1 90 日に設定できます。項目が更新されると、TTL は `updatedAt` \$1 90 日に再計算されます。

計算された有効期限は、秒単位のエポック形式である必要があります。TTL が 5 年を超えて経過すると、有効期限と削除の対象外になります。他の形式を使用すると、TTL プロセスはその項目を無視します。項目の有効期限を未来の時点に設定した場合、その期間が過ぎると項目は期限切れになります。例えば、有効期限を 1724241326 (2024 年 8 月 21 日 (月) 11:55:26 (UTC)) に設定したとします。指定した時刻以降、項目は有効期限切れになります。最小 TTL 期間はありません。有効期限は、現在の時刻から 5 分後など、将来の任意の時刻に設定できます。ただし、DynamoDB は通常、期限切れの項目の有効期限が切れた直後ではなく、有効期限が切れてから 48 時間以内に削除します。

**Topics**
+ [項目を作成して Time to Live を設定する](#time-to-live-ttl-before-you-start-create)
+ [項目を更新して Time to Live を再計算する](#time-to-live-ttl-before-you-start-update)

## 項目を作成して Time to Live を設定する
<a name="time-to-live-ttl-before-you-start-create"></a>

次の例は、`expireAt` を TTL 属性名として使用して、新しいアイテムを作成するときに有効期限を計算する方法を示しています。代入ステートメントは、現在の時刻を変数として取得します。この例では、有効期限は現在の時刻から 90 日後として計算されます。その後、時刻はエポック形式に変換され、TTL 属性に整数データ型として保存されます。

次のコード例は、TTL を含む項目の作成方法を示しています。

------
#### [ Java ]

**SDK for Java 2.x**  

```
package com.amazon.samplelib.ttl;

import com.amazon.samplelib.CodeSampleUtils;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import software.amazon.awssdk.services.dynamodb.model.PutItemResponse;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * Creates an item in a DynamoDB table with TTL attributes.
 * This class demonstrates how to add TTL expiration timestamps to DynamoDB items.
 */
public class CreateTTL {

    private static final String USAGE =
        """
            Usage:
                <tableName> <primaryKey> <sortKey> <region>
            Where:
                tableName - The Amazon DynamoDB table being queried.
                primaryKey - The name of the primary key. Also known as the hash or partition key.
                sortKey - The name of the sort key. Also known as the range attribute.
                region (optional) - The AWS region that the Amazon DynamoDB table is located in. (Default: us-east-1)
            """;
    private static final int DAYS_TO_EXPIRE = 90;
    private static final int SECONDS_PER_DAY = 24 * 60 * 60;
    private static final String PRIMARY_KEY_ATTR = "primaryKey";
    private static final String SORT_KEY_ATTR = "sortKey";
    private static final String CREATION_DATE_ATTR = "creationDate";
    private static final String EXPIRE_AT_ATTR = "expireAt";
    private static final String SUCCESS_MESSAGE = "%s PutItem operation with TTL successful.";
    private static final String TABLE_NOT_FOUND_ERROR = "Error: The Amazon DynamoDB table \"%s\" can't be found.";

    private final DynamoDbClient dynamoDbClient;

    /**
     * Constructs a CreateTTL instance with the specified DynamoDB client.
     *
     * @param dynamoDbClient The DynamoDB client to use
     */
    public CreateTTL(final DynamoDbClient dynamoDbClient) {
        this.dynamoDbClient = dynamoDbClient;
    }

    /**
     * Constructs a CreateTTL with a default DynamoDB client.
     */
    public CreateTTL() {
        this.dynamoDbClient = null;
    }

    /**
     * Main method to demonstrate creating an item with TTL.
     *
     * @param args Command line arguments
     */
    public static void main(final String[] args) {
        try {
            int result = new CreateTTL().processArgs(args);
            System.exit(result);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }

    /**
     * Process command line arguments and create an item with TTL.
     *
     * @param args Command line arguments
     * @return 0 if successful, non-zero otherwise
     * @throws ResourceNotFoundException If the table doesn't exist
     * @throws DynamoDbException If an error occurs during the operation
     * @throws IllegalArgumentException If arguments are invalid
     */
    public int processArgs(final String[] args) {
        // Argument validation (remove or replace this line when reusing this code)
        CodeSampleUtils.validateArgs(args, new int[] {3, 4}, USAGE);

        final String tableName = args[0];
        final String primaryKey = args[1];
        final String sortKey = args[2];
        final Region region = Optional.ofNullable(args.length > 3 ? args[3] : null)
            .map(Region::of)
            .orElse(Region.US_EAST_1);

        try (DynamoDbClient ddb = dynamoDbClient != null
            ? dynamoDbClient
            : DynamoDbClient.builder().region(region).build()) {
            final CreateTTL createTTL = new CreateTTL(ddb);
            createTTL.createItemWithTTL(tableName, primaryKey, sortKey);
            return 0;
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * Creates an item in the specified table with TTL attributes.
     *
     * @param tableName The name of the table
     * @param primaryKeyValue The value for the primary key
     * @param sortKeyValue The value for the sort key
     * @return The response from the PutItem operation
     * @throws ResourceNotFoundException If the table doesn't exist
     * @throws DynamoDbException If an error occurs during the operation
     */
    public PutItemResponse createItemWithTTL(
        final String tableName, final String primaryKeyValue, final String sortKeyValue) {
        // Get current time in epoch second format
        final long createDate = System.currentTimeMillis() / 1000;

        // Calculate expiration time 90 days from now in epoch second format
        final long expireDate = createDate + (DAYS_TO_EXPIRE * SECONDS_PER_DAY);

        final Map<String, AttributeValue> itemMap = new HashMap<>();
        itemMap.put(
            PRIMARY_KEY_ATTR, AttributeValue.builder().s(primaryKeyValue).build());
        itemMap.put(SORT_KEY_ATTR, AttributeValue.builder().s(sortKeyValue).build());
        itemMap.put(
            CREATION_DATE_ATTR,
            AttributeValue.builder().n(String.valueOf(createDate)).build());
        itemMap.put(
            EXPIRE_AT_ATTR,
            AttributeValue.builder().n(String.valueOf(expireDate)).build());

        final PutItemRequest request =
            PutItemRequest.builder().tableName(tableName).item(itemMap).build();

        try {
            final PutItemResponse response = dynamoDbClient.putItem(request);
            System.out.println(String.format(SUCCESS_MESSAGE, tableName));
            return response;
        } catch (ResourceNotFoundException e) {
            System.err.format(TABLE_NOT_FOUND_ERROR, tableName);
            throw e;
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            throw e;
        }
    }
}
```
+  API の詳細については、AWS SDK for Java 2.x API リファレンスの「[PutItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/PutItem)」を参照してください。**

------
#### [ JavaScript ]

**SDK for JavaScript (v3)**  

```
import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";

export function createDynamoDBItem(table_name, region, partition_key, sort_key) {
    const client = new DynamoDBClient({
        region: region,
        endpoint: `https://dynamodb.${region}.amazonaws.com`
    });

    // Get the current time in epoch second format
    const current_time = Math.floor(new Date().getTime() / 1000);

    // Calculate the expireAt time (90 days from now) in epoch second format
    const expire_at = Math.floor((new Date().getTime() + 90 * 24 * 60 * 60 * 1000) / 1000);

    // Create DynamoDB item
    const item = {
        'partitionKey': {'S': partition_key},
        'sortKey': {'S': sort_key},
        'createdAt': {'N': current_time.toString()},
        'expireAt': {'N': expire_at.toString()}
    };

    const putItemCommand = new PutItemCommand({
        TableName: table_name,
        Item: item,
        ProvisionedThroughput: {
            ReadCapacityUnits: 1,
            WriteCapacityUnits: 1,
        },
    });

    client.send(putItemCommand, function(err, data) {
        if (err) {
            console.log("Exception encountered when creating item %s, here's what happened: ", data, err);
            throw err;
        } else {
            console.log("Item created successfully: %s.", data);
            return data;
        }
    });
}

// Example usage (commented out for testing)
// createDynamoDBItem('your-table-name', 'us-east-1', 'your-partition-key-value', 'your-sort-key-value');
```
+  API の詳細については、*AWS SDK for JavaScript API リファレンス*の「[PutItem](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/PutItemCommand)」を参照してください。

------
#### [ Python ]

**SDK for Python (Boto3)**  

```
from datetime import datetime, timedelta

import boto3


def create_dynamodb_item(table_name, region, primary_key, sort_key):
    """
    Creates a DynamoDB item with an attached expiry attribute.

    :param table_name: Table name for the boto3 resource to target when creating an item
    :param region: string representing the AWS region. Example: `us-east-1`
    :param primary_key: one attribute known as the partition key.
    :param sort_key: Also known as a range attribute.
    :return: Void (nothing)
    """
    try:
        dynamodb = boto3.resource("dynamodb", region_name=region)
        table = dynamodb.Table(table_name)

        # Get the current time in epoch second format
        current_time = int(datetime.now().timestamp())

        # Calculate the expiration time (90 days from now) in epoch second format
        expiration_time = int((datetime.now() + timedelta(days=90)).timestamp())

        item = {
            "primaryKey": primary_key,
            "sortKey": sort_key,
            "creationDate": current_time,
            "expireAt": expiration_time,
        }
        response = table.put_item(Item=item)

        print("Item created successfully.")
        return response
    except Exception as e:
        print(f"Error creating item: {e}")
        raise e


# Use your own values
create_dynamodb_item(
    "your-table-name", "us-west-2", "your-partition-key-value", "your-sort-key-value"
)
```
+  API の詳細については、「*AWS SDK for Python (Boto3) API リファレンス*」の「[PutItem](https://docs.aws.amazon.com/goto/boto3/dynamodb-2012-08-10/PutItem)」を参照してください。

------

## 項目を更新して Time to Live を再計算する
<a name="time-to-live-ttl-before-you-start-update"></a>

この例は、[前のセクション](#time-to-live-ttl-before-you-start-create)の例の続きです。項目が更新された場合、有効期限を再計算できます。次の例では、`expireAt` タイムスタンプを現在の時刻から 90 日後として再計算します。

以下のコード例は、項目の TTL の更新方法を示しています。

------
#### [ Java ]

**SDK for Java 2.x**  
テーブル内の既存の DynamoDB 項目の TTL を更新します。  

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

    public UpdateItemResponse updateItemWithTTL(
        final String tableName, final String primaryKeyValue, final String sortKeyValue) {
        // Get current time in epoch second format
        final long currentTime = System.currentTimeMillis() / 1000;

        // Calculate expiration time 90 days from now in epoch second format
        final long expireDate = currentTime + (DAYS_TO_EXPIRE * SECONDS_PER_DAY);

        // Create the key map for the item to update
        final Map<String, AttributeValue> keyMap = new HashMap<>();
        keyMap.put(PRIMARY_KEY_ATTR, AttributeValue.builder().s(primaryKeyValue).build());
        keyMap.put(SORT_KEY_ATTR, AttributeValue.builder().s(sortKeyValue).build());

        // Create the expression attribute values
        final Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
        expressionAttributeValues.put(
            ":c", AttributeValue.builder().n(String.valueOf(currentTime)).build());
        expressionAttributeValues.put(
            ":e", AttributeValue.builder().n(String.valueOf(expireDate)).build());

        final UpdateItemRequest request = UpdateItemRequest.builder()
            .tableName(tableName)
            .key(keyMap)
            .updateExpression(UPDATE_EXPRESSION)
            .expressionAttributeValues(expressionAttributeValues)
            .build();

        try {
            final UpdateItemResponse response = dynamoDbClient.updateItem(request);
            System.out.println(String.format(SUCCESS_MESSAGE, tableName));
            return response;
        } catch (ResourceNotFoundException e) {
            System.err.format(TABLE_NOT_FOUND_ERROR, tableName);
            throw e;
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            throw e;
        }
    }
```
+  API の詳細については、AWS SDK for Java 2.x API リファレンスの**「[UpdateItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/UpdateItem)」を参照してください。

------
#### [ JavaScript ]

**SDK for JavaScript (v3)**  

```
import { DynamoDBClient, UpdateItemCommand } from "@aws-sdk/client-dynamodb";
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";

export const updateItem = async (tableName, partitionKey, sortKey, region = 'us-east-1') => {
    const client = new DynamoDBClient({
        region: region,
        endpoint: `https://dynamodb.${region}.amazonaws.com`
    });

    const currentTime = Math.floor(Date.now() / 1000);
    const expireAt = Math.floor((Date.now() + 90 * 24 * 60 * 60 * 1000) / 1000);

    const params = {
        TableName: tableName,
        Key: marshall({
            partitionKey: partitionKey,
            sortKey: sortKey
        }),
        UpdateExpression: "SET updatedAt = :c, expireAt = :e",
        ExpressionAttributeValues: marshall({
            ":c": currentTime,
            ":e": expireAt
        }),
    };

    try {
        const data = await client.send(new UpdateItemCommand(params));
        const responseData = unmarshall(data.Attributes);
        console.log("Item updated successfully: %s", responseData);
        return responseData;
    } catch (err) {
        console.error("Error updating item:", err);
        throw err;
    }
}

// Example usage (commented out for testing)
// updateItem('your-table-name', 'your-partition-key-value', 'your-sort-key-value');
```
+  API の詳細については、*AWS SDK for JavaScript API リファレンス*の「[UpdateItem](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/UpdateItemCommand)」を参照してください。

------
#### [ Python ]

**SDK for Python (Boto3)**  

```
from datetime import datetime, timedelta

import boto3


def update_dynamodb_item(table_name, region, primary_key, sort_key):
    """
    Update an existing DynamoDB item with a TTL.
    :param table_name: Name of the DynamoDB table
    :param region: AWS Region of the table - example `us-east-1`
    :param primary_key: one attribute known as the partition key.
    :param sort_key: Also known as a range attribute.
    :return: Void (nothing)
    """
    try:
        # Create the DynamoDB resource.
        dynamodb = boto3.resource("dynamodb", region_name=region)
        table = dynamodb.Table(table_name)

        # Get the current time in epoch second format
        current_time = int(datetime.now().timestamp())

        # Calculate the expireAt time (90 days from now) in epoch second format
        expire_at = int((datetime.now() + timedelta(days=90)).timestamp())

        table.update_item(
            Key={"partitionKey": primary_key, "sortKey": sort_key},
            UpdateExpression="set updatedAt=:c, expireAt=:e",
            ExpressionAttributeValues={":c": current_time, ":e": expire_at},
        )

        print("Item updated successfully.")
    except Exception as e:
        print(f"Error updating item: {e}")


# Replace with your own values
update_dynamodb_item(
    "your-table-name", "us-west-2", "your-partition-key-value", "your-sort-key-value"
)
```
+  API の詳細については、[AWS SDK for Python (Boto3) API リファレンス](https://docs.aws.amazon.com/goto/boto3/dynamodb-2012-08-10/UpdateItem)の「*UpdateItem*」を参照してください。

------

この概要で説明した TTL の例は、最近更新された項目だけをテーブルに保持する方法を示しています。更新された項目は有効期限が延びますが、作成後に更新されなかった項目は有効期限が切れて無料で削除されるため、ストレージが削減され、テーブルが整理されます。

# 期限切れのアイテムと Time to Live (TTL) の使用
<a name="ttl-expired-items"></a>

削除が保留になっている期限切れのアイテムは、読み取り操作と書き込み操作の対象から除外できます。これは、期限切れのデータが無効になり、使用すべきではなくなった場合に役に立ちます。フィルターが適用されていないと、バックグラウンドプロセスによって削除されるまで、読み取り/書き込み操作で引き続き表示されます。

**注記**  
これらのアイテムは、削除されるまではストレージコストと読み取りコストにカウントされます。

TTL 削除は DynamoDB ストリームで確認できますが、削除が行われたリージョンでのみ確認できます。グローバルテーブルリージョンにレプリケートされた TTL 削除は、削除がレプリケートされたリージョンの DynamoDB ストリームでは確認できません。

## 期限切れの項目を読み取り操作から除外する
<a name="ttl-expired-items-filter"></a>

[Scan](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html) や [Query](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) などの読み取り操作では、フィルター式を使用して削除待ちの期限切れアイテムを除外できます。次のコードスニペットに示すように、フィルター式は TTL 時間が現在の時刻と同等または未満の項目を除外できます。例えば、Python SDK コードには、現在の時刻を変数 (`now`) として取得し、それをエポック時間形式の `int` に変換する代入ステートメントが含まれています。

以下のコード例は、TTL 項目のクエリの開始方法を示しています。

------
#### [ Java ]

**SDK for Java 2.x**  
AWS SDK for Java 2.x を使用しフィルタリングされた式をクエリして、DynamoDB テーブル内の TTL 項目を収集します。  

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.QueryRequest;
import software.amazon.awssdk.services.dynamodb.model.QueryResponse;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;

import java.util.Map;
import java.util.Optional;

        final QueryRequest request = QueryRequest.builder()
            .tableName(tableName)
            .keyConditionExpression(KEY_CONDITION_EXPRESSION)
            .filterExpression(FILTER_EXPRESSION)
            .expressionAttributeNames(expressionAttributeNames)
            .expressionAttributeValues(expressionAttributeValues)
            .build();

        try (DynamoDbClient ddb = dynamoDbClient != null
            ? dynamoDbClient
            : DynamoDbClient.builder().region(region).build()) {
            final QueryResponse response = ddb.query(request);
            System.out.println("Query successful. Found " + response.count() + " items that have not expired yet.");

            // Print each item
            response.items().forEach(item -> {
                System.out.println("Item: " + item);
            });

            return 0;
        } catch (ResourceNotFoundException e) {
            System.err.format(TABLE_NOT_FOUND_ERROR, tableName);
            throw e;
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            throw e;
        }
```
+  API の詳細については、*AWS SDK for Java 2.x API リファレンス*の「[Query](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/Query)」を参照してください。

------
#### [ JavaScript ]

**SDK for JavaScript (v3)**  
AWS SDK for JavaScript を使用しフィルタリングされた式をクエリして、DynamoDB テーブル内の TTL 項目を収集します。  

```
import { DynamoDBClient, QueryCommand } from "@aws-sdk/client-dynamodb";
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";

export const queryFiltered = async (tableName, primaryKey, region = 'us-east-1') => {
    const client = new DynamoDBClient({
        region: region,
        endpoint: `https://dynamodb.${region}.amazonaws.com`
    });

    const currentTime = Math.floor(Date.now() / 1000);

    const params = {
        TableName: tableName,
        KeyConditionExpression: "#pk = :pk",
        FilterExpression: "#ea > :ea",
        ExpressionAttributeNames: {
            "#pk": "primaryKey",
            "#ea": "expireAt"
        },
        ExpressionAttributeValues: marshall({
            ":pk": primaryKey,
            ":ea": currentTime
        })
    };

    try {
        const { Items } = await client.send(new QueryCommand(params));
        Items.forEach(item => {
            console.log(unmarshall(item))
        });
        return Items;
    } catch (err) {
        console.error(`Error querying items: ${err}`);
        throw err;
    }
}

// Example usage (commented out for testing)
// queryFiltered('your-table-name', 'your-partition-key-value');
```
+  API の詳細については、*AWS SDK for JavaScript API リファレンス*の「[Query](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/QueryCommand)」を参照してください。

------
#### [ Python ]

**SDK for Python (Boto3)**  
AWS SDK for Python (Boto3) を使用しフィルタリングされた式をクエリして、DynamoDB テーブル内の TTL 項目を収集します。  

```
from datetime import datetime

import boto3


def query_dynamodb_items(table_name, partition_key):
    """

    :param table_name: Name of the DynamoDB table
    :param partition_key:
    :return:
    """
    try:
        # Initialize a DynamoDB resource
        dynamodb = boto3.resource("dynamodb", region_name="us-east-1")

        # Specify your table
        table = dynamodb.Table(table_name)

        # Get the current time in epoch format
        current_time = int(datetime.now().timestamp())

        # Perform the query operation with a filter expression to exclude expired items
        # response = table.query(
        #    KeyConditionExpression=boto3.dynamodb.conditions.Key('partitionKey').eq(partition_key),
        #    FilterExpression=boto3.dynamodb.conditions.Attr('expireAt').gt(current_time)
        # )
        response = table.query(
            KeyConditionExpression=dynamodb.conditions.Key("partitionKey").eq(partition_key),
            FilterExpression=dynamodb.conditions.Attr("expireAt").gt(current_time),
        )

        # Print the items that are not expired
        for item in response["Items"]:
            print(item)

    except Exception as e:
        print(f"Error querying items: {e}")


# Call the function with your values
query_dynamodb_items("Music", "your-partition-key-value")
```
+  API の詳細については、「*AWS SDK for Python (Boto3) API リファレンス*」の「[Query](https://docs.aws.amazon.com/goto/boto3/dynamodb-2012-08-10/Query)」を参照してください。

------

## 期限切れのアイテムに条件付きで書き込む
<a name="ttl-expired-items-conditional-write"></a>

条件式を使用すると、期限切れのアイテムに対する書き込みを回避できます。以下のコードスニペットは、有効期限が現在の時間よりも大きいかどうかをチェックする条件付き更新です。true の場合、書き込みオペレーションは続行されます。

以下のコード例は、項目の TTL を条件付きで更新する方法を示しています。

------
#### [ Java ]

**SDK for Java 2.x**  
条件を指定して、テーブル内の既存の DynamoDB 項目の TTL を更新します。  

```
package com.amazon.samplelib.ttl;

import com.amazon.samplelib.CodeSampleUtils;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse;

import java.util.Map;
import java.util.Optional;

/**
 * Updates an item in a DynamoDB table with TTL attributes using a conditional expression.
 * This class demonstrates how to conditionally update TTL expiration timestamps.
 */
public class UpdateTTLConditional {

    private static final String USAGE =
        """
            Usage:
                <tableName> <primaryKey> <sortKey> <region>
            Where:
                tableName - The Amazon DynamoDB table being queried.
                primaryKey - The name of the primary key. Also known as the hash or partition key.
                sortKey - The name of the sort key. Also known as the range attribute.
                region (optional) - The AWS region that the Amazon DynamoDB table is located in. (Default: us-east-1)
            """;
    private static final int DAYS_TO_EXPIRE = 90;
    private static final int SECONDS_PER_DAY = 24 * 60 * 60;
    private static final String PRIMARY_KEY_ATTR = "primaryKey";
    private static final String SORT_KEY_ATTR = "sortKey";
    private static final String UPDATED_AT_ATTR = "updatedAt";
    private static final String EXPIRE_AT_ATTR = "expireAt";
    private static final String UPDATE_EXPRESSION = "SET " + UPDATED_AT_ATTR + "=:c, " + EXPIRE_AT_ATTR + "=:e";
    private static final String CONDITION_EXPRESSION = "attribute_exists(" + PRIMARY_KEY_ATTR + ")";
    private static final String SUCCESS_MESSAGE = "%s UpdateItem operation with TTL successful.";
    private static final String CONDITION_FAILED_MESSAGE = "Condition check failed. Item does not exist.";
    private static final String TABLE_NOT_FOUND_ERROR = "Error: The Amazon DynamoDB table \"%s\" can't be found.";

    private final DynamoDbClient dynamoDbClient;

    /**
     * Constructs an UpdateTTLConditional with a default DynamoDB client.
     */
    public UpdateTTLConditional() {
        this.dynamoDbClient = null;
    }

    /**
     * Constructs an UpdateTTLConditional with the specified DynamoDB client.
     *
     * @param dynamoDbClient The DynamoDB client to use
     */
    public UpdateTTLConditional(final DynamoDbClient dynamoDbClient) {
        this.dynamoDbClient = dynamoDbClient;
    }

    /**
     * Main method to demonstrate conditionally updating an item with TTL.
     *
     * @param args Command line arguments
     */
    public static void main(final String[] args) {
        try {
            int result = new UpdateTTLConditional().processArgs(args);
            System.exit(result);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }

    /**
     * Process command line arguments and conditionally update an item with TTL.
     *
     * @param args Command line arguments
     * @return 0 if successful, non-zero otherwise
     * @throws ResourceNotFoundException If the table doesn't exist
     * @throws DynamoDbException If an error occurs during the operation
     * @throws IllegalArgumentException If arguments are invalid
     */
    public int processArgs(final String[] args) {
        // Argument validation (remove or replace this line when reusing this code)
        CodeSampleUtils.validateArgs(args, new int[] {3, 4}, USAGE);

        final String tableName = args[0];
        final String primaryKey = args[1];
        final String sortKey = args[2];
        final Region region = Optional.ofNullable(args.length > 3 ? args[3] : null)
            .map(Region::of)
            .orElse(Region.US_EAST_1);

        // Get current time in epoch second format
        final long currentTime = System.currentTimeMillis() / 1000;

        // Calculate expiration time 90 days from now in epoch second format
        final long expireDate = currentTime + (DAYS_TO_EXPIRE * SECONDS_PER_DAY);

        // Create the key map for the item to update
        final Map<String, AttributeValue> keyMap = Map.of(
            PRIMARY_KEY_ATTR, AttributeValue.builder().s(primaryKey).build(),
            SORT_KEY_ATTR, AttributeValue.builder().s(sortKey).build());

        // Create the expression attribute values
        final Map<String, AttributeValue> expressionAttributeValues = Map.of(
            ":c", AttributeValue.builder().n(String.valueOf(currentTime)).build(),
            ":e", AttributeValue.builder().n(String.valueOf(expireDate)).build());

        final UpdateItemRequest request = UpdateItemRequest.builder()
            .tableName(tableName)
            .key(keyMap)
            .updateExpression(UPDATE_EXPRESSION)
            .conditionExpression(CONDITION_EXPRESSION)
            .expressionAttributeValues(expressionAttributeValues)
            .build();

        try (DynamoDbClient ddb = dynamoDbClient != null
            ? dynamoDbClient
            : DynamoDbClient.builder().region(region).build()) {
            final UpdateItemResponse response = ddb.updateItem(request);
            System.out.println(String.format(SUCCESS_MESSAGE, tableName));
            return 0;
        } catch (ConditionalCheckFailedException e) {
            System.err.println(CONDITION_FAILED_MESSAGE);
            throw e;
        } catch (ResourceNotFoundException e) {
            System.err.format(TABLE_NOT_FOUND_ERROR, tableName);
            throw e;
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            throw e;
        }
    }
}
```
+  API の詳細については、*AWS SDK for Java 2.x API リファレンス*の「[UpdateItem](https://docs.aws.amazon.com/goto/SdkForJavaV2/dynamodb-2012-08-10/UpdateItem)」を参照してください。

------
#### [ JavaScript ]

**SDK for JavaScript (v3)**  
条件を指定して、テーブル内の既存の DynamoDB 項目の TTL を更新します。  

```
import { DynamoDBClient, UpdateItemCommand } from "@aws-sdk/client-dynamodb";
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";

export const updateItemConditional = async (tableName, partitionKey, sortKey, region = 'us-east-1', newAttribute = 'default-value') => {
    const client = new DynamoDBClient({
        region: region,
        endpoint: `https://dynamodb.${region}.amazonaws.com`
    });

    const currentTime = Math.floor(Date.now() / 1000);

    const params = {
        TableName: tableName,
        Key: marshall({
            artist: partitionKey,
            album: sortKey
        }),
        UpdateExpression: "SET newAttribute = :newAttribute",
        ConditionExpression: "expireAt > :expiration",
        ExpressionAttributeValues: marshall({
            ':newAttribute': newAttribute,
            ':expiration': currentTime
        }),
        ReturnValues: "ALL_NEW"
    };

    try {
        const response = await client.send(new UpdateItemCommand(params));
        const responseData = unmarshall(response.Attributes);
        console.log("Item updated successfully: ", responseData);
        return responseData;
    } catch (error) {
        if (error.name === "ConditionalCheckFailedException") {
            console.log("Condition check failed: Item's 'expireAt' is expired.");
        } else {
            console.error("Error updating item: ", error);
        }
        throw error;
    }
};

// Example usage (commented out for testing)
// updateItemConditional('your-table-name', 'your-partition-key-value', 'your-sort-key-value');
```
+  API の詳細については、*AWS SDK for JavaScript API リファレンス*の「[UpdateItem](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/UpdateItemCommand)」を参照してください。

------
#### [ Python ]

**SDK for Python (Boto3)**  
条件を指定して、テーブル内の既存の DynamoDB 項目の TTL を更新します。  

```
from datetime import datetime, timedelta

import boto3
from botocore.exceptions import ClientError


def update_dynamodb_item_ttl(table_name, region, primary_key, sort_key, ttl_attribute):
    """
    Updates an existing record in a DynamoDB table with a new or updated TTL attribute.

    :param table_name: Name of the DynamoDB table
    :param region: AWS Region of the table - example `us-east-1`
    :param primary_key: one attribute known as the partition key.
    :param sort_key: Also known as a range attribute.
    :param ttl_attribute: name of the TTL attribute in the target DynamoDB table
    :return:
    """
    try:
        dynamodb = boto3.resource("dynamodb", region_name=region)
        table = dynamodb.Table(table_name)

        # Generate updated TTL in epoch second format
        updated_expiration_time = int((datetime.now() + timedelta(days=90)).timestamp())

        # Define the update expression for adding/updating a new attribute
        update_expression = "SET newAttribute = :val1"

        # Define the condition expression for checking if 'expireAt' is not expired
        condition_expression = "expireAt > :val2"

        # Define the expression attribute values
        expression_attribute_values = {":val1": ttl_attribute, ":val2": updated_expiration_time}

        response = table.update_item(
            Key={"primaryKey": primary_key, "sortKey": sort_key},
            UpdateExpression=update_expression,
            ConditionExpression=condition_expression,
            ExpressionAttributeValues=expression_attribute_values,
        )

        print("Item updated successfully.")
        return response["ResponseMetadata"]["HTTPStatusCode"]  # Ideally a 200 OK
    except ClientError as e:
        if e.response["Error"]["Code"] == "ConditionalCheckFailedException":
            print("Condition check failed: Item's 'expireAt' is expired.")
        else:
            print(f"Error updating item: {e}")
    except Exception as e:
        print(f"Error updating item: {e}")


# replace with your values
update_dynamodb_item_ttl(
    "your-table-name",
    "us-east-1",
    "your-partition-key-value",
    "your-sort-key-value",
    "your-ttl-attribute-value",
)
```
+  API の詳細については、[AWS SDK for Python (Boto3) API リファレンス](https://docs.aws.amazon.com/goto/boto3/dynamodb-2012-08-10/UpdateItem)の「*UpdateItem*」を参照してください。

------

## DynamoDB ストリーム内の削除済みアイテムの特定
<a name="ttl-expired-items-identifying"></a>

ストリームレコードにはユーザー ID フィールド `Records[<index>].userIdentity` が含まれます。TTL プロセスによって削除された項目には、次のフィールドが含まれています。

```
Records[<index>].userIdentity.type
"Service"

Records[<index>].userIdentity.principalId
"dynamodb.amazonaws.com"
```

次の JSON は 1 つのストリームレコードの関連する部分を示しています。

```
"Records": [ 
  { 
	... 
		"userIdentity": {
		"type": "Service", 
      	"principalId": "dynamodb.amazonaws.com" 
   	} 
   ... 
	} 
]
```

# DynamoDB のテーブルに対するクエリの実行
<a name="Query"></a>

Amazon DynamoDB の `Query` オペレーションを使用して、プライマリキーの値に基づいて項目を探すことができます。

パーティションキーの属性名、および属性の単一値を指定する必要があります。`Query` はそのパーティションキー値を持つすべての項目を返します。必要に応じて、ソートキーの属性を指定し、比較演算子を使用して、検索結果をさらに絞り込むことができます。

リクエストの構文、レスポンスパラメータ、その他の例など、`Query` の使用方法の詳細については、*Amazon DynamoDB API リファレンス*の「[クエリ](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html)」を参照してください。

**Topics**
+ [DynamoDB でのクエリオペレーションのキー条件式](Query.KeyConditionExpressions.md)
+ [DynamoDB でのクエリオペレーションのフィルター式](Query.FilterExpression.md)
+ [DynamoDB でテーブルクエリ結果をページ分割する](Query.Pagination.md)
+ [DynamoDB でクエリオペレーションを使用する際のその他の側面](Query.Other.md)

# DynamoDB でのクエリオペレーションのキー条件式
<a name="Query.KeyConditionExpressions"></a>

最初の文字が `a-z` または `A-Z` であり、残りの文字 (ある場合、2 番目以降の文字) が `a-z`、`A-Z`、または `0-9` である場合は、キー条件式で任意の属性値を使用できます。さらに、属性名は DynamoDB の予約語であってはいけません。（詳細リストについては、「[DynamoDB の予約語](ReservedWords.md)」を参照してください） 属性名がこれらの要件を満たさない場合は、式の属性名をプレースホルダーとして定義する必要があります。詳細については、「[DynamoDB の式の属性名 (エイリアス)](Expressions.ExpressionAttributeNames.md)」を参照してください。

特定のパーティションキー値を持つ項目は、DynamoDB によってソートキーの値で並べ替えられた順序で近くに配置されて保存されます。`Query` オペレーションでは、並べ替えられた順序でDynamoDB によって項目が取得され、`KeyConditionExpression` や存在する任意の `FilterExpression` を使用して処理されます。その時初めて、`Query` の結果がクライアントに返されます。

`Query` オペレーションは常に結果セットを返します。一致する項目がない場合、結果セットは空になります。

`Query` の結果は常にソートキーの値によってソートされます。ソートキーのデータ型が `Number` である場合は、結果が番号順で返されます。その他の場合は、UTF-8 バイトの順序で結果が返されます。デフォルトの並べ替え順序は昇順です。順序を反転させるには、`ScanIndexForward` パラメータを `false` に設定します。

1 回の `Query` オペレーションで、最大 1 MB のデータを取得できます。この制限は、`FilterExpression` または `ProjectionExpression` が結果に適用される前に適用されます。レスポンスに `LastEvaluatedKey` が存在し、Null 以外の場合、結果セットをページ分割する必要があります ([DynamoDB でテーブルクエリ結果をページ分割する](Query.Pagination.md) を参照)。

## キー条件式の例
<a name="Query.KeyConditionExpressions-example"></a>

検索条件を指定するには、*キー条件式* (テーブルまたはインデックスから読み取る項目を決定する文字列) を使用します。

等価条件としてパーティションキーの名前と値を指定する必要があります。キー条件式では、非キー属性を使用することはできません。

オプションで、ソートキーに 2 番目の条件を指定できます (存在する場合)。ソートキーの条件では、次の比較演算子を 1 つ使用する必要があります。
+ `a = b` — 属性 *a* が値 *b* と等しい場合、true
+ `a < b` — *a* が *b* 未満の場合、true
+ `a <= b` — *a* が *b* 以下である場合、true
+ `a > b` — *a* が *b* より大きい場合、true
+ `a >= b` — *a* が *b* 以上である場合、true
+ `a BETWEEN b AND c` — *a* が *b* 以上で、*c* 以下である場合、true。

次の関数もサポートされます。
+ `begins_with (a, substr)` — 属性の値 `a` が特定のサブ文字列から始まる場合、true。

キー条件式の使用方法を示す AWS Command Line Interface (AWS CLI) の例を次に示します。これらの式では、実際の値の代わりにプレースホルダー (`:name` や `:sub` など) を使用しています。詳細については、「[DynamoDB の式の属性名 (エイリアス)](Expressions.ExpressionAttributeNames.md)」および「[DynamoDB での式属性値の使用](Expressions.ExpressionAttributeValues.md)」を参照してください。

**Example**  
`Thread` テーブルに対して、特定の `ForumName` (パーティションキー) についてのクエリを実行します。その `ForumName` の値を持つすべての項目はクエリによって読み込まれます。これはソートキー (`Subject`) が `KeyConditionExpression` に含まれないためです。  

```
aws dynamodb query \
    --table-name Thread \
    --key-condition-expression "ForumName = :name" \
    --expression-attribute-values  '{":name":{"S":"Amazon DynamoDB"}}'
```

**Example**  
`Thread` テーブルに対して、特定の `ForumName` (パーティションキー) についてのクエリを実行しますが、今回は指定の `Subject` (ソートキー) を持つ項目のみを返します。  

```
aws dynamodb query \
    --table-name Thread \
    --key-condition-expression "ForumName = :name and Subject = :sub" \
    --expression-attribute-values  file://values.json
```
`--expression-attribute-values` の引数は、`values.json` のファイルに保存されます。  

```
{
    ":name":{"S":"Amazon DynamoDB"},
    ":sub":{"S":"DynamoDB Thread 1"}
}
```

**Example**  
`Reply` テーブルに対して、特定の `Id` (パーティションキー) についてのクエリを実行しますが、`ReplyDateTime` (ソートキー) が特定の文字で始まる項目のみを返します。  

```
aws dynamodb query \
    --table-name Reply \
    --key-condition-expression "Id = :id and begins_with(ReplyDateTime, :dt)" \
    --expression-attribute-values  file://values.json
```
`--expression-attribute-values` の引数は、`values.json` のファイルに保存されます。  

```
{
    ":id":{"S":"Amazon DynamoDB#DynamoDB Thread 1"},
    ":dt":{"S":"2015-09"}
}
```

# DynamoDB でのクエリオペレーションのフィルター式
<a name="Query.FilterExpression"></a>

`Query` 結果の絞り込みが必要な場合は、オプションでフィルタ式を指定できます。*フィルタ式*によって、`Query` 結果の内、どの項目を返すべきか確定します。他のすべての結果は破棄されます。

フィルタ式は、`Query` の完了後、結果が返される前に適用されます。そのため、`Query` は、フィルタ式があるかどうかにかかわらず、同じ量の読み込みキャパシティーを消費します。

1 回の `Query` オペレーションで、最大 1 MB のデータを取得できます。この制限は、フィルタ式を評価する前に適用されます。

フィルタ式には、パーティションキーまたはソートキーの属性を含めることはできません。フィルタ式ではなく、キー条件式でこれらの属性を指定する必要があります。

フィルター式の構文は、キー条件式の構文と似ています。フィルター式は、キー条件式と同じコンパレータ、関数および論理演算子を使用できます。さらに、フィルター式では、非等号演算子 (`<>`)、`OR` 演算子、`CONTAINS` 演算子、`IN` 演算子、`BEGINS_WITH` 演算子、`BETWEEN` 演算子、`EXISTS` 演算子、および `SIZE` 演算子を使用できます。詳細については、「[DynamoDB でのクエリオペレーションのキー条件式](Query.KeyConditionExpressions.md)」および「[フィルター式と条件式の構文](Expressions.OperatorsAndFunctions.md#Expressions.OperatorsAndFunctions.Syntax)」を参照してください。

**Example**  
以下の AWS CLI の例では、`Thread` テーブルに対して、特定の `ForumName` (パーティションキー) および `Subject` (ソートキー) についてのクエリを実行します。見つかった項目のうち、最も一般的なディスカッションスレッド (つまり、一定数以上の `Views` があるスレッド) のみが返されます。  

```
aws dynamodb query \
    --table-name Thread \
    --key-condition-expression "ForumName = :fn and Subject begins_with :sub" \
    --filter-expression "#v >= :num" \
    --expression-attribute-names '{"#v": "Views"}' \
    --expression-attribute-values file://values.json
```
`--expression-attribute-values` の引数は、`values.json` のファイルに保存されます。  

```
{
    ":fn":{"S":"Amazon DynamoDB"},
    ":sub":{"S":"DynamoDB Thread 1"},
    ":num":{"N":"3"}
}
```
`Views` は DynamoDB で予約語であるため ([DynamoDB の予約語](ReservedWords.md) を参照)、この例では `#v` をプレースホルダーとして使用することにご注意ください。詳細については、「[DynamoDB の式の属性名 (エイリアス)](Expressions.ExpressionAttributeNames.md)」を参照してください。

**注記**  
フィルタ式は、`Query` 結果セットから項目を削除します。可能であれば、大量の項目を取得してもそのほとんどを破棄する必要がある場合、 `Query` の使用は避けてください。

# DynamoDB でテーブルクエリ結果をページ分割する
<a name="Query.Pagination"></a>

DynamoDB では、`Query` オペレーションの結果を*ページ割り*します。ページ分割を行うことで `Query` 結果が 1 MB サイズ (またはそれ以下) のデータの「ページ」に分割されます。アプリケーションは結果の最初のページ、次に 2 ページと処理できます。

1 つの `Query` は、サイズの制限である1 MB 以内の結果セットだけを返します。さらに結果があるかどうかを確認して、一度に 1 ページずつ結果を取り出すには、アプリケーションで次の操作を行う必要があります。

1. 低レベルの `Query` 結果を確認します。
   + 結果に `LastEvaluatedKey` 要素が含まれており、それが Null 以外の場合、ステップ 2 に進みます。
   + 結果に `LastEvaluatedKey` が*ない*場合、これ以上取得する項目はありません。

1. 同じ `KeyConditionExpression` を使用して `Query` を構築します。ただし、今回は、ステップ 1 から `LastEvaluatedKey` 値を取得して、新しい `Query` リクエストの `ExclusiveStartKey` パラメータとして使用します。

1. 新しい `Query` リクエストを実行します。

1. ステップ 1 に進んでください。

言い換えると、`Query` レスポンスからの `LastEvaluatedKey` を次の `Query` リクエストの `ExclusiveStartKey` として使用する必要があります。`Query` レスポンスに `LastEvaluatedKey` の要素がない場合、結果の最後のページを取得します。`LastEvaluatedKey` が空ではない場合でも、必ずしも結果セットにまだ値があることを意味するわけではありません。結果セットの最後まで到達したことを確認できるのは、`LastEvaluatedKey` が空になったときだけです。

AWS CLI を使用してこの動作を表示できます。AWS CLI は、`LastEvaluatedKey` が結果に表示されなくなるまで、低レベルの `Query` リクエストを DynamoDB に繰り返し送信します。特定の年の映画タイトルを取得する次の AWS CLI の例を考えてみます。

```
aws dynamodb query --table-name Movies \
    --projection-expression "title" \
    --key-condition-expression "#y = :yyyy" \
    --expression-attribute-names '{"#y":"year"}' \
    --expression-attribute-values '{":yyyy":{"N":"1993"}}' \
    --page-size 5 \
    --debug
```

通常、AWS CLI はページ分割を自動的に処理します。ただし、この例では、AWS CLI `--page-size` パラメータによりページごとの項目数が制限されています。`--debug` パラメータは、リクエストとレスポンスについての低レベルの情報を表示します。

この例を実行した場合、DynamoDB からの最初のレスポンスは次のようになります。

```
2017-07-07 11:13:15,603 - MainThread - botocore.parsers - DEBUG - Response body:
b'{"Count":5,"Items":[{"title":{"S":"A Bronx Tale"}},
{"title":{"S":"A Perfect World"}},{"title":{"S":"Addams Family Values"}},
{"title":{"S":"Alive"}},{"title":{"S":"Benny & Joon"}}],
"LastEvaluatedKey":{"year":{"N":"1993"},"title":{"S":"Benny & Joon"}},
"ScannedCount":5}'
```

レスポンスの `LastEvaluatedKey` は、すべての項目が取得されたわけではないことを示します。その後、AWS CLI は DynamoDB に別の `Query` リクエストを送信します。このリクエストとレスポンスのパターンが、最終レスポンスまで繰り返されます。

```
2017-07-07 11:13:16,291 - MainThread - botocore.parsers - DEBUG - Response body:
b'{"Count":1,"Items":[{"title":{"S":"What\'s Eating Gilbert Grape"}}],"ScannedCount":1}'
```

`LastEvaluatedKey` がない場合、これ以上取得する項目がないことを示します。

**注記**  
AWS SDK は低レベルの DynamoDB レスポンス (`LastEvaluatedKey` の有無を含む) を処理し、ページ割りした `Query` 結果のさまざまな抽象化を提供します。たとえば、SDK for Java のドキュメントインターフェイスでは、`java.util.Iterator` サポートが利用可能なため、結果を一度に 1 つずつ確認できます。  
各種のプログラミング言語のコード例については、「[Amazon DynamoDB 利用開始ガイド](https://docs.aws.amazon.com/amazondynamodb/latest/gettingstartedguide/)」と、該当言語の「AWS SDK ドキュメント」を参照してください。

`Query` オペレーションの `Limit` パラメータを使用して結果セット内の項目数を制限することで、ページサイズを小さくすることもできます。

DynamoDB でのクエリ実行の詳細については、「[DynamoDB のテーブルに対するクエリの実行](Query.md)」を参照してください。

# DynamoDB でクエリオペレーションを使用する際のその他の側面
<a name="Query.Other"></a>

このセクションでは、DynamoDB クエリオペレーションのその他の側面について説明します。これには、結果サイズの制限、スキャンされた項目と返された項目のカウント、読み取り容量の消費のモニタリング、読み取り整合性の制御が含まれます。

## 結果セットの項目数の制限
<a name="Query.Limit"></a>

`Query` オペレーションを使用すると、読み取られる項目数を制限することができます。これを行うには、`Limit` パラメータに項目の最大数を設定します。

たとえば、フィルタ式を使用せず、`Limit` 値を `6` として、テーブルを `Query` するとします。`Query` 結果には、リクエストのキー条件式に一致するテーブルからの最初 6 つの項目が含まれます。

ここで、`Query` にフィルタ式を追加するとします。この場合、DynamoDB は最大 6 つの項目を読み込み、フィルタ式と一致する項目だけを返します。DynamoDB が項目の読み込みを続けた場合、さらに多くの項目がフィルタ式にマッチしても、最終的な `Query` 結果に含まれる項目数は 6 つ以下です。

## 結果での項目のカウント
<a name="Query.Count"></a>

`Query` レスポンスには、条件に一致する項目に加えて次の要素が含まれます。
+ `ScannedCount` — フィルター式 (存在する場合) が適用される*前に*、キー条件式に一致した項目数。
+ `Count` — フィルター式 (存在する場合) が適用された*後に*残っている項目数。

**注記**  
フィルタ式を使用しない場合、`ScannedCount` と `Count` は同じ値を持ちます。

`Query` 結果セットのサイズが 1 MB より大きい場合、`ScannedCount` および `Count` では、合計項目数の一部のみが示されます。すべての結果を取得するためには、複数の `Query` オペレーションを実行する必要があります ([DynamoDB でテーブルクエリ結果をページ分割する](Query.Pagination.md) を参照してください)。

各 `Query` レスポンスには、特定の `Query` リクエストによって処理された項目の `ScannedCount` および `Count` が含まれます。すべての `Query` リクエストの合計を取得するには、`ScannedCount` および `Count` の実行中の集計を維持することができます。

## クエリで消費されるキャパシティユニット
<a name="Query.CapacityUnits"></a>

パーティションキーの属性名、およびその属性の単一値を指定する限り、いずれのテーブルまたはセカンダリインデックスにも、`Query` を実行できます。`Query` により、そのパーティションのキー値を持つすべての項目が返されます。必要に応じて、ソートキーの属性を指定し、比較演算子を使用して、検索結果をさらに絞り込むことができます。`Query`API オペレーションでは、次のように読み込みキャパシティーユニットを消費します。


****  

| ...を `Query` する場合 | DynamoDB は ... からの読み込み容量ユニットを消費します。 | 
| --- | --- | 
| テーブル | テーブルのプロビジョニングされた読み込みキャパシティー。 | 
| グローバルセカンダリインデックス | インデックスのプロビジョニングされた読み込みキャパシティー。 | 
| ローカルセカンダリインデックス | ベーステーブルのプロビジョニングされた読み込みキャパシティー。 | 

デフォルトでは、`Query`オペレーションはどのくらいの読み込みキャパシティーを消費するかについてのデータを返しません。ただし、この情報を取得するために `Query` リクエストで `ReturnConsumedCapacity` パラメータを指定できます。`ReturnConsumedCapacity` の有効な設定を以下に示します。
+ `NONE` — 消費された容量データは返されません。(これがデフォルトです)
+ `TOTAL` — レスポンスには消費された読み込み容量単位の合計値が含まれます。
+ `INDEXES` — レスポンスは、アクセスする各テーブルとインデックスの消費される容量とともに、消費される読み込み容量単位の合計値を示します。

DynamoDB はアプリケーションに返されるデータ量ではなく、項目の数と項目のサイズに基づいて、消費される読み込みキャパシティユニットの数を計算します。このため、消費される容量ユニットの数は、(デフォルトの動作で) 属性のすべてをリクエストしても、(プロジェクション式を使用して) 一部をリクエストしても、同じになります。この数は、フィルター式を使用していてもいなくても同じです。`Query` は最小読み込みキャパシティユニットを消費して、最大 4 KB の項目について強力な整合性のある読み込みを 1 秒あたり 1 回、または結果整合性のある読み込みを 1 秒あたり 2 回実行します。4 KB より大きい項目を読み込む必要がある場合、DynamoDB には追加の読み込みリクエストユニットが必要です。空のテーブルや、パーティションキーの数が少ない非常に大きなテーブルでは、クエリされたデータの量を超えて追加の RCU が課金される場合があります。これにより、データが存在しない場合でも、`Query` リクエストを処理するためのコストがカバーされます。

## クエリの読み込み整合性
<a name="Query.ReadConsistency"></a>

`Query` オペレーションは、結果的に整合性のある読み込みをデフォルトで行います。つまり、`Query` 結果が、最近完了した `PutItem` または `UpdateItem` オペレーションによる変更を反映しない場合があります。詳細については、「[DynamoDB の読み取り整合性](HowItWorks.ReadConsistency.md)」を参照してください。

強力な整合性のある読み込みが必要な場合は、`Query` リクエストで `ConsistentRead` パラメータを `true` に設定します。

# DynamoDB でのテーブルのスキャン
<a name="Scan"></a>

Amazon DynamoDB の `Scan` オペレーションでは、テーブルまたはセカンダリインデックスのすべての項目を読み込みます。デフォルトでは、`Scan` オペレーションはテーブルまたはインデックスのすべての項目のデータ属性を返します。`ProjectionExpression` パラメータを使用し、`Scan` がすべての属性ではなく一部のみを返すようにできます。

`Scan` は常に結果セットを返します。一致する項目がない場合、結果セットは空になります。

1 回の `Scan` リクエストで、最大 1 MB のデータを取得できます。DynamoDB では、必要に応じてこのデータにフィルター式を適用して、結果をユーザーに返す前に絞り込むことができます。

**Topics**
+ [Scan のフィルタ式](#Scan.FilterExpression)
+ [結果セットの項目数の制限](#Scan.Limit)
+ [結果のページ分割](#Scan.Pagination)
+ [結果での項目のカウント](#Scan.Count)
+ [Scan で消費されるキャパシティユニット](#Scan.CapacityUnits)
+ [スキャンの読み込み整合性](#Scan.ReadConsistency)
+ [並列スキャン](#Scan.ParallelScan)

## Scan のフィルタ式
<a name="Scan.FilterExpression"></a>

`Scan` 結果の絞り込みが必要な場合は、オプションでフィルタ式を指定できます。*フィルタ式*によって、`Scan` 結果の内、どの項目を返すべきか確定します。他のすべての結果は破棄されます。

フィルタ式は、`Scan` の完了後、結果が返される前に適用されます。そのため、`Scan` は、フィルタ式があるかどうかにかかわらず、同じ量の読み込みキャパシティーを消費します。

1 回の `Scan` オペレーションで、最大 1 MB のデータを取得できます。この制限は、フィルタ式を評価する前に適用されます。

`Scan` では、パーティションキー属性やソートキー属性など、フィルター式で任意の属性を指定できます。

フィルタ式の構文は、条件式の構文と同じです。フィルタ式は、条件式と同じコンパレータ、関数および論理演算子を使用できます。論理演算子の詳細については、「[DynamoDB の条件式とフィルター式、演算子、関数](Expressions.OperatorsAndFunctions.md)」を参照してください。

**Example**  
次の AWS Command Line Interface (AWS CLI) の例では `Thread` テーブルをスキャンして、特定のユーザーによって最後に投稿された項目のみを返します。  

```
aws dynamodb scan \
     --table-name Thread \
     --filter-expression "LastPostedBy = :name" \
     --expression-attribute-values '{":name":{"S":"User A"}}'
```

## 結果セットの項目数の制限
<a name="Scan.Limit"></a>

`Scan` オペレーションは、結果で返される項目数を制限することができます。​これを行うには、フィルタ式を評価する前に、`Limit` パラメータに、`Scan` オペレーションが返す項目の最大数を設定します。

たとえば、フィルタ式を使用せず、`Scan` 値を `Limit` として、テーブルを `6` するとします。`Scan` 結果には、テーブルの最初の 6 つの項目が含まれます。

ここで、`Scan` にフィルタ式を追加するとします。この場合、DynamoDB は返される 6 つの項目にフィルター式を適用し、一致しない項目を廃棄します。最終的な `Scan` 結果はフィルタリングされる項目の数に応じて、6 つ以下の項目を含みます。

## 結果のページ分割
<a name="Scan.Pagination"></a>

DynamoDB では、`Scan` オペレーションの結果を*ページ割り*します。ページ分割を行うことで `Scan` 結果が 1 MB サイズ (またはそれ以下) のデータの「ページ」に分割されます。アプリケーションは結果の最初のページ、次に 2 ページと処理できます。

1 つの `Scan` は、サイズの制限である1 MB 以内の結果セットだけを返します。

さらに結果があるかどうかを確認して、一度に 1 ページずつ結果を取り出すには、アプリケーションで次の操作を行う必要があります。

1. 低レベルの `Scan` 結果を確認します。
   + 結果に `LastEvaluatedKey` 要素が含まれる場合、ステップ 2 に進みます。
   + 結果に `LastEvaluatedKey` が*ない*場合、これ以上取得する項目はありません。

1. 以前のものと同じパラメータを使用して新しい `Scan` リクエストを作成します。ただし、今回は、ステップ 1 から `LastEvaluatedKey` 値を取得して、新しい `Scan` リクエストの `ExclusiveStartKey` パラメータとして使用します。

1. 新しい `Scan` リクエストを実行します。

1. ステップ 1 に進んでください。

言い換えると、`LastEvaluatedKey` レスポンスからの `Scan` を次の `ExclusiveStartKey` リクエストの `Scan` として使用する必要があります。`LastEvaluatedKey` レスポンスに `Scan` 要素がない場合、結果の最後のページを取得します。(結果セットの最後まで到達したことを確認できるのは、`LastEvaluatedKey` がないときだけです)

AWS CLI を使用してこの動作を表示できます。AWS CLI は低レベル `Scan` リクエストを DynamoDB に送信し、`LastEvaluatedKey` が結果に表示されなくなるまで送信を繰り返します。次の AWS CLI の例を見てください。この例は、`Movies` テーブル全体をスキャンしますが、特定のジャンルの映画のみを返します。

```
aws dynamodb scan \
    --table-name Movies \
    --projection-expression "title" \
    --filter-expression 'contains(info.genres,:gen)' \
    --expression-attribute-values '{":gen":{"S":"Sci-Fi"}}' \
    --page-size 100  \
    --debug
```

通常、AWS CLI はページ分割を自動的に処理します。ただし、この例では、AWS CLI `--page-size` パラメータによりページごとの項目数が制限されています。`--debug` パラメータは、リクエストとレスポンスについての低レベルの情報を表示します。

**注記**  
ページ分割の結果は、渡した入力パラメータによっても異なります。  
`aws dynamodb scan --table-name Prices --max-items 1` を使用すると `NextToken` が返されます
`aws dynamodb scan --table-name Prices --limit 1` を使用すると `LastEvaluatedKey` が返されます。
また、特に `--starting-token` を使用するには、`NextToken` 値が必要であることに注意してください。

この例を実行した場合、DynamoDB からの最初のレスポンスは次のようになります。

```
2017-07-07 12:19:14,389 - MainThread - botocore.parsers - DEBUG - Response body:
b'{"Count":7,"Items":[{"title":{"S":"Monster on the Campus"}},{"title":{"S":"+1"}},
{"title":{"S":"100 Degrees Below Zero"}},{"title":{"S":"About Time"}},{"title":{"S":"After Earth"}},
{"title":{"S":"Age of Dinosaurs"}},{"title":{"S":"Cloudy with a Chance of Meatballs 2"}}],
"LastEvaluatedKey":{"year":{"N":"2013"},"title":{"S":"Curse of Chucky"}},"ScannedCount":100}'
```

レスポンスの `LastEvaluatedKey` は、すべての項目が取得されたわけではないことを示します。その後、AWS CLI は DynamoDB に別の `Scan` リクエストを送信します。このリクエストとレスポンスのパターンが、最終レスポンスまで繰り返されます。

```
2017-07-07 12:19:17,830 - MainThread - botocore.parsers - DEBUG - Response body:
b'{"Count":1,"Items":[{"title":{"S":"WarGames"}}],"ScannedCount":6}'
```

`LastEvaluatedKey` がない場合、これ以上取得する項目がないことを示します。

**注記**  
AWS SDK は低レベルの DynamoDB レスポンス (`LastEvaluatedKey` の有無を含む) を処理し、ページ割りした `Scan` 結果のさまざまな抽象化を提供します。たとえば、SDK for Java のドキュメントインターフェイスでは、`java.util.Iterator` サポートが利用可能なため、結果を一度に 1 つずつ確認できます。  
各種のプログラミング言語のコード例については、[Amazon DynamoDB 利用開始ガイド](https://docs.aws.amazon.com/amazondynamodb/latest/gettingstartedguide/)と、該当言語の AWS SDK ドキュメントを参照してください。

## 結果での項目のカウント
<a name="Scan.Count"></a>

`Scan` レスポンスには、条件に一致する項目に加えて次の要素が含まれます。
+ `ScannedCount` — `ScanFilter` が適用される前に評価される項目数。`ScannedCount` 値が大きく、`Count` 結果が小さいまたはない場合は、`Scan` オペレーションが不十分であることを示しています。リクエストでフィルタを使用していない場合、`ScannedCount` は `Count` と同じです。
+ `Count` — フィルター式 (存在する場合) が適用された*後*に残っている項目数。

**注記**  
フィルタ式を使用しない場合、`ScannedCount` と `Count` は同じ値を持ちます。

`Scan` 結果セットのサイズが 1 MB より大きい場合、`ScannedCount` および `Count` では、合計項目数の一部のみが示されます。すべての結果を取得するためには、複数の `Scan` オペレーションを実行する必要があります ([結果のページ分割](#Scan.Pagination) を参照してください)。

各 `Scan` レスポンスには、特定の `ScannedCount` によって処理された項目の `Count` および `Scan` が含まれます。すべての `Scan` リクエストの合計を取得するには、`ScannedCount` および `Count` の実行中の集計を維持することができます。

## Scan で消費されるキャパシティユニット
<a name="Scan.CapacityUnits"></a>

任意のテーブルまたはセカンダリインデックスで `Scan` できます。`Scan` オペレーションでは、次のように読み込み容量単位を消費します。


****  

| ...を `Scan` する場合 | DynamoDB は ... からの読み込み容量ユニットを消費します。 | 
| --- | --- | 
| テーブル | テーブルのプロビジョニングされた読み込みキャパシティー。 | 
| グローバルセカンダリインデックス | インデックスのプロビジョニングされた読み込みキャパシティー。 | 
| ローカルセカンダリインデックス | ベーステーブルのプロビジョニングされた読み込みキャパシティー。 | 

**注記**  
セカンダリインデックススキャンオペレーションのクロスアカウントアクセスは、現在、[リソースベースのポリシー](access-control-resource-based.md)ではサポートされていません。

デフォルトでは、`Scan`オペレーションはどのくらいの読み込みキャパシティーを消費するかについてのデータを返しません。ただし、この情報を取得するために `Scan` リクエストで `ReturnConsumedCapacity` パラメータを指定できます。`ReturnConsumedCapacity` の有効な設定を以下に示します。
+ `NONE` — 消費された容量データは返されません。(これがデフォルトです)
+ `TOTAL` — レスポンスには消費された読み込み容量単位の合計値が含まれます。
+ `INDEXES` — レスポンスは、アクセスする各テーブルとインデックスの消費される容量とともに、消費される読み込み容量単位の合計値を示します。

DynamoDB はアプリケーションに返されるデータ量ではなく、項目の数と項目のサイズに基づいて、消費される読み込みキャパシティユニットの数を計算します。このため、消費される容量ユニットの数は、(デフォルトの動作で) 属性のすべてをリクエストしても、(プロジェクション式を使用して) 一部をリクエストしても、同じになります。この数は、フィルター式を使用していてもいなくても同じです。`Scan` は最小読み込みキャパシティユニットを消費して、最大 4 KB の項目について強力な整合性のある読み込みを 1 秒あたり 1 回、または結果整合性のある読み込みを 1 秒あたり 2 回実行します。4 KB より大きい項目を読み込む必要がある場合、DynamoDB には追加の読み込みリクエストユニットが必要です。空のテーブルや、パーティションキーの数が少ない非常に大きなテーブルでは、スキャンされたデータ量を超える追加の RCU が課金される場合があります。これにより、データが存在しない場合でも、`Scan` リクエストを処理するためのコストがカバーされます。

## スキャンの読み込み整合性
<a name="Scan.ReadConsistency"></a>

`Scan` オペレーションは、結果的に整合性のある読み込みをデフォルトで行います。つまり、`Scan` 結果が、最近完了した `PutItem` または `UpdateItem` オペレーションによる変更を反映しない場合があります。詳細については、「[DynamoDB の読み取り整合性](HowItWorks.ReadConsistency.md)」を参照してください。

強力な整合性のある読み込みが必要な場合は、`Scan` が開始する時に `ConsistentRead` パラメータを `true` リクエストで `Scan` に設定できます。これにより、`Scan` が開始する前に完了した書き込みオペレーションがすべて `Scan` 応答に含められます。

`ConsistentRead` を `true` に設定し、[DynamoDB Streams](./Streams.html) と同時に使用すると、テーブルのバックアップまたはレプリケーションシナリオで役立ちます。最初に、テーブル内のデータの整合性のあるコピーを取得するため、`Scan` を true に設定して `ConsistentRead` を使用します。`Scan` の実行中、DynamoDB Streams はテーブルで発生した追加の書き込みアクティビティをすべて記録します。`Scan` が完了したら、ストリームからテーブルへの書き込みアクティビティを適用できます。

**注記**  
`Scan` を `ConsistentRead` に設定した `true` オペレーションでは、`ConsistentRead` をデフォルト値 (`false`) のままにした場合と比較して、2 倍の読み込みキャパシティーユニットが使用されます。

## 並列スキャン
<a name="Scan.ParallelScan"></a>

デフォルトでは、`Scan` オペレーションは、データを順次処理します。Amazon DynamoDB はアプリケーションに 1 MB 単位でデータを返し、アプリケーションは追加の `Scan` オペレーションを使用して、次の 1 MB のデータを取得できます。

スキャンするテーブルまたはインデックスが大きいほど、`Scan` を完了するのに時間がかかります。さらに、シーケンシャル `Scan` は、プロビジョンされた読み込みスループットキャパシティを常に完全に使用できるとは限りません。DynamoDB は大きなテーブルのデータを複数の物理パーティションに分散しますが、`Scan` オペレーションでは、一度に 1 つのパーティションしか読み込むことができません。このため、`Scan` のスループットは、単一のパーティションの最大スループットによって制約されます。

これらの問題に対処するために、`Scan` オペレーションでは、テーブルまたはセカンダリインデックスを論理的に複数の*セグメント*に分割し、複数のアプリケーションワーカーがセグメントを並行してスキャンします。各ワーカーは、スレッド (マルチスレッドをサポートするプログラミング言語) またはオペレーティングシステムプロセスにすることができます。並列スキャンを実行するには、各ワーカーが独自の `Scan` リクエストを以下のパラメータで送信します。
+ `Segment` — 特定のワーカーによってスキャンされるセグメント。各ワーカーは、`Segment` に異なる値を使用する必要があります。
+ `TotalSegments` — 並列スキャンのセグメントの合計数。この値は、アプリケーションが使用するワーカーの数と同じでなければなりません。

次の図表は、マルチスレッドアプリケーションが 3 度の並列処理で並列 `Scan` を実行する方法を示しています。

![\[テーブルを 3 つのセグメントに分割して並列スキャンを実行するマルチスレッドアプリケーション。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/ParallelScan.png)




この図では、アプリケーションは 3 つのスレッドを生成し、各スレッドに番号を割り当てます。(セグメントはゼロベースであるため、最初の数値は常に 0 です。) 各スレッドは `Scan` リクエストを公開し、設定 `Segment` をその指定された番号に設定し、`TotalSegments` を 3 に設定します。各スレッドは、指定されたセグメントをスキャンし、一度に 1 MB のデータを取得し、アプリケーションのメインスレッドにデータを返します。

DynamoDB は、各項目のパーティションキーにハッシュ関数を適用して、項目を*セグメント*に割り当てます。指定された `TotalSegments` 値の場合、同じパーティションキーを持つすべての項目が常に同じ `Segment` に割り当てられます。つまり、*項目 1*、*項目 2*、*項目 3* すべてが `pk="account#123"` を共有するテーブルでは (ソートキーは異なる)、ソートキーの値や*項目コレクション*のサイズに関係なく、これらの項目は同じワーカーによって処理されます。

*セグメント*割り当てはパーティションキーハッシュのみに基づいているため、セグメントは不均等に分散される可能性があります。一部のセグメントには項目が含まれていない場合がありますが、他のセグメントには大きな項目コレクションを持つパーティションキーが多く含まれている場合があります。その結果、セグメントの合計数を増やしても、特にパーティションキーがキースペース全体に均一に分散されていない場合、スキャンパフォーマンスが速くなることは保証されません。

`Segment` および `TotalSegments` の値は、個々の `Scan` リクエストに適用されるため、いつでも異なる値を使用できます。アプリケーションが最高のパフォーマンスを達成するまで、これらの値および使用するワーカーの数を試さなければならない場合があります。

**注記**  
多数のワーカーを使用した並列スキャンでは、スキャン対象のテーブルまたはインデックスに対してプロビジョンされたスループットをすべて簡単に使用できます。テーブルまたはインデックスが他のアプリケーションから大量の読み込みまたは書き込みアクティビティが発生している場合は、このようなスキャンを避けることをお勧めします。  
リクエストごとに返されるデータの量を制御するには、`Limit` パラメータを使用します。これにより、1 人のワーカーがプロビジョンされたスループットをすべて消費し、他のすべてのワーカーが犠牲になる状況を防ぐことができます。

# PartiQL: Amazon DynamoDB 用の SQL 互換クエリ言語
<a name="ql-reference"></a>

Amazon DynamoDB は SQL 互換のクエリ言語である [PartiQL](https://partiql.org/) をサポートしており、Amazon DynamoDB でデータの選択、挿入、更新、および削除を行うことができます。PartiQL を使用すると、AWS マネジメントコンソール、NoSQL Workbench、AWS Command Line Interface、および PartiQL 用の DynamoDB API を使用して、DynamoDB テーブルと簡単にやり取りを行い、アドホッククエリを実行できます。

PartiQL オペレーションは、他の DynamoDB データプレーンオペレーションと同様の可用性、レイテンシー、パフォーマンスを提供します。

次のセクションでは、PartiQL の DynamoDB 実装について説明します。

**Topics**
+ [PartiQL とは何ですか?](#ql-reference.what-is)
+ [Amazon DynamoDB での PartiQL](#ql-reference.what-is)
+ [開始方法](ql-gettingstarted.md)
+ [データ型](ql-reference.data-types.md)
+ [ステートメント](ql-reference.statements.md)
+ [関数](ql-functions.md)
+ [演算子](ql-operators.md)
+ [トランザクション](ql-reference.multiplestatements.transactions.md)
+ [バッチオペレーション](ql-reference.multiplestatements.batching.md)
+ [IAM ポリシー](ql-iam.md)

## PartiQL とは何ですか?
<a name="ql-reference.what-is"></a>

*PartiQL* は、構造化データ、半構造化データ、ネストされたデータを含む複数のデータストア間で、SQL 互換のクエリアクセスを提供します。PartiQL は、Amazon 内で広く使用されており、現在、DynamoDB を含む多くの AWS のサービスの一部として利用できます。

PartiQL の仕様とコアクエリ言語のチュートリアルについては、[PartiQL ドキュメント](https://partiql.org/docs.html)を参照してください。

**注記**  
Amazon DynamoDB は、[PartiQL](https://partiql.org/) クエリ言語の*サブセット*をサポートしています。
Amazon DynamoDB では、[Amazon ion](http://amzn.github.io/ion-docs/) データ形式または Amazon Ion リテラルはサポートしていません。

## Amazon DynamoDB での PartiQL
<a name="ql-reference.what-is"></a>

DynamoDB で PartiQL クエリを実行するには、次を使用します。
+ DynamoDB コンソール
+ NoSQL Workbench
+ AWS Command Line Interface (AWS CLI)
+ DynamoDB API

これらの方法を使用して DynamoDB にアクセスする方法については、「[DynamoDB へのアクセス](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AccessingDynamoDB.html)」を参照してください。

# DynamoDB 用の PartiQL の開始方法
<a name="ql-gettingstarted"></a>

このセクションでは、Amazon DynamoDB コンソール、AWS Command Line Interface (AWS CLI)、および DynamoDB API から DynamoDB 用の PartiQL を使用する方法について説明します。

次の例では、「[DynamoDB の開始方法](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStartedDynamoDB.html)」チュートリアルで、DynamoDB テーブルを定義していることが前提です。

DynamoDB コンソール、AWS Command Line Interface、または DynamoDB API を使用して DynamoDB にアクセスする方法については、「[DynamoDB へのアクセス](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AccessingDynamoDB.html)」を参照してください。

[NoSQL Workbench](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.html) を[ダウンロード](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.settingup.html)して、[DynamoDB 用の PartiQL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.html) ステートメントを構築するには、DynamoDB [Operation Builder](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.querybuilder.operationbuilder.html) 用の NoSQL Workbench の右上にある **[PartiQL operations]** (PartiQL オペレーション) を選択します。

------
#### [ Console ]

![\[Music テーブルに対してクエリオペレーションを実行した結果を示す PartiQL エディタインターフェイス。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/partiqlgettingstarted.png)


1. AWS マネジメントコンソール にサインインして DynamoDB コンソール ([https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/)) を開きます。

1. コンソール左側のナビゲーションペインで、[**PartiQL エディター**] を選択します。

1. [**Music**] テーブルを選択します。

1. **[Query table]** (クエリテーブル) を選択します。このアクションで生成したクエリは、完全なテーブルスキャンを実行しません。

1. `partitionKeyValue` を文字列型の値である `Acme Band` に置換します。`sortKeyValue` を文字列型の値である `Happy Day` に置換します。

1. [**実行**] ボタンを選択します。

1. **[Table view]** (テーブルビュー) または **[JSON view]** (JSON ビュー) ボタンを選択すると、クエリの結果を確認できます。

------
#### [ NoSQL workbench ]

![\[NoSQL Workbench インターフェイス。Music テーブルに対して実行できる PartiQL SELECT ステートメントを示します。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/workbench/partiql.single.png)


1. [**PartiQL ステートメント**] を選択します。

1. 次の PartiQL [[SELECT statement]](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.select.html) (SELECT ステートメント) を入力します。

   ```
   SELECT *                                         
   FROM Music  
   WHERE Artist=? and SongTitle=?
   ```

1. `Artist` および `SongTitle` パラメータの値を指定するには

   1. [**オプションのリクエストパラメータ**] を選択します。

   1. [**新しいパラメータの追加**] を選択します。

   1. 属性タイプとして **[string]** (文字列型) を選択し、値に `Acme Band` を選択します。

   1. ステップ b とステップ c を繰り返し、**[string]** (文字列型) のタイプと `PartiQL Rocks` の値を選択します。

1. コードを生成する場合は、[**Generate code (コードの生成)**] を選択します。

   表示されたタブから目的の言語を選択します。これで、このコードをコピーしてアプリケーションで使用できるようになります。

1. オペレーションをすぐに実行する場合は、[**実行**] をクリックします。

------
#### [ AWS CLI ]

1. INSERT PartiQL ステートメントを使用して、`Music` テーブルに項目を作成します。

   ```
   aws dynamodb execute-statement --statement "INSERT INTO Music  \
   					    VALUE  \
   					    {'Artist':'Acme Band','SongTitle':'PartiQL Rocks'}"
   ```

1. SELECT PartiQL ステートメントを使用して、Music テーブルから項目を取得します。

   ```
   aws dynamodb execute-statement --statement "SELECT * FROM Music   \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

1. UPDATE PartiQL ステートメントを使用して、`Music` テーブルの項目を更新します。

   ```
   aws dynamodb execute-statement --statement "UPDATE Music  \
                                               SET AwardsWon=1  \
                                               SET AwardDetail={'Grammys':[2020, 2018]}  \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

   `Music` テーブルに、項目のリスト値を追加します。

   ```
   aws dynamodb execute-statement --statement "UPDATE Music  \
                                               SET AwardDetail.Grammys =list_append(AwardDetail.Grammys,[2016])  \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

   `Music` テーブルから項目のリスト値を削除します。

   ```
   aws dynamodb execute-statement --statement "UPDATE Music  \
                                               REMOVE AwardDetail.Grammys[2]  \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

   `Music` テーブルに、項目の新しいマップメンバーを追加します。

   ```
   aws dynamodb execute-statement --statement "UPDATE Music  \
                                               SET AwardDetail.BillBoard=[2020]  \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

   `Music` テーブルに、項目の新しい文字列セットの属性を追加します。

   ```
   aws dynamodb execute-statement --statement "UPDATE Music  \
                                               SET BandMembers =<<'member1', 'member2'>>  \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

   `Music` テーブルで、項目の文字列セットの属性を更新します。

   ```
   aws dynamodb execute-statement --statement "UPDATE Music  \
                                               SET BandMembers =set_add(BandMembers, <<'newmember'>>)  \
                                               WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

1. DELETE PartiQL ステートメントを使用して、`Music` テーブルから項目を削除します。

   ```
   aws dynamodb execute-statement --statement "DELETE  FROM Music  \
       WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
   ```

------
#### [ Java ]

```
import java.util.ArrayList;
import java.util.List;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import software.amazon.dynamodb.AmazonDynamoDB;
import software.amazon.dynamodb.AmazonDynamoDBClientBuilder;
import software.amazon.dynamodb.model.AttributeValue;
import software.amazon.dynamodb.model.ConditionalCheckFailedException;
import software.amazon.dynamodb.model.ExecuteStatementRequest;
import software.amazon.dynamodb.model.ExecuteStatementResult;
import software.amazon.dynamodb.model.InternalServerErrorException;
import software.amazon.dynamodb.model.ItemCollectionSizeLimitExceededException;
import software.amazon.dynamodb.model.ProvisionedThroughputExceededException;
import software.amazon.dynamodb.model.RequestLimitExceededException;
import software.amazon.dynamodb.model.ResourceNotFoundException;
import software.amazon.dynamodb.model.TransactionConflictException;

public class DynamoDBPartiQGettingStarted {

    public static void main(String[] args) {
        // Create the DynamoDB Client with the region you want
        AmazonDynamoDB dynamoDB = createDynamoDbClient("us-west-1");

        try {
            // Create ExecuteStatementRequest
            ExecuteStatementRequest executeStatementRequest = new ExecuteStatementRequest();
            List<AttributeValue> parameters= getPartiQLParameters();

            //Create an item in the Music table using the INSERT PartiQL statement
            processResults(executeStatementRequest(dynamoDB, "INSERT INTO Music value {'Artist':?,'SongTitle':?}", parameters));

            //Retrieve an item from the Music table using the SELECT PartiQL statement.
            processResults(executeStatementRequest(dynamoDB, "SELECT * FROM Music  where Artist=? and SongTitle=?", parameters));

            //Update an item in the Music table using the UPDATE PartiQL statement.
            processResults(executeStatementRequest(dynamoDB, "UPDATE Music SET AwardsWon=1 SET AwardDetail={'Grammys':[2020, 2018]}  where Artist=? and SongTitle=?", parameters));

            //Add a list value for an item in the Music table.
            processResults(executeStatementRequest(dynamoDB, "UPDATE Music SET AwardDetail.Grammys =list_append(AwardDetail.Grammys,[2016])  where Artist=? and SongTitle=?", parameters));

            //Remove a list value for an item in the Music table.
            processResults(executeStatementRequest(dynamoDB, "UPDATE Music REMOVE AwardDetail.Grammys[2]   where Artist=? and SongTitle=?", parameters));

            //Add a new map member for an item in the Music table.
            processResults(executeStatementRequest(dynamoDB, "UPDATE Music set AwardDetail.BillBoard=[2020] where Artist=? and SongTitle=?", parameters));

            //Add a new string set attribute for an item in the Music table.
            processResults(executeStatementRequest(dynamoDB, "UPDATE Music SET BandMembers =<<'member1', 'member2'>> where Artist=? and SongTitle=?", parameters));

            //update a string set attribute for an item in the Music table.
            processResults(executeStatementRequest(dynamoDB, "UPDATE Music SET BandMembers =set_add(BandMembers, <<'newmember'>>) where Artist=? and SongTitle=?", parameters));

            //Retrieve an item from the Music table using the SELECT PartiQL statement.
            processResults(executeStatementRequest(dynamoDB, "SELECT * FROM Music  where Artist=? and SongTitle=?", parameters));

            //delete an item from the Music Table
            processResults(executeStatementRequest(dynamoDB, "DELETE  FROM Music  where Artist=? and SongTitle=?", parameters));
        } catch (Exception e) {
            handleExecuteStatementErrors(e);
        }
    }

    private static AmazonDynamoDB createDynamoDbClient(String region) {
        return AmazonDynamoDBClientBuilder.standard().withRegion(region).build();
    }

    private static List<AttributeValue> getPartiQLParameters() {
        List<AttributeValue> parameters = new ArrayList<AttributeValue>();
        parameters.add(new AttributeValue("Acme Band"));
        parameters.add(new AttributeValue("PartiQL Rocks"));
        return parameters;
    }

    private static ExecuteStatementResult executeStatementRequest(AmazonDynamoDB client, String statement, List<AttributeValue> parameters ) {
        ExecuteStatementRequest request = new ExecuteStatementRequest();
        request.setStatement(statement);
        request.setParameters(parameters);
        return client.executeStatement(request);
    }

    private static void processResults(ExecuteStatementResult executeStatementResult) {
        System.out.println("ExecuteStatement successful: "+ executeStatementResult.toString());

    }

    // Handles errors during ExecuteStatement execution. Use recommendations in error messages below to add error handling specific to
    // your application use-case.
    private static void handleExecuteStatementErrors(Exception exception) {
        try {
            throw exception;
        } catch (ConditionalCheckFailedException ccfe) {
            System.out.println("Condition check specified in the operation failed, review and update the condition " +
                                       "check before retrying. Error: " + ccfe.getErrorMessage());
        } catch (TransactionConflictException tce) {
            System.out.println("Operation was rejected because there is an ongoing transaction for the item, generally " +
                                       "safe to retry with exponential back-off. Error: " + tce.getErrorMessage());
        } catch (ItemCollectionSizeLimitExceededException icslee) {
            System.out.println("An item collection is too large, you\'re using Local Secondary Index and exceeded " +
                                       "size limit of items per partition key. Consider using Global Secondary Index instead. Error: " + icslee.getErrorMessage());
        } catch (Exception e) {
            handleCommonErrors(e);
        }
    }

    private static void handleCommonErrors(Exception exception) {
        try {
            throw exception;
        } catch (InternalServerErrorException isee) {
            System.out.println("Internal Server Error, generally safe to retry with exponential back-off. Error: " + isee.getErrorMessage());
        } catch (RequestLimitExceededException rlee) {
            System.out.println("Throughput exceeds the current throughput limit for your account, increase account level throughput before " +
                                       "retrying. Error: " + rlee.getErrorMessage());
        } catch (ProvisionedThroughputExceededException ptee) {
            System.out.println("Request rate is too high. If you're using a custom retry strategy make sure to retry with exponential back-off. " +
                                       "Otherwise consider reducing frequency of requests or increasing provisioned capacity for your table or secondary index. Error: " +
                                       ptee.getErrorMessage());
        } catch (ResourceNotFoundException rnfe) {
            System.out.println("One of the tables was not found, verify table exists before retrying. Error: " + rnfe.getErrorMessage());
        } catch (AmazonServiceException ase) {
            System.out.println("An AmazonServiceException occurred, indicates that the request was correctly transmitted to the DynamoDB " +
                                       "service, but for some reason, the service was not able to process it, and returned an error response instead. Investigate and " +
                                       "configure retry strategy. Error type: " + ase.getErrorType() + ". Error message: " + ase.getErrorMessage());
        } catch (AmazonClientException ace) {
            System.out.println("An AmazonClientException occurred, indicates that the client was unable to get a response from DynamoDB " +
                                       "service, or the client was unable to parse the response from the service. Investigate and configure retry strategy. "+
                                       "Error: " + ace.getMessage());
        } catch (Exception e) {
            System.out.println("An exception occurred, investigate and configure retry strategy. Error: " + e.getMessage());
        }
    }

}
```

------

## パラメータ化されたステートメントの使用
<a name="ql-gettingstarted.parameterized"></a>

PartiQL ステートメント文字列に値を直接埋め込む代わりに、疑問符 (`?`) プレースホルダーを使用して、`Parameters` フィールドに値を個別に指定できます。各 `?` は、指定された順序で、対応するパラメータ値に置き換えられます。

パラメータ化されたステートメントを使用すると、ステートメント構造がデータ値から分離され、ステートメントの読み取りと再利用が容易になるため、ベストプラクティスです。また、ステートメント文字列の属性値を手動でフォーマットしたりエスケープしたりする必要もなくなります。

パラメータ化されたステートメントは `ExecuteStatement`、`BatchExecuteStatement`、および `ExecuteTransaction` オペレーションでサポートされています。

次の例では、パーティションキーとソートキーのパラメータ化された値を使用して、`Music` テーブルから項目を取得します。

------
#### [ AWS CLI parameterized ]

```
aws dynamodb execute-statement \
    --statement "SELECT * FROM \"Music\" WHERE Artist=? AND SongTitle=?" \
    --parameters '[{"S": "Acme Band"}, {"S": "PartiQL Rocks"}]'
```

------
#### [ Java parameterized ]

```
List<AttributeValue> parameters = new ArrayList<>();
parameters.add(new AttributeValue("Acme Band"));
parameters.add(new AttributeValue("PartiQL Rocks"));

ExecuteStatementRequest request = new ExecuteStatementRequest()
    .withStatement("SELECT * FROM Music WHERE Artist=? AND SongTitle=?")
    .withParameters(parameters);

ExecuteStatementResult result = dynamoDB.executeStatement(request);
```

------
#### [ Python parameterized ]

```
response = dynamodb_client.execute_statement(
    Statement="SELECT * FROM Music WHERE Artist=? AND SongTitle=?",
    Parameters=[
        {'S': 'Acme Band'},
        {'S': 'PartiQL Rocks'}
    ]
)
```

------

**注記**  
前の開始方法セクションの Java の例では、全体にわたってパラメータ化されたステートメントを使用しています。`getPartiQLParameters()` メソッドはパラメータリストを構築し、各ステートメントはインライン値の代わりに `?` プレースホルダーを使用します。

# DynamoDB の PartiQL データ型
<a name="ql-reference.data-types"></a>

次の表に、DynamoDB 用の PartiQL で使用できるデータ型を、一覧で表示します。

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/ql-reference.data-types.html)

## 例
<a name="ql-reference.data-types"></a>

次のデータ型を挿入する方法を、次のステートメントで示します。`String`、`Number`、`Map`、`List`、`Number Set` および `String Set`。

```
INSERT INTO TypesTable value {'primarykey':'1', 
'NumberType':1,
'MapType' : {'entryname1': 'value', 'entryname2': 4}, 
'ListType': [1,'stringval'], 
'NumberSetType':<<1,34,32,4.5>>, 
'StringSetType':<<'stringval','stringval2'>>
}
```

次のステートメントでは、`Map`、`List`、`Number Set` および `String Set` の型に、新しい要素を挿入する方法を示しています。また、`Number` 型の値を変更する方法も示しています。

```
UPDATE TypesTable 
SET NumberType=NumberType + 100 
SET MapType.NewMapEntry=[2020, 'stringvalue', 2.4]
SET ListType = LIST_APPEND(ListType, [4, <<'string1', 'string2'>>])
SET NumberSetType= SET_ADD(NumberSetType, <<345, 48.4>>)
SET StringSetType = SET_ADD(StringSetType, <<'stringsetvalue1', 'stringsetvalue2'>>)
WHERE primarykey='1'
```

次のステートメントでは、`Map`、`List`、`Number Set` および `String Set` の型から要素を削除する方法を示しています。また、`Number` 型の値を変更する方法も示しています。

```
UPDATE TypesTable 
SET NumberType=NumberType - 1
REMOVE ListType[1]
REMOVE MapType.NewMapEntry
SET NumberSetType = SET_DELETE( NumberSetType, <<345>>)
SET StringSetType = SET_DELETE( StringSetType, <<'stringsetvalue1'>>)
WHERE primarykey='1'
```

詳細については、「[DynamoDB のデータ型](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes)」を参照してください。

# DynamoDB 用の PartiQL ステートメント
<a name="ql-reference.statements"></a>

Amazon DynamoDB は、次の PartiQL ステートメントをサポートしています。

**注記**  
DynamoDB はすべての PartiQL ステートメントをサポートしているわけではありません。  
このリファレンスでは、AWS CLI シェルまたは API で手動で実行する PartiQL ステートメントについて、ベーシックな構文と使用例を示します。

*データ操作言語* (DML) は、DynamoDB テーブル内のデータを管理するために使用する、PartiQL ステートメントのセットです。DML ステートメントを使用して、テーブル内のデータを追加、変更、または削除します。

次の DML ステートメントとクエリ言語ステートメントがサポートされています。
+ [DynamoDB 用の PartiQL select ステートメント](ql-reference.select.md)
+ [DynamoDB 用の PartiQL 更新ステートメント](ql-reference.update.md)
+ [DynamoDB 用の PartiQL 挿入ステートメント](ql-reference.insert.md)
+ [DynamoDB 用の PartiQL 削除ステートメント](ql-reference.delete.md)

[DynamoDB 用の PartiQL を使用してトランザクションを実行する](ql-reference.multiplestatements.transactions.md) および [DynamoDB 用の PartiQL を使用してバッチ操作を実行する](ql-reference.multiplestatements.batching.md) は、DynamoDB 用の PartiQL でもサポートされています。

# DynamoDB 用の PartiQL select ステートメント
<a name="ql-reference.select"></a>

Amazon DynamoDB では、`SELECT` ステートメントを使用して、テーブルからデータを取得します。

`SELECT` ステートメントを使用すると、パーティションキーを持つ等価条件または IN 条件が WHERE 句で指定されていない場合、完全なテーブルスキャンになることがあります。スキャンオペレーションは、すべての項目でリクエストされた値を調べるので、大きなテーブルまたはインデックスに対してプロビジョニングされたスループットを 1 回のオペレーションで使い果たすことがあります。

PartiQL で完全なテーブルスキャンを避けたい場合は、次のようにします。
+ 完全なテーブルスキャンが行われないように、[WHERE 句の条件](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.select.html#ql-reference.select.parameters)を適切に設定していることを確かめながら、`SELECT` ステートメントを作成します。
+ DynamoDB デベロッパーガイドの [例: DynamoDB 用の PartiQL で ステートメントの選択を許可し、テーブル全体のスキャンを行うステートメントを拒否する](ql-iam.md#access-policy-ql-iam-example6) に記載されているように、IAM ポリシーを使用して、完全なテーブルスキャンを無効にします。

詳細については、「DynamoDB デベロッパーガイド」の「[データのクエリとスキャンのベストプラクティス](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-query-scan.html)」を参照してください。

**Topics**
+ [構文](#ql-reference.select.syntax)
+ [パラメータ](#ql-reference.select.parameters)
+ [例](#ql-reference.select.examples)

## 構文
<a name="ql-reference.select.syntax"></a>

```
SELECT expression  [, ...] 
FROM table[.index]
[ WHERE condition ] [ [ORDER BY key [DESC|ASC] , ...]
```

## パラメータ
<a name="ql-reference.select.parameters"></a>

***expression***  
(必須) `*` ワイルドカードからの射影、または結果セットからの 1 つ以上の属性名かドキュメントパスの射影リスト。式には、[DynamoDB での PartiQL 関数の使用](ql-functions.md) への呼び出し、または [DynamoDB での PartiQL 算術演算子、比較演算子、論理演算子](ql-operators.md) によって変更されたフィールドで構成できます。

***テーブル*\$1**  
(必須) クエリするテーブル名。

*** インデックス***  
(オプション) クエリを実行するインデックスの名前です。  
インデックスにクエリを実行するときは、テーブル名とインデックス名に二重引用符を追加する必要があります。  

```
SELECT * 
FROM "TableName"."IndexName"
```

***condition***  
(オプション) クエリの選択条件。  
`SELECT` ステートメントによって、完全なテーブルスキャンが行われないようにするには、`WHERE` 句の条件がパーティションキーを指定する必要があります。等価演算子または IN 演算子を使用します。  
例えば、`OrderID` パーティションキーと、`Address` を含むその他の非キー属性がある `Orders` テーブルがある場合、次のステートメントは完全なテーブルスキャンを実行しません。  

```
SELECT * 
FROM "Orders" 
WHERE OrderID = 100

SELECT * 
FROM "Orders" 
WHERE OrderID = 100 and Address='some address'

SELECT * 
FROM "Orders" 
WHERE OrderID = 100 or OrderID = 200

SELECT * 
FROM "Orders" 
WHERE OrderID IN [100, 300, 234]
```
ただし、次の `SELECT` ステートメントを実行すると、完全なテーブルスキャンが行われます。  

```
SELECT * 
FROM "Orders" 
WHERE OrderID > 1

SELECT * 
FROM "Orders" 
WHERE Address='some address'

SELECT * 
FROM "Orders" 
WHERE OrderID = 100 OR Address='some address'
```

***key***  
(任意) 返ってきた結果の並び替えに使用するハッシュキーまたはソートキー。デフォルトの順序は昇順 (`ASC`) です。返ってきた結果を降順に並べる場合は、`DESC` を指定します。

**注記**  
`WHERE` 句を省略すると、テーブル内のすべての項目が取得されます。

## 例
<a name="ql-reference.select.examples"></a>

次のクエリは、`Orders` テーブルでパーティションキーと `OrderID` を指定し、等価演算子を使用することで、存在する場合に項目を 1 つ返します。

```
SELECT OrderID, Total
FROM "Orders"
WHERE OrderID = 1
```

次のクエリは、OR 演算子を使用することで、特定のパーティションキーと `OrderID`、値を持つ `Orders` テーブルから、すべての項目を返します。

```
SELECT OrderID, Total
FROM "Orders"
WHERE OrderID = 1 OR OrderID = 2
```

次のクエリは、IN 演算子を使用することで、特定のパーティションキーと `OrderID`、値を持つ `Orders` テーブルから、すべての項目を返します。返ってきた結果は、`OrderID` キー属性の値によって、降順に並べられます。

```
SELECT OrderID, Total
FROM "Orders"
WHERE OrderID IN [1, 2, 3] ORDER BY OrderID DESC
```

次のクエリは、`Total` が非キー属性である場合に、`Total` が 500 以上である `Orders` テーブルからすべての項目を返す、完全なテーブルスキャンを示します。

```
SELECT OrderID, Total 
FROM "Orders"
WHERE Total > 500
```

次のクエリは、IN 演算子と非キー属性である `Total` を使用して、特定の `Total` 範囲の順序で、`Orders` テーブルからすべての項目を返す、完全なテーブルスキャンを示します。

```
SELECT OrderID, Total 
FROM "Orders"
WHERE Total IN [500, 600]
```

次のクエリは、BETWEEN 演算子と非キー属性である `Total` を使用して、特定の `Total` 範囲の順序で、`Orders` テーブルからすべての項目を返す、完全なテーブルスキャンを示します。

```
SELECT OrderID, Total 
FROM "Orders" 
WHERE Total BETWEEN 500 AND 600
```

次のクエリは、WHERE 句の条件で `CustomerID` パーティションキーと `MovieID` ソートキーを指定し、SELECT 句でドキュメントのパスを使用することで、firestick デバイスで視聴を開始した最初の日付を返します。

```
SELECT Devices.FireStick.DateWatched[0] 
FROM WatchList 
WHERE CustomerID= 'C1' AND MovieID= 'M1'
```

次のクエリは、テーブル全体のスキャンを示しています。このスキャンでは、WHERE 句の条件でドキュメントのパスを使用し、2019 年 12 月 24 日以降に初めて firestick デバイスを使用した項目のリストを返します。

```
SELECT Devices 
FROM WatchList 
WHERE Devices.FireStick.DateWatched[0] >= '12/24/19'
```

# DynamoDB 用の PartiQL 更新ステートメント
<a name="ql-reference.update"></a>

`UPDATE` ステートメントを使用して、Amazon DynamoDB テーブルの項目内にある、1 つ以上の属性の値を変更します。

**注記**  
一度に更新できる項目は 1 つだけです。1 つの DynamoDB PartiQL ステートメントを発行して、複数の項目を更新することはできません。複数項目の更新については、「[DynamoDB 用の PartiQL を使用してトランザクションを実行する](ql-reference.multiplestatements.transactions.md)」または「[DynamoDB 用の PartiQL を使用してバッチ操作を実行する](ql-reference.multiplestatements.batching.md)」を参照してください。

**Topics**
+ [構文](#ql-reference.update.syntax)
+ [パラメータ](#ql-reference.update.parameters)
+ [戻り値](#ql-reference.update.return)
+ [例](#ql-reference.update.examples)

## 構文
<a name="ql-reference.update.syntax"></a>

```
UPDATE  table  
[SET | REMOVE]  path  [=  data] […]
WHERE condition [RETURNING returnvalues]
<returnvalues>  ::= [ALL OLD | MODIFIED OLD | ALL NEW | MODIFIED NEW] *
```

## パラメータ
<a name="ql-reference.update.parameters"></a>

***テーブル*\$1**  
(必須) 修正されるデータを含んでいるテーブル。

***パス***  
(必須) 作成または変更される属性名、またはドキュメントパス。

***data***  
(必須) 属性値またはオペレーションの結果。  
SET で使用するためにサポートされている操作は、次の通りです。  
+ LIST\$1APPEND: List 型に値を追加します。
+ SET\$1ADD: 数値または文字列セットに値を追加します。
+ SET\$1DELETE: 数値または文字列セットから値を削除します。

***condition***  
(必須) 修正される項目の選択条件。この条件は、単一のプライマリキー値を解決する必要があります。

***returnvalues***  
(オプション) 属性が更新される前か、更新された後に、表示された項目の属性を取得したい場合に、`returnvalues` を使用します。有効な値は以下のとおりです。  
+ `ALL OLD *`: 更新操作の前に表示されていた項目について、すべての属性を返します。
+ `MODIFIED OLD *`: 更新操作の前に表示されていた属性について、更新された属性だけを返します。
+ `ALL NEW *`: 更新操作の後に表示される項目について、すべての属性を返します。
+ `MODIFIED NEW *`: `UpdateItem` 操作の後に表示される属性について、更新された属性だけを返します。

## 戻り値
<a name="ql-reference.update.return"></a>

`returnvalues` パラメータが指定されない限り、このステートメントは値を返しません。

**注記**  
UPDATE ステートメントの WHERE 句が、DynamoDB テーブルのどの項目も true と評価しない場合、`ConditionalCheckFailedException` が返ります。

## 例
<a name="ql-reference.update.examples"></a>

既存の項目の属性値を更新します。属性が存在しない場合は、作成されます。

次のクエリは、数値型の属性 (`AwardsWon`) とマップ型の属性 (`AwardDetail`) を追加して、`"Music"` テーブルの項目を更新します。

```
UPDATE "Music" 
SET AwardsWon=1 
SET AwardDetail={'Grammys':[2020, 2018]}  
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
```

`RETURNING ALL OLD *` を追加すると、`Update` 操作の前に表示されていた属性を返すことができます。

```
UPDATE "Music" 
SET AwardsWon=1 
SET AwardDetail={'Grammys':[2020, 2018]}  
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
RETURNING ALL OLD *
```

以下が返されます。

```
{
    "Items": [
        {
            "Artist": {
                "S": "Acme Band"
            },
            "SongTitle": {
                "S": "PartiQL Rocks"
            }
        }
    ]
}
```

`RETURNING ALL NEW *` を追加すると、`Update` 操作の後に表示されていた属性を返すことができます。

```
UPDATE "Music" 
SET AwardsWon=1 
SET AwardDetail={'Grammys':[2020, 2018]}  
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
RETURNING ALL NEW *
```

以下が返されます。

```
{
    "Items": [
        {
            "AwardDetail": {
                "M": {
                    "Grammys": {
                        "L": [
                            {
                                "N": "2020"
                            },
                            {
                                "N": "2018"
                            }
                        ]
                    }
                }
            },
            "AwardsWon": {
                "N": "1"
            }
        }
    ]
}
```

次のクエリは、`"Music"` テーブルの項目を、`AwardDetail.Grammys` リストに追加して更新します。

```
UPDATE "Music" 
SET AwardDetail.Grammys =list_append(AwardDetail.Grammys,[2016])  
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
```

次のクエリは、`"Music"` テーブルの項目を、`AwardDetail.Grammys` リストから削除して更新します。

```
UPDATE "Music" 
REMOVE AwardDetail.Grammys[2]   
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
```

次のクエリは、`"Music"` テーブルで `AwardDetail` マップに `BillBoard` を追加して、項目を更新します。

```
UPDATE "Music" 
SET AwardDetail.BillBoard=[2020] 
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
```

次のクエリは、`"Music"` テーブルで、文字列セットの属性 `BandMembers` を追加して、項目を更新します。

```
UPDATE "Music" 
SET BandMembers =<<'member1', 'member2'>> 
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
```

次のクエリは、`"Music"` テーブルで文字列セットの属性 `BandMembers` に `newbandmember` を追加して、項目を更新します。

```
UPDATE "Music" 
SET BandMembers =set_add(BandMembers, <<'newbandmember'>>) 
WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'
```

# DynamoDB 用の PartiQL 削除ステートメント
<a name="ql-reference.delete"></a>

`DELETE` ステートメントを使用して、Amazon DynamoDB テーブルにある項目を削除します。

**注記**  
一度に削除できる項目は 1 つだけです。1 つの DynamoDB PartiQL ステートメントを発行して、複数の項目を削除することはできません。複数のアイテムの削除については、「[DynamoDB 用の PartiQL を使用してトランザクションを実行する](ql-reference.multiplestatements.transactions.md)」または「[DynamoDB 用の PartiQL を使用してバッチ操作を実行する](ql-reference.multiplestatements.batching.md)」を参照してください。

**Topics**
+ [構文](#ql-reference.delete.syntax)
+ [パラメータ](#ql-reference.delete.parameters)
+ [戻り値](#ql-reference.delete.return)
+ [例](#ql-reference.delete.examples)

## 構文
<a name="ql-reference.delete.syntax"></a>

```
DELETE FROM table 
 WHERE condition [RETURNING returnvalues]
 <returnvalues>  ::= ALL OLD *
```

## パラメータ
<a name="ql-reference.delete.parameters"></a>

***テーブル*\$1**  
(必須) 削除する項目を含む DynamoDB テーブル。

***condition***  
(必須) 削除する項目の選択基準。この条件は、単一のプライマリキー値を解決する必要があります。

***returnvalues***  
(オプション) 削除される前に表示された、項目の属性を取得したい場合には、`returnvalues` を使用します。有効な値は以下のとおりです。  
+ `ALL OLD *`: 古い項目の内容が返されます。

## 戻り値
<a name="ql-reference.delete.return"></a>

`returnvalues` パラメータが指定されない限り、このステートメントは値を返しません。

**注記**  
DynamoDB テーブルに、DELETE が発行された項目と同じプライマリキーを持つ項目が無い場合、削除した項目を 0 としたうえで、SUCCESS を返します。テーブルに同じプライマリキーを持つ項目があるが、DELETE ステートメントの WHERE 句の条件で false と評価された場合、`ConditionalCheckFailedException` が返されます。

## 例
<a name="ql-reference.delete.examples"></a>

次のクエリは、`"Music"` テーブルの項目を削除します。

```
DELETE FROM "Music" WHERE "Artist" = 'Acme Band' AND "SongTitle" = 'PartiQL Rocks'
```

`RETURNING ALL OLD *` を追加すると、削除されたデータを返すことができます。

```
DELETE FROM "Music" WHERE "Artist" = 'Acme Band' AND "SongTitle" = 'PartiQL Rocks' RETURNING ALL OLD *
```

`Delete` ステートメントは、以下を返すようになりました。

```
{
    "Items": [
        {
            "Artist": {
                "S": "Acme Band"
            },
            "SongTitle": {
                "S": "PartiQL Rocks"
            }
        }
    ]
}
```

# DynamoDB 用の PartiQL 挿入ステートメント
<a name="ql-reference.insert"></a>

`INSERT` ステートメントを使用して、Amazon DynamoDB のテーブルに項目を追加します。

**注記**  
一度に挿入できる項目は 1 つだけです。1 つの DynamoDB PartiQL ステートメントを発行して、複数の項目を挿入することはできません。複数の項目の挿入については、「[DynamoDB 用の PartiQL を使用してトランザクションを実行する](ql-reference.multiplestatements.transactions.md)」または「[DynamoDB 用の PartiQL を使用してバッチ操作を実行する](ql-reference.multiplestatements.batching.md)」を参照してください。

**Topics**
+ [構文](#ql-reference.insert.syntax)
+ [パラメータ](#ql-reference.insert.parameters)
+ [戻り値](#ql-reference.insert.return)
+ [例](#ql-reference.insert.examples)

## 構文
<a name="ql-reference.insert.syntax"></a>

項目を 1 つ挿入します。

```
INSERT INTO table VALUE item
```

## パラメータ
<a name="ql-reference.insert.parameters"></a>

***テーブル*\$1**  
(必須) データを挿入するテーブル。このテーブルは既存であることが必要です。

***item***  
(必須) 有効な DynamoDB 項目は [PartiQL タプル](https://partiql.org/docs.html)。*1 つ*の項目のみ指定する必要があります。また、項目の各属性名は大文字と小文字が区別され、PartiQL では*一重*引用符 (`'...'`) で示されます。  
文字列値は、PartiQL では*一重*引用符 (`'...'`) で示されます。

## 戻り値
<a name="ql-reference.insert.return"></a>

このステートメントは値を返しません。

**注記**  
DynamoDB テーブルに、挿入される項目と同じプライマリキーを持つ項目が既にある場合、`DuplicateItemException` が返されます。

## 例
<a name="ql-reference.insert.examples"></a>

```
INSERT INTO "Music" value {'Artist' : 'Acme Band','SongTitle' : 'PartiQL Rocks'}
```

# DynamoDB での PartiQL 関数の使用
<a name="ql-functions"></a>

Amazon DynamoDB の PartiQL は、次の SQL 標準関数の組み込みバージョンをサポートしています。

**注記**  
このリストに含まれていない SQL 関数は、DynamoDB で現在サポートされていません。

## 集計関数
<a name="ql-functions.aggregate"></a>
+ [Amazon DynamoDB 用の PartiQL で SIZE 関数を使用する](ql-functions.size.md)

## 条件関数
<a name="ql-functions.conditional"></a>
+ [DynamoDB 用の PartiQL での EXISTS 関数の使用](ql-functions.exists.md)
+ [DynamoDB 用の PartiQL で ATTRIBUTE\$1TYPE 関数を使用する](ql-functions.attribute_type.md)
+ [DynamoDB 用の PartiQL で BEGINS\$1WITH 関数を使用する](ql-functions.beginswith.md)
+ [DynamoDB 用の PartiQL で CONTAINS 関数を使用する](ql-functions.contains.md)
+ [DynamoDB 用の PartiQL で MISSING 関数を使用する](ql-functions.missing.md)

# DynamoDB 用の PartiQL での EXISTS 関数の使用
<a name="ql-functions.exists"></a>

EXISTS を使用して、`ConditionCheck` が [TransactWriteItems](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txwriteitems) API で実行するのと同じ機能を実行できます。EXISTS 関数は、トランザクションでのみ使用できます。

値が指定されると、値が空でないコレクションである場合に `TRUE` を返します。それ以外の場合は `FALSE` を返します。

**注記**  
この関数は、トランザクション操作でのみ使用できます。

## 構文
<a name="ql-functions.exists.syntax"></a>

```
EXISTS ( statement )
```

## 引数
<a name="ql-functions.exists.arguments"></a>

*statement*  
(必須) 関数が評価する SELECT ステートメント。  
SELECT 文では、完全なプライマリキーと他の条件を、1 つ指定する必要があります。

## 戻り型
<a name="ql-functions.exists.return-type"></a>

`bool`

## 例
<a name="ql-functions.exists.examples"></a>

```
EXISTS(
    SELECT * FROM "Music" 
    WHERE "Artist" = 'Acme Band' AND "SongTitle" = 'PartiQL Rocks')
```

# DynamoDB 用の PartiQL で BEGINS\$1WITH 関数を使用する
<a name="ql-functions.beginswith"></a>

指定した属性が特定の部分文字列から始まる場合、`TRUE` を返します。

## 構文
<a name="ql-functions.beginswith.syntax"></a>

```
begins_with(path, value )
```

## 引数
<a name="ql-functions.beginswith.arguments"></a>

*パス*  
(必須) 使用する属性名またはドキュメントのパス。

*value*  
(必須) 検索対象の文字列。

## 戻り型
<a name="ql-functions.beginswith.return-type"></a>

`bool`

## 例
<a name="ql-functions.beginswith.examples"></a>

```
SELECT * FROM "Orders" WHERE "OrderID"=1 AND begins_with("Address", '7834 24th')
```

# DynamoDB 用の PartiQL で MISSING 関数を使用する
<a name="ql-functions.missing"></a>

指定した属性が項目に含まれていない場合、`TRUE` を返します。この関数では、等価演算子および不等価演算子のみを使用できます。

## 構文
<a name="ql-functions.missing.syntax"></a>

```
 attributename IS | IS NOT  MISSING 
```

## 引数
<a name="ql-functions.missing.arguments"></a>

*attributename*  
(必須) 検索対象の属性名。

## 戻り型
<a name="ql-functions.missing.return-type"></a>

`bool`

## 例
<a name="ql-functions.missing.examples"></a>

```
SELECT * FROM Music WHERE "Awards" is MISSING
```

# DynamoDB 用の PartiQL で ATTRIBUTE\$1TYPE 関数を使用する
<a name="ql-functions.attribute_type"></a>

指定したパスの属性が特定のデータ型のものである場合、`TRUE` を返します。

## 構文
<a name="ql-functions.attribute_type.syntax"></a>

```
attribute_type( attributename, type )
```

## 引数
<a name="ql-functions.attribute_type.arguments"></a>

*attributename*  
(必須) 使用する属性名。

*type*  
(必須) チェックする属性タイプ。有効な値のリストについては、DynamoDB の「[attribute\$1type](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions)」を参照してください。

## 戻り型
<a name="ql-functions.attribute_type.return-type"></a>

`bool`

## 例
<a name="ql-functions.attribute_type.examples"></a>

```
SELECT * FROM "Music" WHERE attribute_type("Artist", 'S')
```

# DynamoDB 用の PartiQL で CONTAINS 関数を使用する
<a name="ql-functions.contains"></a>

パスで指定した属性が次のいずれかである場合、`TRUE` を返します。
+ 特定の部分文字列を含む文字列型。
+ 特定の要素を含むセット型。

詳細については、DynamoDB の「[contains](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions) 関数」を参照してください。

## 構文
<a name="ql-functions.contains.syntax"></a>

```
contains( path, substring )
```

## 引数
<a name="ql-functions.contains.arguments"></a>

*パス*  
(必須) 使用する属性名またはドキュメントのパス。

*substring*  
(必須) チェックする対象の属性の部分文字列またはセットのメンバー。詳細については、DynamoDB の「[contains](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions) 関数」を参照してください。

## 戻り型
<a name="ql-functions.contains.return-type"></a>

`bool`

## 例
<a name="ql-functions.contains.examples"></a>

```
SELECT * FROM "Orders" WHERE "OrderID"=1 AND contains("Address", 'Kirkland')
```

# Amazon DynamoDB 用の PartiQL で SIZE 関数を使用する
<a name="ql-functions.size"></a>

属性のサイズを表す数値をバイト単位で返します。Size で使用できる有効なデータ型は、次の通りです。詳細については、DynamoDB の「[size](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions) 関数」を参照してください。

## 構文
<a name="ql-functions.size.syntax"></a>

```
size( path)
```

## 引数
<a name="ql-functions.size.arguments"></a>

*パス*  
(必須) 属性名またはドキュメントのパス。  
サポートされているタイプについては、DynamoDB の「[size](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions) 関数」を参照してください。

## 戻り型
<a name="ql-functions.size.return-type"></a>

`int`

## 例
<a name="ql-functions.size.examples"></a>

```
 SELECT * FROM "Orders" WHERE "OrderID"=1 AND size("Image") >300
```

# DynamoDB での PartiQL 算術演算子、比較演算子、論理演算子
<a name="ql-operators"></a>

Amazon DynamoDB の PartiQL は、次の [SQL 標準演算子](https://www.w3schools.com/sql/sql_operators.asp)をサポートしています。

**注記**  
このリストに含まれていない SQL 演算子は、DynamoDB で現在サポートされていません。

## 算術演算子
<a name="ql-operators.arithmetic"></a>


****  

| 演算子 | 説明 | 
| --- | --- | 
| \$1 | Add | 
| - | - (減算) | 

## 比較演算子
<a name="ql-operators.comparison"></a>


****  

| 演算子 | 説明 | 
| --- | --- | 
| = | Equal to | 
| <> | 等しくない | 
| \$1= | 等しくない | 
| > | 超 | 
| < | 未満 | 
| >= | 以上 | 
| <= | 以下 | 

## 論理演算子
<a name="ql-operators.logical"></a>


****  

| 演算子 | 説明 | 
| --- | --- | 
| AND | TRUE AND で区切られたすべての条件が TRUE の場合に | 
| BETWEEN |  `TRUE` オペランドが比較の範囲内にある場合に この演算子には、適用するオペランドの下限と上限が含まれます。  | 
| IN | `TRUE` オペランドが (最大 50 ハッシュ属性値、または 100 個の非キー属性値において) 式のリストの 1 つに等しい場合。 結果は最大 10 項目までのページで返されます。`IN` リストにより多くの値が含まれている場合は、レスポンスで返された `NextToken` を使用して後続のページを取得する必要があります。 | 
| IS | TRUE オペランドが指定されていて、PartiQL のデータ型であり、NULL または MISSING を含む場合に | 
| NOT | 指定されたブール式の値を反転する | 
| OR | TRUE OR で区切られた条件のいずれかが TRUEの場合に | 

論理演算子の使用の詳細については、「[比較の実行](Expressions.OperatorsAndFunctions.md#Expressions.OperatorsAndFunctions.Comparators)」および「[論理評価](Expressions.OperatorsAndFunctions.md#Expressions.OperatorsAndFunctions.LogicalEvaluations)」を参照してください。

# DynamoDB 用の PartiQL を使用してトランザクションを実行する
<a name="ql-reference.multiplestatements.transactions"></a>

このセクションでは、DynamoDB 用の PartiQL でトランザクションを使用する方法について説明します。PartiQL トランザクションは、合計 100 件のステートメント (アクション) に制限されています。

DynamoDB トランザクションの詳細については、「[DynamoDB トランザクションで複雑なワークフローを管理する](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transactions.html)」を参照してください。

**注記**  
トランザクション全体は、読み込みステートメントまたは 書き込みステートメントのいずれかで構成されている必要があります。1 つのトランザクションで両方を混在させることはできません。この EXISTS 関数は例外です。[TransactWriteItems](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txwriteitems) API オペレーションの `ConditionCheck` と同様に、項目の特定の属性の状態を確認するために使用できます。

**Topics**
+ [構文](#ql-reference.multiplestatements.transactions.syntax)
+ [パラメータ](#ql-reference.multiplestatements.transactions.parameters)
+ [戻り値](#ql-reference.multiplestatements.transactions.return)
+ [例](#ql-reference.multiplestatements.transactions.examples)

## 構文
<a name="ql-reference.multiplestatements.transactions.syntax"></a>

```
[
   {
      "Statement":" statement ",
      "Parameters":[
         {
            " parametertype " : " parametervalue "
         }, ...]
   } , ...
]
```

## パラメータ
<a name="ql-reference.multiplestatements.transactions.parameters"></a>

***ステートメント***  
(必須) DynamoDB 用の PartiQL がサポートするステートメントです。  
トランザクション全体は、読み込みステートメントまたは 書き込みステートメントのいずれかで構成されている必要があります。1 つのトランザクションで両方を混在させることはできません。

***parametertype***  
(オプション) PartiQL ステートメントを指定するときにパラメータが使用された場合のDynamoDB タイプ。

***parametervalue***  
(オプション) PartiQL ステートメントを指定するときにパラメータが使用された場合のパラメータの値。

## 戻り値
<a name="ql-reference.multiplestatements.transactions.return"></a>

このステートメントは、書き込み操作 (INSERT、UPDATE、DELETE) の値を返しません。ただし、WHERE 句で指定された条件に基づいて、読み取り操作 (SELECT) に対して異なる値を返します。

**注記**  
シングルトンの INSERT、UPDATE、または DELETE 操作のいずれかでエラーが返された場合、`TransactionCanceledException` 例外によりトランザクションがキャンセルされます。キャンセル理由コードには、個々のシングルトン操作からのエラーが含まれます。

## 例
<a name="ql-reference.multiplestatements.transactions.examples"></a>

以下の例では、トランザクションとして複数のステートメントを実行します。

------
#### [ AWS CLI ]

1. 次の JSON コードを partiql.json というファイルに保存します 

   ```
   [
       {
           "Statement": "EXISTS(SELECT * FROM \"Music\" where Artist='No One You Know' and SongTitle='Call Me Today' and Awards is  MISSING)"
       },
       {
           "Statement": "INSERT INTO Music value {'Artist':?,'SongTitle':'?'}",
           "Parameters": [{\"S\": \"Acme Band\"}, {\"S\": \"Best Song\"}]
       },
       {
           "Statement": "UPDATE \"Music\" SET AwardsWon=1 SET AwardDetail={'Grammys':[2020, 2018]}  where Artist='Acme Band' and SongTitle='PartiQL Rocks'"
       }
   ]
   ```

1. コマンドプロンプトで、次のコマンドを実行します。

   ```
   aws dynamodb execute-transaction --transact-statements  file://partiql.json
   ```

------
#### [ Java ]

```
public class DynamoDBPartiqlTransaction {

    public static void main(String[] args) {
        // Create the DynamoDB Client with the region you want
        AmazonDynamoDB dynamoDB = createDynamoDbClient("us-west-2");
        
        try {
            // Create ExecuteTransactionRequest
            ExecuteTransactionRequest executeTransactionRequest = createExecuteTransactionRequest();
            ExecuteTransactionResult executeTransactionResult = dynamoDB.executeTransaction(executeTransactionRequest);
            System.out.println("ExecuteTransaction successful.");
            // Handle executeTransactionResult

        } catch (Exception e) {
            handleExecuteTransactionErrors(e);
        }
    }

    private static AmazonDynamoDB createDynamoDbClient(String region) {
        return AmazonDynamoDBClientBuilder.standard().withRegion(region).build();
    }

    private static ExecuteTransactionRequest createExecuteTransactionRequest() {
        ExecuteTransactionRequest request = new ExecuteTransactionRequest();
        
        // Create statements
        List<ParameterizedStatement> statements = getPartiQLTransactionStatements();

        request.setTransactStatements(statements);
        return request;
    }

    private static List<ParameterizedStatement> getPartiQLTransactionStatements() {
        List<ParameterizedStatement> statements = new ArrayList<ParameterizedStatement>();

        statements.add(new ParameterizedStatement()
                               .withStatement("EXISTS(SELECT * FROM "Music" where Artist='No One You Know' and SongTitle='Call Me Today' and Awards is  MISSING)"));

        statements.add(new ParameterizedStatement()
                               .withStatement("INSERT INTO "Music" value {'Artist':'?','SongTitle':'?'}")
                               .withParameters(new AttributeValue("Acme Band"),new AttributeValue("Best Song")));

        statements.add(new ParameterizedStatement()
                               .withStatement("UPDATE "Music" SET AwardsWon=1 SET AwardDetail={'Grammys':[2020, 2018]}  where Artist='Acme Band' and SongTitle='PartiQL Rocks'"));

        return statements;
    }

    // Handles errors during ExecuteTransaction execution. Use recommendations in error messages below to add error handling specific to 
    // your application use-case.
    private static void handleExecuteTransactionErrors(Exception exception) {
        try {
            throw exception;
        } catch (TransactionCanceledException tce) {
            System.out.println("Transaction Cancelled, implies a client issue, fix before retrying. Error: " + tce.getErrorMessage());
        } catch (TransactionInProgressException tipe) {
            System.out.println("The transaction with the given request token is already in progress, consider changing " +
                "retry strategy for this type of error. Error: " + tipe.getErrorMessage());
        } catch (IdempotentParameterMismatchException ipme) {
            System.out.println("Request rejected because it was retried with a different payload but with a request token that was already used, " +
                "change request token for this payload to be accepted. Error: " + ipme.getErrorMessage());
        } catch (Exception e) {
            handleCommonErrors(e);
        }
    }

    private static void handleCommonErrors(Exception exception) {
        try {
            throw exception;
        } catch (InternalServerErrorException isee) {
            System.out.println("Internal Server Error, generally safe to retry with exponential back-off. Error: " + isee.getErrorMessage());
        } catch (RequestLimitExceededException rlee) {
            System.out.println("Throughput exceeds the current throughput limit for your account, increase account level throughput before " + 
                "retrying. Error: " + rlee.getErrorMessage());
        } catch (ProvisionedThroughputExceededException ptee) {
            System.out.println("Request rate is too high. If you're using a custom retry strategy make sure to retry with exponential back-off. " +
                "Otherwise consider reducing frequency of requests or increasing provisioned capacity for your table or secondary index. Error: " + 
                ptee.getErrorMessage());
        } catch (ResourceNotFoundException rnfe) {
            System.out.println("One of the tables was not found, verify table exists before retrying. Error: " + rnfe.getErrorMessage());
        } catch (AmazonServiceException ase) {
            System.out.println("An AmazonServiceException occurred, indicates that the request was correctly transmitted to the DynamoDB " + 
                "service, but for some reason, the service was not able to process it, and returned an error response instead. Investigate and " +
                "configure retry strategy. Error type: " + ase.getErrorType() + ". Error message: " + ase.getErrorMessage());
        } catch (AmazonClientException ace) {
            System.out.println("An AmazonClientException occurred, indicates that the client was unable to get a response from DynamoDB " +
                "service, or the client was unable to parse the response from the service. Investigate and configure retry strategy. "+
                "Error: " + ace.getMessage());
        } catch (Exception e) {
            System.out.println("An exception occurred, investigate and configure retry strategy. Error: " + e.getMessage());
        }
    }

}
```

------

次の例は、DynamoDB が WHERE 句で指定された異なる条件を持つ項目を読み取るときのさまざまな戻り値を示しています。

------
#### [ AWS CLI ]

1. 次の JSON コードを partiql.json というファイルに保存します

   ```
   [
       // Item exists and projected attribute exists
       {
           "Statement": "SELECT * FROM "Music" WHERE Artist='No One You Know' and SongTitle='Call Me Today'"
       },
       // Item exists but projected attributes do not exist
       {
           "Statement": "SELECT non_existent_projected_attribute FROM "Music" WHERE Artist='No One You Know' and SongTitle='Call Me Today'"
       },
       // Item does not exist
       {
           "Statement": "SELECT * FROM "Music" WHERE Artist='No One I Know' and SongTitle='Call You Today'"
       }
   ]
   ```

1.  コマンドプロンプトで、次のコマンドを実行します。

   ```
   aws dynamodb execute-transaction --transact-statements  file://partiql.json
   ```

1. 以下のレスポンスが返されます。

   ```
   {
       "Responses": [
           // Item exists and projected attribute exists
           {
               "Item": {
                   "Artist":{
                       "S": "No One You Know"
                   },
                   "SongTitle":{
                       "S": "Call Me Today"
                   }    
               }
           },
           // Item exists but projected attributes do not exist
           {
               "Item": {}
           },
           // Item does not exist
           {}
       ]
   }
   ```

------

# DynamoDB 用の PartiQL を使用してバッチ操作を実行する
<a name="ql-reference.multiplestatements.batching"></a>

このセクションでは、DynamoDB 用の PartiQL でバッチステートメントを使用する方法について説明します。

**注記**  
バッチ全体は、読み取りステートメントまたは書き込みステートメントのいずれかで構成する必要があります。1 つのバッチに両方を混在させることはできません。
`BatchExecuteStatement` と `BatchWriteItem` が実行できるステートメントの数は、バッチあたり 25 が上限です。
`BatchExecuteStatement` は、`BatchGetItem` を使用して、個別のステートメントでプライマリキーのリストを取得します。

**Topics**
+ [構文](#ql-reference.multiplestatements.batching.syntax)
+ [パラメータ](#ql-reference.multiplestatements.batching.parameters)
+ [例](#ql-reference.multiplestatements.batching.examples)

## 構文
<a name="ql-reference.multiplestatements.batching.syntax"></a>

```
[
  {
    "Statement": "SELECT pk FROM ProblemSet WHERE pk = 'p#9StkWHYTxm7x2AqSXcrfu7' AND sk = 'info'"
  },
  {
    "Statement": "SELECT pk FROM ProblemSet WHERE pk = 'p#isC2ChceGbxHgESc4szoTE' AND sk = 'info'"
  }
]
```

```
[
   {
      "Statement":" statement ",
      "Parameters":[
         {
            " parametertype " : " parametervalue "
         }, ...]
   } , ...
]
```

## パラメータ
<a name="ql-reference.multiplestatements.batching.parameters"></a>

***ステートメント***  
(必須) DynamoDB 用の PartiQL がサポートするステートメントです。  
+ バッチ全体は、読み取りステートメントまたは書き込みステートメントのいずれかで構成する必要があります。1 つのバッチに両方を混在させることはできません。
+ `BatchExecuteStatement` と `BatchWriteItem` が実行できるステートメントの数は、バッチあたり 25 が上限です。

***parametertype***  
(オプション) PartiQL ステートメントを指定するときにパラメータが使用された場合のDynamoDB タイプ。

***parametervalue***  
(オプション) PartiQL ステートメントを指定するときにパラメータが使用された場合のパラメータの値。

## 例
<a name="ql-reference.multiplestatements.batching.examples"></a>

------
#### [ AWS CLI ]

1. 次の json を partiql.json というファイルに保存します

   ```
   [
      {
   	 "Statement": "INSERT INTO Music VALUE {'Artist':?,'SongTitle':?}",
   	  "Parameters": [{"S": "Acme Band"}, {"S": "Best Song"}]
   	},
   	{
   	 "Statement": "UPDATE Music SET AwardsWon=1, AwardDetail={'Grammys':[2020, 2018]} WHERE Artist='Acme Band' AND SongTitle='PartiQL Rocks'"
       }
   ]
   ```

1. コマンドプロンプトで、次のコマンドを実行します。

   ```
   aws dynamodb batch-execute-statement  --statements  file://partiql.json
   ```

------
#### [ Java ]

```
public class DynamoDBPartiqlBatch {

    public static void main(String[] args) {
        // Create the DynamoDB Client with the region you want
        AmazonDynamoDB dynamoDB = createDynamoDbClient("us-west-2");
        
        try {
            // Create BatchExecuteStatementRequest
            BatchExecuteStatementRequest batchExecuteStatementRequest = createBatchExecuteStatementRequest();
            BatchExecuteStatementResult batchExecuteStatementResult = dynamoDB.batchExecuteStatement(batchExecuteStatementRequest);
            System.out.println("BatchExecuteStatement successful.");
            // Handle batchExecuteStatementResult

        } catch (Exception e) {
            handleBatchExecuteStatementErrors(e);
        }
    }

    private static AmazonDynamoDB createDynamoDbClient(String region) {

        return AmazonDynamoDBClientBuilder.standard().withRegion(region).build();
    }

    private static BatchExecuteStatementRequest createBatchExecuteStatementRequest() {
        BatchExecuteStatementRequest request = new BatchExecuteStatementRequest();

        // Create statements
        List<BatchStatementRequest> statements = getPartiQLBatchStatements();

        request.setStatements(statements);
        return request;
    }

    private static List<BatchStatementRequest> getPartiQLBatchStatements() {
        List<BatchStatementRequest> statements = new ArrayList<BatchStatementRequest>();

        statements.add(new BatchStatementRequest()
                               .withStatement("INSERT INTO Music value {'Artist':'Acme Band','SongTitle':'PartiQL Rocks'}"));

        statements.add(new BatchStatementRequest()
                               .withStatement("UPDATE Music set AwardDetail.BillBoard=[2020] where Artist='Acme Band' and SongTitle='PartiQL Rocks'"));

        return statements;
    }

    // Handles errors during BatchExecuteStatement execution. Use recommendations in error messages below to add error handling specific to 
    // your application use-case.
    private static void handleBatchExecuteStatementErrors(Exception exception) {
        try {
            throw exception;
        } catch (Exception e) {
            // There are no API specific errors to handle for BatchExecuteStatement, common DynamoDB API errors are handled below
            handleCommonErrors(e);
        }
    }

    private static void handleCommonErrors(Exception exception) {
        try {
            throw exception;
        } catch (InternalServerErrorException isee) {
            System.out.println("Internal Server Error, generally safe to retry with exponential back-off. Error: " + isee.getErrorMessage());
        } catch (RequestLimitExceededException rlee) {
            System.out.println("Throughput exceeds the current throughput limit for your account, increase account level throughput before " + 
                "retrying. Error: " + rlee.getErrorMessage());
        } catch (ProvisionedThroughputExceededException ptee) {
            System.out.println("Request rate is too high. If you're using a custom retry strategy make sure to retry with exponential back-off. " +
                "Otherwise consider reducing frequency of requests or increasing provisioned capacity for your table or secondary index. Error: " + 
                ptee.getErrorMessage());
        } catch (ResourceNotFoundException rnfe) {
            System.out.println("One of the tables was not found, verify table exists before retrying. Error: " + rnfe.getErrorMessage());
        } catch (AmazonServiceException ase) {
            System.out.println("An AmazonServiceException occurred, indicates that the request was correctly transmitted to the DynamoDB " + 
                "service, but for some reason, the service was not able to process it, and returned an error response instead. Investigate and " +
                "configure retry strategy. Error type: " + ase.getErrorType() + ". Error message: " + ase.getErrorMessage());
        } catch (AmazonClientException ace) {
            System.out.println("An AmazonClientException occurred, indicates that the client was unable to get a response from DynamoDB " +
                "service, or the client was unable to parse the response from the service. Investigate and configure retry strategy. "+
                "Error: " + ace.getMessage());
        } catch (Exception e) {
            System.out.println("An exception occurred, investigate and configure retry strategy. Error: " + e.getMessage());
        }
    }

}
```

------

# DynamoDB 用 PartiQL における IAM セキュリティポリシー
<a name="ql-iam"></a>

以下のアクセス権限が必要です。
+ DynamoDB 用の PartiQL を使用して項目を読み込むには、`dynamodb:PartiQLSelect` アクセス許可を、テーブルまたはインデックスに付与します。
+ DynamoDB 用の PartiQL を使用して項目を挿入するには、`dynamodb:PartiQLInsert` アクセス許可をテーブルまたはインデックスに付与します。
+ DynamoDB 用の PartiQL を使用して項目を更新するには、`dynamodb:PartiQLUpdate` アクセス許可をテーブルまたはインデックスに付与します。
+ DynamoDB 用の PartiQL を使用して項目を削除するには、`dynamodb:PartiQLDelete` アクセス許可をテーブルまたはインデックスに付与します。

## 例: テーブルに対して、すべての DynamoDB 用の PartiQL ステートメントを許可する (選択、挿入、更新、削除)
<a name="access-policy-ql-iam-example1"></a>

次の IAM ポリシーは、すべての DynamoDB 用の PartiQL ステートメントをテーブルに対して実行するための、アクセス許可を付与します。

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "dynamodb:PartiQLInsert",
            "dynamodb:PartiQLUpdate",
            "dynamodb:PartiQLDelete",
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/Music"
         ]
      }
   ]
}
```

------

## 例: テーブルに対して、DynamoDB 用の PartiQL 選択ステートメントを許可する
<a name="access-policy-ql-iam-example2"></a>

次の IAM ポリシーは、特定のテーブルに対して `select` ステートメントを実行するためのアクセス許可を付与します。

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/Music"
         ]
      }
   ]
}
```

------

## 例: インデックスに対して、DynamoDB 用の PartiQL 挿入ステートメントを許可する
<a name="access-policy-ql-iam-example3"></a>

次の IAM ポリシーは、特定のインデックスに対して `insert` ステートメントを実行するためのアクセス許可を付与します。

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "dynamodb:PartiQLInsert"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/Music/index/index1"
         ]
      }
   ]
}
```

------

## 例: テーブルに対して、DynamoDB 用の PartiQL トランザクションステートメントを許可する
<a name="access-policy-ql-iam-example4"></a>

次の IAM ポリシーは、特定のテーブルに対してトランザクションステートメントだけを実行するためのアクセス許可を付与します。

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "dynamodb:PartiQLInsert",
            "dynamodb:PartiQLUpdate",
            "dynamodb:PartiQLDelete",
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/Music"
         ],
         "Condition":{
            "StringEquals":{
               "dynamodb:EnclosingOperation":[
                  "ExecuteTransaction"
               ]
            }
         }
      }
   ]
}
```

------

## 例: テーブルに対して、DynamoDB 用の PartiQL 非トランザクションの読み込み/書き込みステートメントを許可し、PartiQL 読み込み/書き込みトランザクションをブロックします。
<a name="access-policy-ql-iam-example5"></a>

 次の IAM ポリシーは、DynamoDB 用の PartiQL トランザクション読み込み/書き込みをブロックしている間に、DynamoDB 用の PartiQL 非トランザクションの読み込み/書き込みを実行するためのアクセス許可を付与します。

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement":[
      {
         "Effect":"Deny",
         "Action":[
            "dynamodb:PartiQLInsert",
            "dynamodb:PartiQLUpdate",
            "dynamodb:PartiQLDelete",
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/Music"
         ],
         "Condition":{
            "StringEquals":{
               "dynamodb:EnclosingOperation":[
                  "ExecuteTransaction"
               ]
            }
         }
      },
      {
         "Effect":"Allow",
         "Action":[
            "dynamodb:PartiQLInsert",
            "dynamodb:PartiQLUpdate",
            "dynamodb:PartiQLDelete",
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/Music"
         ]
      }
   ]
}
```

------

## 例: DynamoDB 用の PartiQL で ステートメントの選択を許可し、テーブル全体のスキャンを行うステートメントを拒否する
<a name="access-policy-ql-iam-example6"></a>

次の IAM ポリシーは、テーブル全体のスキャンを行う `select` ステートメントをブロックしている間に、特定のテーブルに対して `select` ステートメントを実行するためのアクセス許可を付与します。

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement":[
      {
         "Effect":"Deny",
         "Action":[
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/WatchList"
         ],
         "Condition":{
            "Bool":{
               "dynamodb:FullTableScan":[
                  "true"
               ]
            }
         }
      },
      {
         "Effect":"Allow",
         "Action":[
            "dynamodb:PartiQLSelect"
         ],
         "Resource":[
            "arn:aws:dynamodb:us-west-2:123456789012:table/WatchList"
         ]
      }
   ]
}
```

------

# 項目の操作: Java
<a name="JavaDocumentAPIItemCRUD"></a>

AWS SDK for Java ドキュメント API を使用して、テーブル内の Amazon DynamoDB 項目に対して、一般的な作成、読み込み、更新、削除 (CRUD) のオペレーションを実行できます。

**注記**  
SDK for Java には、オブジェクト永続性モデルも用意されています。このモデルにより、クライアント側のクラスを DynamoDB テーブルにマッピングすることができます。この方法により、記述する必要のあるコードの量を減らすことができます。詳細については、「」を参照してください[Java 1.x: DynamoDBMapper](DynamoDBMapper.md)

このセクションには、いくつかの Java ドキュメント API 項目のアクションを実行する Java サンプルといくつかの完全な操作例が含まれています。

**Topics**
+ [項目の置換](#PutDocumentAPIJava)
+ [項目の取得](#JavaDocumentAPIGetItem)
+ [バッチ書き込み: 複数の項目の置換および削除](#BatchWriteDocumentAPIJava)
+ [バッチ取得: 複数の項目の取得](#JavaDocumentAPIBatchGetItem)
+ [項目の更新](#JavaDocumentAPIItemUpdate)
+ [項目の削除](#DeleteMidLevelJava)
+ [例: AWS SDK for Java ドキュメント API を使用した CRUD オペレーション](JavaDocumentAPICRUDExample.md)
+ [例: AWS SDK for Java ドキュメント API を使用したバッチオペレーション](batch-operation-document-api-java.md)
+ [例: AWS SDK for Java ドキュメント API を使用したバイナリタイプ属性の処理](JavaDocumentAPIBinaryTypeExample.md)

## 項目の置換
<a name="PutDocumentAPIJava"></a>

`putItem` メソッドによって、項目をテーブルに格納します。項目が存在する場合、その項目全体が置き換えられます。項目全体を置き換える代わりに固有の属性のみを更新する場合は、`updateItem` メソッドを使用できます。詳細については、「」を参照してください[項目の更新](#JavaDocumentAPIItemUpdate) 

------
#### [ Java v2 ]

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import software.amazon.awssdk.services.dynamodb.model.PutItemResponse;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;
import java.util.HashMap;

/**
 * Before running this Java V2 code example, set up your development
 * environment, including your credentials.
 *
 * For more information, see the following documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 *
 * To place items into an Amazon DynamoDB table using the AWS SDK for Java V2,
 * its better practice to use the
 * Enhanced Client. See the EnhancedPutItem example.
 */
public class PutItem {
    public static void main(String[] args) {
        final String usage = """

                Usage:
                    <tableName> <key> <keyVal> <albumtitle> <albumtitleval> <awards> <awardsval> <Songtitle> <songtitleval>

                Where:
                    tableName - The Amazon DynamoDB table in which an item is placed (for example, Music3).
                    key - The key used in the Amazon DynamoDB table (for example, Artist).
                    keyval - The key value that represents the item to get (for example, Famous Band).
                    albumTitle - The Album title (for example, AlbumTitle).
                    AlbumTitleValue - The name of the album (for example, Songs About Life ).
                    Awards - The awards column (for example, Awards).
                    AwardVal - The value of the awards (for example, 10).
                    SongTitle - The song title (for example, SongTitle).
                    SongTitleVal - The value of the song title (for example, Happy Day).
                **Warning** This program will  place an item that you specify into a table!
                """;

        if (args.length != 9) {
            System.out.println(usage);
            System.exit(1);
        }

        String tableName = args[0];
        String key = args[1];
        String keyVal = args[2];
        String albumTitle = args[3];
        String albumTitleValue = args[4];
        String awards = args[5];
        String awardVal = args[6];
        String songTitle = args[7];
        String songTitleVal = args[8];

        Region region = Region.US_EAST_1;
        DynamoDbClient ddb = DynamoDbClient.builder()
                .region(region)
                .build();

        putItemInTable(ddb, tableName, key, keyVal, albumTitle, albumTitleValue, awards, awardVal, songTitle,
                songTitleVal);
        System.out.println("Done!");
        ddb.close();
    }

    public static void putItemInTable(DynamoDbClient ddb,
            String tableName,
            String key,
            String keyVal,
            String albumTitle,
            String albumTitleValue,
            String awards,
            String awardVal,
            String songTitle,
            String songTitleVal) {

        HashMap<String, AttributeValue> itemValues = new HashMap<>();
        itemValues.put(key, AttributeValue.builder().s(keyVal).build());
        itemValues.put(songTitle, AttributeValue.builder().s(songTitleVal).build());
        itemValues.put(albumTitle, AttributeValue.builder().s(albumTitleValue).build());
        itemValues.put(awards, AttributeValue.builder().s(awardVal).build());

        PutItemRequest request = PutItemRequest.builder()
                .tableName(tableName)
                .item(itemValues)
                .build();

        try {
            PutItemResponse response = ddb.putItem(request);
            System.out.println(tableName + " was successfully updated. The request id is "
                    + response.responseMetadata().requestId());

        } catch (ResourceNotFoundException e) {
            System.err.format("Error: The Amazon DynamoDB table \"%s\" can't be found.\n", tableName);
            System.err.println("Be sure that it exists and that you've typed its name correctly!");
            System.exit(1);
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
}
```

------
#### [ Java v1 ]

以下のステップに従ってください。

1. `DynamoDB` クラスのインスタンスを作成します。

1. 操作対象のテーブルを表すために、`Table` クラスのインスタンスを作成します。

1. 新しい項目を表す `Item` クラスのインスタンスを作成します。新しい項目のプライマリキーと属性を指定する必要があります。

1. 前の手順で作成した `putItem` を使用して、`Table` オブジェクトの `Item` メソッドを呼び出します。

以下の Java コード例は、前述のタスクの例です。このコードでは、`ProductCatalog` テーブルに新しい項目を書き込みます。

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("ProductCatalog");

// Build a list of related items
List<Number> relatedItems = new ArrayList<Number>();
relatedItems.add(341);
relatedItems.add(472);
relatedItems.add(649);

//Build a map of product pictures
Map<String, String> pictures = new HashMap<String, String>();
pictures.put("FrontView", "http://example.com/products/123_front.jpg");
pictures.put("RearView", "http://example.com/products/123_rear.jpg");
pictures.put("SideView", "http://example.com/products/123_left_side.jpg");

//Build a map of product reviews
Map<String, List<String>> reviews = new HashMap<String, List<String>>();

List<String> fiveStarReviews = new ArrayList<String>();
fiveStarReviews.add("Excellent! Can't recommend it highly enough!  Buy it!");
fiveStarReviews.add("Do yourself a favor and buy this");
reviews.put("FiveStar", fiveStarReviews);

List<String> oneStarReviews = new ArrayList<String>();
oneStarReviews.add("Terrible product!  Do not buy this.");
reviews.put("OneStar", oneStarReviews);

// Build the item
Item item = new Item()
    .withPrimaryKey("Id", 123)
    .withString("Title", "Bicycle 123")
    .withString("Description", "123 description")
    .withString("BicycleType", "Hybrid")
    .withString("Brand", "Brand-Company C")
    .withNumber("Price", 500)
    .withStringSet("Color",  new HashSet<String>(Arrays.asList("Red", "Black")))
    .withString("ProductCategory", "Bicycle")
    .withBoolean("InStock", true)
    .withNull("QuantityOnHand")
    .withList("RelatedItems", relatedItems)
    .withMap("Pictures", pictures)
    .withMap("Reviews", reviews);

// Write the item to the table
PutItemOutcome outcome = table.putItem(item);
```

前の例では、項目にはスカラー (`String`、`Number`、`Boolean`、`Null`)、セット (`String Set`)、ドキュメントタイプ (`List`、`Map`) があります。

------

### オプションパラメータの指定
<a name="PutItemJavaDocumentAPIOptions"></a>

必須のパラメータに加え、`putItem` メソッドにはオプションパラメータも指定できます。たとえば、以下の Java コード例では、オプションパラメータを使用して、項目のアップロード条件を指定します。指定した条件を満たさない場合は、AWS SDK for Java が `ConditionalCheckFailedException` をスローします。このコード例では、`putItem` メソッドに以下のオプションパラメータを指定します。
+ リクエストの条件を定義する `ConditionExpression`。このコードでは、特定の値に等しい ISBN 属性が既存の項目にある場合にのみ同じプライマリキーを持つ既存の項目を置き換えるという条件を定義します。
+ 条件で使用される `ExpressionAttributeValues` のマップ。この場合、必要な置き換えは 1 つだけです。条件式のプレースホルダー `:val` が、実行時に、チェックする実際の ISBN 値に置き換えられます。

以下の例では、これらのオプションパラメータを使用して新しい書籍項目を追加します。

**Example**  

```
Item item = new Item()
    .withPrimaryKey("Id", 104)
    .withString("Title", "Book 104 Title")
    .withString("ISBN", "444-4444444444")
    .withNumber("Price", 20)
    .withStringSet("Authors",
        new HashSet<String>(Arrays.asList("Author1", "Author2")));

Map<String, Object> expressionAttributeValues = new HashMap<String, Object>();
expressionAttributeValues.put(":val", "444-4444444444");

PutItemOutcome outcome = table.putItem(
    item,
    "ISBN = :val", // ConditionExpression parameter
    null,          // ExpressionAttributeNames parameter - we're not using it for this example
    expressionAttributeValues);
```

### PutItem および JSON ドキュメント
<a name="PutItemJavaDocumentAPI.JSON"></a>

JSON ドキュメントは、DynamoDB テーブルに属性として格納できます。これを行うには、`withJSON` 項目の `Item` メソッドを使用します。このメソッドは、JSON ドキュメントを解析し、各要素を DynamoDB のネイティブデータ型にマッピングします。

特定の製品の注文を処理できるベンダーを含む、次の JSON ドキュメントを格納するとします。

**Example**  

```
{
    "V01": {
        "Name": "Acme Books",
        "Offices": [ "Seattle" ]
    },
    "V02": {
        "Name": "New Publishers, Inc.",
        "Offices": ["London", "New York"
        ]
    },
    "V03": {
        "Name": "Better Buy Books",
        "Offices": [ "Tokyo", "Los Angeles", "Sydney"
        ]
    }
}
```

`withJSON` メソッドを使用して、`ProductCatalog` という名前の `Map` 属性でこれを `VendorInfo` テーブルに格納することができます。次の Java コード例はそれを行う方法を示しています。

```
// Convert the document into a String.  Must escape all double-quotes.
String vendorDocument = "{"
    + "    \"V01\": {"
    + "        \"Name\": \"Acme Books\","
    + "        \"Offices\": [ \"Seattle\" ]"
    + "    },"
    + "    \"V02\": {"
    + "        \"Name\": \"New Publishers, Inc.\","
    + "        \"Offices\": [ \"London\", \"New York\"" + "]" + "},"
    + "    \"V03\": {"
    + "        \"Name\": \"Better Buy Books\","
    +          "\"Offices\": [ \"Tokyo\", \"Los Angeles\", \"Sydney\""
    + "            ]"
    + "        }"
    + "    }";

Item item = new Item()
    .withPrimaryKey("Id", 210)
    .withString("Title", "Book 210 Title")
    .withString("ISBN", "210-2102102102")
    .withNumber("Price", 30)
    .withJSON("VendorInfo", vendorDocument);

PutItemOutcome outcome = table.putItem(item);
```

## 項目の取得
<a name="JavaDocumentAPIGetItem"></a>

単一の項目を取り出すには、`getItem` オブジェクトの `Table` メソッドを使用します。以下のステップに従ってください。

1. `DynamoDB` クラスのインスタンスを作成します。

1. 操作対象のテーブルを表すために、`Table` クラスのインスタンスを作成します。

1. `getItem` インスタンスの `Table` メソッドを呼び出します。取り出す項目のプライマリキーを指定する必要があります。

以下の Java コード例は、前述のステップの例です。このコードでは、指定したパーティションキーを持つ項目を取得します。

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("ProductCatalog");

Item item = table.getItem("Id", 210);
```

### オプションパラメータの指定
<a name="GetItemJavaDocumentAPIOptions"></a>

必須のパラメータに加え、`getItem` メソッドにはオプションパラメータも指定できます。たとえば、以下の Java コード例では、オプションメソッドを使用して、特定の属性リストのみを取り出します。また、強い整合性のある戻り値をリクエストします。読み込み整合性の詳細については、「[DynamoDB の読み取り整合性](HowItWorks.ReadConsistency.md)」を参照してください。

`ProjectionExpression` を使用すると、項目全体ではなく特定の属性または要素のみを取り出すことができます。`ProjectionExpression` は、ドキュメントのパスを使用して最上位の属性または入れ子になった属性を指定できます。詳細については、「」を参照してください[DynamoDB でのプロジェクション式の使用](Expressions.ProjectionExpressions.md)

`getItem` メソッドのパラメータでは、読み取りの一貫性を指定できません。ただし、`GetItemSpec` を作成して、低レベル `GetItem` オペレーションへの入力のすべてに対するフルアクセスを提供できます。以下のコード例では、`GetItemSpec` を作成し、その仕様を `getItem` メソッドへの入力として使用します。

**Example**  

```
GetItemSpec spec = new GetItemSpec()
    .withPrimaryKey("Id", 206)
    .withProjectionExpression("Id, Title, RelatedItems[0], Reviews.FiveStar")
    .withConsistentRead(true);

Item item = table.getItem(spec);

System.out.println(item.toJSONPretty());
```

 `Item` を人間が読める形式で出力するには、`toJSONPretty` メソッドを使用します。前のサンプルからの出力は、次のようになります。

```
{
  "RelatedItems" : [ 341 ],
  "Reviews" : {
    "FiveStar" : [ "Excellent! Can't recommend it highly enough! Buy it!", "Do yourself a favor and buy this" ]
  },
  "Id" : 123,
  "Title" : "20-Bicycle 123"
}
```

### GetItem および JSON ドキュメント
<a name="GetItemJavaDocumentAPI.JSON"></a>

[PutItem および JSON ドキュメント](#PutItemJavaDocumentAPI.JSON) セクションでは、`Map` という名前の `VendorInfo` 属性に JSON ドキュメントを格納します。`getItem` メソッドを使用して、ドキュメント全体を JSON 形式で取得できます。または、ドキュメントパスの表記を使用して、ドキュメントの一部の要素のみを取得することもできます。次の Java コード例は、この手法を示しています。

```
GetItemSpec spec = new GetItemSpec()
    .withPrimaryKey("Id", 210);

System.out.println("All vendor info:");
spec.withProjectionExpression("VendorInfo");
System.out.println(table.getItem(spec).toJSON());

System.out.println("A single vendor:");
spec.withProjectionExpression("VendorInfo.V03");
System.out.println(table.getItem(spec).toJSON());

System.out.println("First office location for this vendor:");
spec.withProjectionExpression("VendorInfo.V03.Offices[0]");
System.out.println(table.getItem(spec).toJSON());
```

前のサンプルからの出力は、次のようになります。

```
All vendor info:
{"VendorInfo":{"V03":{"Name":"Better Buy Books","Offices":["Tokyo","Los Angeles","Sydney"]},"V02":{"Name":"New Publishers, Inc.","Offices":["London","New York"]},"V01":{"Name":"Acme Books","Offices":["Seattle"]}}}
A single vendor:
{"VendorInfo":{"V03":{"Name":"Better Buy Books","Offices":["Tokyo","Los Angeles","Sydney"]}}}
First office location for a single vendor:
{"VendorInfo":{"V03":{"Offices":["Tokyo"]}}}
```

**注記**  
`toJSON` メソッドを使用して、任意の項目（またはその属性）を JSON 形式文字列に変換できます。次のコード例は、最上位の属性と入れ子になった属性を複数取り出し、JSON として結果を出力します。  

```
GetItemSpec spec = new GetItemSpec()
    .withPrimaryKey("Id", 210)
    .withProjectionExpression("VendorInfo.V01, Title, Price");

Item item = table.getItem(spec);
System.out.println(item.toJSON());
```
出力は次のようになります。  

```
{"VendorInfo":{"V01":{"Name":"Acme Books","Offices":["Seattle"]}},"Price":30,"Title":"Book 210 Title"}
```

## バッチ書き込み: 複数の項目の置換および削除
<a name="BatchWriteDocumentAPIJava"></a>

*バッチ書き込み*は、複数の項目の書き込みと削除をバッチで行うことを意味します。`batchWriteItem` メソッドによって、単一の コール内にある 1 つまたは複数のテーブルから複数の項目を置換および削除できます。以下に、AWS SDK for Java ドキュメント API を使用して複数の項目を入力および削除する手順を示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. テーブルのすべての入力および削除オペレーションを記述する `TableWriteItems` クラスのインスタンスを作成します。1 つのバッチ書き込みオペレーションで複数のテーブルに書き込む場合、テーブルごとに `TableWriteItems` インスタンスを 1 つずつ作成する必要があります。

1. 前の手順で作成した `batchWriteItem` オブジェクトを指定して、`TableWriteItems` メソッドを呼び出します。

1. 応答を処理します。返された未処理のリクエスト項目が応答内に存在していたかどうかをチェックする必要があります。これは、プロビジョニングされたスループットクォータまたは他の何らかの一時的エラーに達する場合に、発生する可能性があります。また、DynamoDB によって、リクエストのサイズ、およびリクエスト内で指定できるオペレーションの数が制限されます。これらの制限を超えると、DynamoDB によってリクエストが却下されます。詳細については、「[Amazon DynamoDB のクォータ](ServiceQuotas.md)」を参照してください。

以下の Java コード例は、前述のステップの例です。この例では、2 つのテーブル (`batchWriteItem` と `Forum`) で `Thread` オペレーションを実行します。対応する `TableWriteItems` オブジェクトは、次のアクションを定義します。
+ `Forum` テーブル内で項目を配置します。
+ `Thread` テーブル内で項目を入力および削除します。

その後、コードは `batchWriteItem` を呼び出してオペレーションを実行します。

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

TableWriteItems forumTableWriteItems = new TableWriteItems("Forum")
    .withItemsToPut(
        new Item()
            .withPrimaryKey("Name", "Amazon RDS")
            .withNumber("Threads", 0));

TableWriteItems threadTableWriteItems = new TableWriteItems("Thread")
    .withItemsToPut(
        new Item()
            .withPrimaryKey("ForumName","Amazon RDS","Subject","Amazon RDS Thread 1")
    .withHashAndRangeKeysToDelete("ForumName","Some partition key value", "Amazon S3", "Some sort key value");

BatchWriteItemOutcome outcome = dynamoDB.batchWriteItem(forumTableWriteItems, threadTableWriteItems);

// Code for checking unprocessed items is omitted in this example
```

実例については、「[例: AWS SDK for Java ドキュメント API を使用したバッチ書き込みオペレーション](batch-operation-document-api-java.md#JavaDocumentAPIBatchWrite)」を参照してください。

## バッチ取得: 複数の項目の取得
<a name="JavaDocumentAPIBatchGetItem"></a>

`batchGetItem` メソッドによって、1 つまたは複数のテーブルから複数の項目を取得できます。単一の項目を取り出すために、`getItem` メソッドを使用できます。

以下のステップに従ってください。

1. `DynamoDB` クラスのインスタンスを作成します。

1. テーブルから取り出すプライマリキーのリストを記述する `TableKeysAndAttributes` クラスのインスタンスを作成します。1 つのバッチ取得オペレーションで複数のテーブルから読み込む場合、テーブルごとに `TableKeysAndAttributes` インスタンスを 1 つずつ作成する必要があります。

1. 前の手順で作成した `batchGetItem` オブジェクトを指定して、`TableKeysAndAttributes` メソッドを呼び出します。

以下の Java コード例は、前述のステップの例です。この例では、`Forum` テーブル内の 2 つの項目、および `Thread` テーブル内の 3 つの項目を取り出します。

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

    TableKeysAndAttributes forumTableKeysAndAttributes = new TableKeysAndAttributes(forumTableName);
    forumTableKeysAndAttributes.addHashOnlyPrimaryKeys("Name",
    "Amazon S3",
    "Amazon DynamoDB");

TableKeysAndAttributes threadTableKeysAndAttributes = new TableKeysAndAttributes(threadTableName);
threadTableKeysAndAttributes.addHashAndRangePrimaryKeys("ForumName", "Subject",
    "Amazon DynamoDB","DynamoDB Thread 1",
    "Amazon DynamoDB","DynamoDB Thread 2",
    "Amazon S3","S3 Thread 1");

BatchGetItemOutcome outcome = dynamoDB.batchGetItem(
    forumTableKeysAndAttributes, threadTableKeysAndAttributes);

for (String tableName : outcome.getTableItems().keySet()) {
    System.out.println("Items in table " + tableName);
    List<Item> items = outcome.getTableItems().get(tableName);
    for (Item item : items) {
        System.out.println(item);
    }
}
```

### オプションパラメータの指定
<a name="BatchGetItemJavaDocumentAPIOptions"></a>

必須のパラメータに加え、`batchGetItem` を使用する場合はオプションパラメータも指定できます。たとえば、定義した `ProjectionExpression` ごとに `TableKeysAndAttributes` を指定できます。これにより、テーブルから取り出す属性を指定することができます。

次のサンプルコードでは、`Forum` から 2 つの項目を取得します。`withProjectionExpression` パラメータは、`Threads` 属性のみを取得することを指定します。

**Example**  

```
TableKeysAndAttributes forumTableKeysAndAttributes = new TableKeysAndAttributes("Forum")
    .withProjectionExpression("Threads");

forumTableKeysAndAttributes.addHashOnlyPrimaryKeys("Name",
    "Amazon S3",
    "Amazon DynamoDB");

BatchGetItemOutcome outcome = dynamoDB.batchGetItem(forumTableKeysAndAttributes);
```

## 項目の更新
<a name="JavaDocumentAPIItemUpdate"></a>

`updateItem` オブジェクトの `Table` メソッドは、既存の属性値の更新、新しい属性の追加、または既存の項目からの属性の削除を実行できます。

`updateItem` メソッドは、以下のように動作します。
+ 項目が存在しない (指定されたプライマリキーを持つ項目がテーブル内にない) 場合、`updateItem` はテーブルに新しい項目を追加します
+ 項目が存在する場合、`updateItem` は `UpdateExpression` パラメータで指定されたとおりに更新を実行します。

**注記**  
`putItem` を使用して項目を「更新」することもできます。たとえば、`putItem` を呼び出して項目をテーブルに追加したが、指定されたプライマリキーを持つ項目がすでに存在する場合、`putItem` は項目全体を置き換えます。入力で指定されていない属性が既存の項目内にある場合、`putItem` は項目からそれらの属性を削除します。  
一般に、項目属性を変更するときは必ず `updateItem` を使用することをお勧めします。`updateItem` メソッドは、入力で指定した項目属性のみを変更し、項目内の他の属性は変更されません。

以下のステップに従ってください。

1. 操作対象のテーブルを表すために、`Table` クラスのインスタンスを作成します。

1. `updateTable` インスタンスの `Table` メソッドを呼び出します。変更する属性とその変更方法を記述する `UpdateExpression` と同時に、取り出す項目のプライマリーキーを指定する必要があります。

以下の Java コード例は、前述のタスクの例です。このコードでは、`ProductCatalog` テーブルの書籍項目を更新します。この例では、`Authors` のセットに著者を追加し、既存の `ISBN` 属性を削除します。また、価格を 1 引き下げます。

`ExpressionAttributeValues` マップは `UpdateExpression` で使用されます。プレースホルダー `:val1` および `:val2` は、実行時に、`Authors` と `Price` の実際の値に置き換えられます。

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("ProductCatalog");

Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#A", "Authors");
expressionAttributeNames.put("#P", "Price");
expressionAttributeNames.put("#I", "ISBN");

Map<String, Object> expressionAttributeValues = new HashMap<String, Object>();
expressionAttributeValues.put(":val1",
    new HashSet<String>(Arrays.asList("Author YY","Author ZZ")));
expressionAttributeValues.put(":val2", 1);   //Price

UpdateItemOutcome outcome =  table.updateItem(
    "Id",          // key attribute name
    101,           // key attribute value
    "add #A :val1 set #P = #P - :val2 remove #I", // UpdateExpression
    expressionAttributeNames,
    expressionAttributeValues);
```

### オプションパラメータの指定
<a name="UpdateItemJavaDocumentAPIOptions"></a>

必須のパラメータに加えて、更新を実行するために満たす必要がある条件も含めて、`updateItem` メソッドのオプションパラメータを指定することもできます。指定した条件を満たさない場合は、AWS SDK for Java が `ConditionalCheckFailedException` をスローします。たとえば、以下の Java コード例では、書籍項目の価格を条件付きで 25 に更新します。現在の価格が 20 である場合にのみ価格を更新する必要があることを示す `ConditionExpression` を指定します。

**Example**  

```
Table table = dynamoDB.getTable("ProductCatalog");

Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#P", "Price");

Map<String, Object> expressionAttributeValues = new HashMap<String, Object>();
expressionAttributeValues.put(":val1", 25);  // update Price to 25...
expressionAttributeValues.put(":val2", 20);  //...but only if existing Price is 20

UpdateItemOutcome outcome = table.updateItem(
    new PrimaryKey("Id",101),
    "set #P = :val1", // UpdateExpression
    "#P = :val2",     // ConditionExpression
    expressionAttributeNames,
    expressionAttributeValues);
```

### アトミックカウンタ
<a name="AtomicCounterJavaDocumentAPI"></a>

`updateItem` を使用してアトミックカウンターを実装できます。アトミックカウンターでは、他の書き込みリクエストを妨げることなく、既存の属性の値をインクリメントまたはデクリメントします。アトミックカウンターをインクリメントするには、`UpdateExpression` を使用して、`set` アクションで既存の `Number` 型の属性に数値を加算します。

次のサンプルはこのアトミックカウンターを示しており、`Quantity` 属性を 1 ずつインクリメントさせています。さらに、`ExpressionAttributeNames` での `UpdateExpression` パラメータの使用方法も示します。

```
Table table = dynamoDB.getTable("ProductCatalog");

Map<String,String> expressionAttributeNames = new HashMap<String,String>();
expressionAttributeNames.put("#p", "PageCount");

Map<String,Object> expressionAttributeValues = new HashMap<String,Object>();
expressionAttributeValues.put(":val", 1);

UpdateItemOutcome outcome = table.updateItem(
    "Id", 121,
    "set #p = #p + :val",
    expressionAttributeNames,
    expressionAttributeValues);
```

## 項目の削除
<a name="DeleteMidLevelJava"></a>

`deleteItem` メソッドによって、テーブルから項目を削除します。削除する項目のプライマリキーを入力する必要があります。

以下のステップに従ってください。

1. `DynamoDB` クライアントのインスタンスを作成します。

1. 削除する項目のキーを指定して、`deleteItem` メソッドを呼び出します。

次の Java サンプルは、このタスクを示しています。

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("ProductCatalog");

DeleteItemOutcome outcome = table.deleteItem("Id", 101);
```

### オプションパラメータの指定
<a name="DeleteItemJavaDocumentAPIOptions"></a>

`deleteItem` のオプションパラメータを指定できます。たとえば、次の Java コード例は、`ConditionExpression` 内の書籍項目は書籍が絶版になった (`ProductCatalog` 属性が false) 場合のみ削除可能であることを示す `InPublication` を指定します。

**Example**  

```
Map<String,Object> expressionAttributeValues = new HashMap<String,Object>();
expressionAttributeValues.put(":val", false);

DeleteItemOutcome outcome = table.deleteItem("Id",103,
    "InPublication = :val",
    null, // ExpressionAttributeNames - not used in this example
    expressionAttributeValues);
```

# 例: AWS SDK for Java ドキュメント API を使用した CRUD オペレーション
<a name="JavaDocumentAPICRUDExample"></a>

以下のコード例は、Amazon DynamoDB 項目に対する CRUD オペレーションの例です。この例では、項目の作成、項目の取得、さまざまな更新の実行、さらに項目の削除を行います。

**注記**  
SDK for Java には、オブジェクト永続性モデルも用意されています。このモデルにより、クライアント側のクラスを DynamoDB テーブルにマッピングできます。この方法により、記述する必要のあるコードの量を減らすことができます。詳細については、「[Java 1.x: DynamoDBMapper](DynamoDBMapper.md)」を参照してください。

**注記**  
このコード例では、アカウントの DynamoDB に対し、[DynamoDB でのコード例用のテーブルの作成とデータのロード](SampleData.md) セクションの手順に従ってデータが既にロードされていることを前提としています。  
以下の例を実行するための詳しい手順については、「[Java コードの例](CodeSamples.Java.md)」を参照してください。

```
package com.amazonaws.codesamples.document;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DeleteItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.UpdateItemOutcome;
import com.amazonaws.services.dynamodbv2.document.spec.DeleteItemSpec;
import com.amazonaws.services.dynamodbv2.document.spec.UpdateItemSpec;
import com.amazonaws.services.dynamodbv2.document.utils.NameMap;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.ReturnValue;

public class DocumentAPIItemCRUDExample {

    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    static String tableName = "ProductCatalog";

    public static void main(String[] args) throws IOException {

        createItems();

        retrieveItem();

        // Perform various updates.
        updateMultipleAttributes();
        updateAddNewAttribute();
        updateExistingAttributeConditionally();

        // Delete the item.
        deleteItem();

    }

    private static void createItems() {

        Table table = dynamoDB.getTable(tableName);
        try {

            Item item = new Item().withPrimaryKey("Id", 120).withString("Title", "Book 120 Title")
                    .withString("ISBN", "120-1111111111")
                    .withStringSet("Authors", new HashSet<String>(Arrays.asList("Author12", "Author22")))
                    .withNumber("Price", 20).withString("Dimensions", "8.5x11.0x.75").withNumber("PageCount", 500)
                    .withBoolean("InPublication", false).withString("ProductCategory", "Book");
            table.putItem(item);

            item = new Item().withPrimaryKey("Id", 121).withString("Title", "Book 121 Title")
                    .withString("ISBN", "121-1111111111")
                    .withStringSet("Authors", new HashSet<String>(Arrays.asList("Author21", "Author 22")))
                    .withNumber("Price", 20).withString("Dimensions", "8.5x11.0x.75").withNumber("PageCount", 500)
                    .withBoolean("InPublication", true).withString("ProductCategory", "Book");
            table.putItem(item);

        } catch (Exception e) {
            System.err.println("Create items failed.");
            System.err.println(e.getMessage());

        }
    }

    private static void retrieveItem() {
        Table table = dynamoDB.getTable(tableName);

        try {

            Item item = table.getItem("Id", 120, "Id, ISBN, Title, Authors", null);

            System.out.println("Printing item after retrieving it....");
            System.out.println(item.toJSONPretty());

        } catch (Exception e) {
            System.err.println("GetItem failed.");
            System.err.println(e.getMessage());
        }

    }

    private static void updateAddNewAttribute() {
        Table table = dynamoDB.getTable(tableName);

        try {

            UpdateItemSpec updateItemSpec = new UpdateItemSpec().withPrimaryKey("Id", 121)
                    .withUpdateExpression("set #na = :val1").withNameMap(new NameMap().with("#na", "NewAttribute"))
                    .withValueMap(new ValueMap().withString(":val1", "Some value"))
                    .withReturnValues(ReturnValue.ALL_NEW);

            UpdateItemOutcome outcome = table.updateItem(updateItemSpec);

            // Check the response.
            System.out.println("Printing item after adding new attribute...");
            System.out.println(outcome.getItem().toJSONPretty());

        } catch (Exception e) {
            System.err.println("Failed to add new attribute in " + tableName);
            System.err.println(e.getMessage());
        }
    }

    private static void updateMultipleAttributes() {

        Table table = dynamoDB.getTable(tableName);

        try {

            UpdateItemSpec updateItemSpec = new UpdateItemSpec().withPrimaryKey("Id", 120)
                    .withUpdateExpression("add #a :val1 set #na=:val2")
                    .withNameMap(new NameMap().with("#a", "Authors").with("#na", "NewAttribute"))
                    .withValueMap(
                            new ValueMap().withStringSet(":val1", "Author YY", "Author ZZ").withString(":val2",
                                    "someValue"))
                    .withReturnValues(ReturnValue.ALL_NEW);

            UpdateItemOutcome outcome = table.updateItem(updateItemSpec);

            // Check the response.
            System.out.println("Printing item after multiple attribute update...");
            System.out.println(outcome.getItem().toJSONPretty());

        } catch (Exception e) {
            System.err.println("Failed to update multiple attributes in " + tableName);
            System.err.println(e.getMessage());

        }
    }

    private static void updateExistingAttributeConditionally() {

        Table table = dynamoDB.getTable(tableName);

        try {

            // Specify the desired price (25.00) and also the condition (price =
            // 20.00)

            UpdateItemSpec updateItemSpec = new UpdateItemSpec().withPrimaryKey("Id", 120)
                    .withReturnValues(ReturnValue.ALL_NEW).withUpdateExpression("set #p = :val1")
                    .withConditionExpression("#p = :val2").withNameMap(new NameMap().with("#p", "Price"))
                    .withValueMap(new ValueMap().withNumber(":val1", 25).withNumber(":val2", 20));

            UpdateItemOutcome outcome = table.updateItem(updateItemSpec);

            // Check the response.
            System.out.println("Printing item after conditional update to new attribute...");
            System.out.println(outcome.getItem().toJSONPretty());

        } catch (Exception e) {
            System.err.println("Error updating item in " + tableName);
            System.err.println(e.getMessage());
        }
    }

    private static void deleteItem() {

        Table table = dynamoDB.getTable(tableName);

        try {

            DeleteItemSpec deleteItemSpec = new DeleteItemSpec().withPrimaryKey("Id", 120)
                    .withConditionExpression("#ip = :val").withNameMap(new NameMap().with("#ip", "InPublication"))
                    .withValueMap(new ValueMap().withBoolean(":val", false)).withReturnValues(ReturnValue.ALL_OLD);

            DeleteItemOutcome outcome = table.deleteItem(deleteItemSpec);

            // Check the response.
            System.out.println("Printing item that was deleted...");
            System.out.println(outcome.getItem().toJSONPretty());

        } catch (Exception e) {
            System.err.println("Error deleting item in " + tableName);
            System.err.println(e.getMessage());
        }
    }
}
```

# 例: AWS SDK for Java ドキュメント API を使用したバッチオペレーション
<a name="batch-operation-document-api-java"></a>

このセクションでは、AWS SDK for Java ドキュメント API を使用した Amazon DynamoDB でのバッチ書き込みおよびバッチ取得オペレーションの例を示します。

**注記**  
SDK for Java には、オブジェクト永続性モデルも用意されています。このモデルにより、クライアント側のクラスを DynamoDB テーブルにマッピングできます。この方法により、記述する必要のあるコードの量を減らすことができます。詳細については、「」を参照してください[Java 1.x: DynamoDBMapper](DynamoDBMapper.md)

**Topics**
+ [例: AWS SDK for Java ドキュメント API を使用したバッチ書き込みオペレーション](#JavaDocumentAPIBatchWrite)
+ [例: AWS SDK for Java ドキュメント API を使用したバッチ取得オペレーション](#JavaDocumentAPIBatchGet)

## 例: AWS SDK for Java ドキュメント API を使用したバッチ書き込みオペレーション
<a name="JavaDocumentAPIBatchWrite"></a>

以下の Java コード例では、`batchWriteItem` メソッドを使用して、以下の置換および削除のオペレーションを実行します。
+ `Forum` テーブル内で 1 つの項目を配置します。
+ `Thread` テーブルに対して 1 つの項目を配置および削除します。

バッチの書き込みリクエストを作成すると、1 つまたは複数のテーブルに対して多数の置換リクエストと削除リクエストを指定できます。ただし、`batchWriteItem` では、1 回のバッチ書き込みオペレーションで可能なバッチ書き込みリクエストのサイズ、置換および削除のオペレーションの数を制限しています。これらの制限を超えるリクエストは却下されます。プロビジョニングされたスループットがテーブルに不足しているためにこのリクエストを処理できない場合は、応答時に未処理のリクエスト項目が返されます。

以下の例では、未処理のリクエスト項目がないか、応答を確認します。未処理のリクエスト項目がある場合は、`batchWriteItem` リクエストをループバックして再送信します。このガイドの例に従っていれば、`Forum` テーブルおよび `Thread` テーブルは作成済みです。プログラムで、これらのテーブルを作成し、サンプルデータをアップロードすることもできます。詳細については、「」を参照してください[AWS SDK for Java を使用してサンプルテーブルを作成してデータをアップロードする](AppendixSampleDataCodeJava.md)

以下の例をテストするための詳細な手順については、「[Java コードの例](CodeSamples.Java.md)」を参照してください。

**Example**  

```
package com.amazonaws.codesamples.document;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.BatchWriteItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.TableWriteItems;
import com.amazonaws.services.dynamodbv2.model.WriteRequest;

public class DocumentAPIBatchWrite {

    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    static String forumTableName = "Forum";
    static String threadTableName = "Thread";

    public static void main(String[] args) throws IOException {

        writeMultipleItemsBatchWrite();

    }

    private static void writeMultipleItemsBatchWrite() {
        try {

            // Add a new item to Forum
            TableWriteItems forumTableWriteItems = new TableWriteItems(forumTableName) // Forum
                    .withItemsToPut(new Item().withPrimaryKey("Name", "Amazon RDS").withNumber("Threads", 0));

            // Add a new item, and delete an existing item, from Thread
            // This table has a partition key and range key, so need to specify
            // both of them
            TableWriteItems threadTableWriteItems = new TableWriteItems(threadTableName)
                    .withItemsToPut(
                            new Item().withPrimaryKey("ForumName", "Amazon RDS", "Subject", "Amazon RDS Thread 1")
                                    .withString("Message", "ElastiCache Thread 1 message")
                                    .withStringSet("Tags", new HashSet<String>(Arrays.asList("cache", "in-memory"))))
                    .withHashAndRangeKeysToDelete("ForumName", "Subject", "Amazon S3", "S3 Thread 100");

            System.out.println("Making the request.");
            BatchWriteItemOutcome outcome = dynamoDB.batchWriteItem(forumTableWriteItems, threadTableWriteItems);

            do {

                // Check for unprocessed keys which could happen if you exceed
                // provisioned throughput

                Map<String, List<WriteRequest>> unprocessedItems = outcome.getUnprocessedItems();

                if (outcome.getUnprocessedItems().size() == 0) {
                    System.out.println("No unprocessed items found");
                } else {
                    System.out.println("Retrieving the unprocessed items");
                    outcome = dynamoDB.batchWriteItemUnprocessed(unprocessedItems);
                }

            } while (outcome.getUnprocessedItems().size() > 0);

        } catch (Exception e) {
            System.err.println("Failed to retrieve items: ");
            e.printStackTrace(System.err);
        }

    }

}
```

## 例: AWS SDK for Java ドキュメント API を使用したバッチ取得オペレーション
<a name="JavaDocumentAPIBatchGet"></a>

以下の Java コード例では、`batchGetItem` メソッドを使用して、`Forum` テーブルおよび `Thread` テーブルから複数の項目を取り出します。`BatchGetItemRequest` は、取得する各項目のテーブル名とキーのリストを指定します。この例では、取得した項目を印刷して応答を処理します。

**注記**  
このコード例では、アカウントの DynamoDB に対し、[DynamoDB でのコード例用のテーブルの作成とデータのロード](SampleData.md) セクションの手順に従ってデータが既にロードされていることを前提としています。  
以下の例を実行するための詳しい手順については、「[Java コードの例](CodeSamples.Java.md)」を参照してください。

**Example**  

```
package com.amazonaws.codesamples.document;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.BatchGetItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.TableKeysAndAttributes;
import com.amazonaws.services.dynamodbv2.model.KeysAndAttributes;

public class DocumentAPIBatchGet {
    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    static String forumTableName = "Forum";
    static String threadTableName = "Thread";

    public static void main(String[] args) throws IOException {
        retrieveMultipleItemsBatchGet();
    }

    private static void retrieveMultipleItemsBatchGet() {

        try {

            TableKeysAndAttributes forumTableKeysAndAttributes = new TableKeysAndAttributes(forumTableName);
            // Add a partition key
            forumTableKeysAndAttributes.addHashOnlyPrimaryKeys("Name", "Amazon S3", "Amazon DynamoDB");

            TableKeysAndAttributes threadTableKeysAndAttributes = new TableKeysAndAttributes(threadTableName);
            // Add a partition key and a sort key
            threadTableKeysAndAttributes.addHashAndRangePrimaryKeys("ForumName", "Subject", "Amazon DynamoDB",
                    "DynamoDB Thread 1", "Amazon DynamoDB", "DynamoDB Thread 2", "Amazon S3", "S3 Thread 1");

            System.out.println("Making the request.");

            BatchGetItemOutcome outcome = dynamoDB.batchGetItem(forumTableKeysAndAttributes,
                    threadTableKeysAndAttributes);

            Map<String, KeysAndAttributes> unprocessed = null;

            do {
                for (String tableName : outcome.getTableItems().keySet()) {
                    System.out.println("Items in table " + tableName);
                    List<Item> items = outcome.getTableItems().get(tableName);
                    for (Item item : items) {
                        System.out.println(item.toJSONPretty());
                    }
                }

                // Check for unprocessed keys which could happen if you exceed
                // provisioned
                // throughput or reach the limit on response size.
                unprocessed = outcome.getUnprocessedKeys();

                if (unprocessed.isEmpty()) {
                    System.out.println("No unprocessed keys found");
                } else {
                    System.out.println("Retrieving the unprocessed keys");
                    outcome = dynamoDB.batchGetItemUnprocessed(unprocessed);
                }

            } while (!unprocessed.isEmpty());

        } catch (Exception e) {
            System.err.println("Failed to retrieve items.");
            System.err.println(e.getMessage());
        }

    }

}
```

# 例: AWS SDK for Java ドキュメント API を使用したバイナリタイプ属性の処理
<a name="JavaDocumentAPIBinaryTypeExample"></a>

以下の Java コード例は、バイナリタイプ属性の処理の例です。この例では、`Reply` テーブルに項目を追加します。この項目には、圧縮データを格納するバイナリタイプ属性（`ExtendedMessage`）などがあります。また、この例では、項目を取得し、すべての属性値を印刷します。説明のため、この例では `GZIPOutputStream` クラスを使用して、サンプルストリームを圧縮し、圧縮したデータを `ExtendedMessage` 属性に割り当てます。バイナリ属性が取得されると、`GZIPInputStream` クラスを使用して展開されます。

**注記**  
SDK for Java には、オブジェクト永続性モデルも用意されています。このモデルにより、クライアント側のクラスを DynamoDB テーブルにマッピングできます。この方法により、記述する必要のあるコードの量を減らすことができます。詳細については、「」を参照してください[Java 1.x: DynamoDBMapper](DynamoDBMapper.md)

[DynamoDB でのコード例用のテーブルの作成とデータのロード](SampleData.md) セクションの手順に従っていれば、`Reply` テーブルは作成済みです。このテーブルは、プログラムで作成することもできます。詳細については、「」を参照してください[AWS SDK for Java を使用してサンプルテーブルを作成してデータをアップロードする](AppendixSampleDataCodeJava.md)

以下の例をテストするための詳細な手順については、「[Java コードの例](CodeSamples.Java.md)」を参照してください。

**Example**  

```
package com.amazonaws.codesamples.document;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.spec.GetItemSpec;

public class DocumentAPIItemBinaryExample {

    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    static String tableName = "Reply";
    static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

    public static void main(String[] args) throws IOException {
        try {

            // Format the primary key values
            String threadId = "Amazon DynamoDB#DynamoDB Thread 2";

            dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
            String replyDateTime = dateFormatter.format(new Date());

            // Add a new reply with a binary attribute type
            createItem(threadId, replyDateTime);

            // Retrieve the reply with a binary attribute type
            retrieveItem(threadId, replyDateTime);

            // clean up by deleting the item
            deleteItem(threadId, replyDateTime);
        } catch (Exception e) {
            System.err.println("Error running the binary attribute type example: " + e);
            e.printStackTrace(System.err);
        }
    }

    public static void createItem(String threadId, String replyDateTime) throws IOException {

        Table table = dynamoDB.getTable(tableName);

        // Craft a long message
        String messageInput = "Long message to be compressed in a lengthy forum reply";

        // Compress the long message
        ByteBuffer compressedMessage = compressString(messageInput.toString());

        table.putItem(new Item().withPrimaryKey("Id", threadId).withString("ReplyDateTime", replyDateTime)
                .withString("Message", "Long message follows").withBinary("ExtendedMessage", compressedMessage)
                .withString("PostedBy", "User A"));
    }

    public static void retrieveItem(String threadId, String replyDateTime) throws IOException {

        Table table = dynamoDB.getTable(tableName);

        GetItemSpec spec = new GetItemSpec().withPrimaryKey("Id", threadId, "ReplyDateTime", replyDateTime)
                .withConsistentRead(true);

        Item item = table.getItem(spec);

        // Uncompress the reply message and print
        String uncompressed = uncompressString(ByteBuffer.wrap(item.getBinary("ExtendedMessage")));

        System.out.println("Reply message:\n" + " Id: " + item.getString("Id") + "\n" + " ReplyDateTime: "
                + item.getString("ReplyDateTime") + "\n" + " PostedBy: " + item.getString("PostedBy") + "\n"
                + " Message: "
                + item.getString("Message") + "\n" + " ExtendedMessage (uncompressed): " + uncompressed + "\n");
    }

    public static void deleteItem(String threadId, String replyDateTime) {

        Table table = dynamoDB.getTable(tableName);
        table.deleteItem("Id", threadId, "ReplyDateTime", replyDateTime);
    }

    private static ByteBuffer compressString(String input) throws IOException {
        // Compress the UTF-8 encoded String into a byte[]
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        GZIPOutputStream os = new GZIPOutputStream(baos);
        os.write(input.getBytes("UTF-8"));
        os.close();
        baos.close();
        byte[] compressedBytes = baos.toByteArray();

        // The following code writes the compressed bytes to a ByteBuffer.
        // A simpler way to do this is by simply calling
        // ByteBuffer.wrap(compressedBytes);
        // However, the longer form below shows the importance of resetting the
        // position of the buffer
        // back to the beginning of the buffer if you are writing bytes directly
        // to it, since the SDK
        // will consider only the bytes after the current position when sending
        // data to DynamoDB.
        // Using the "wrap" method automatically resets the position to zero.
        ByteBuffer buffer = ByteBuffer.allocate(compressedBytes.length);
        buffer.put(compressedBytes, 0, compressedBytes.length);
        buffer.position(0); // Important: reset the position of the ByteBuffer
                            // to the beginning
        return buffer;
    }

    private static String uncompressString(ByteBuffer input) throws IOException {
        byte[] bytes = input.array();
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        GZIPInputStream is = new GZIPInputStream(bais);

        int chunkSize = 1024;
        byte[] buffer = new byte[chunkSize];
        int length = 0;
        while ((length = is.read(buffer, 0, chunkSize)) != -1) {
            baos.write(buffer, 0, length);
        }

        String result = new String(baos.toByteArray(), "UTF-8");

        is.close();
        baos.close();
        bais.close();

        return result;
    }
}
```

# 項目の操作: .NET
<a name="LowLevelDotNetItemCRUD"></a>

AWS SDK for .NET 低レベル API を使用して、テーブル内の項目に対して、一般的な作成、読み込み、更新、削除 (CRUD) のオペレーションを実行できます。以下に、.NET の低レベル API を使用してデータ CRUD オペレーションを実行するための一般的なステップを示します。

1. `AmazonDynamoDBClient` クラスのインスタンス（クライアント）を作成します。

1. 対応するリクエストオブジェクトで、オペレーション固有の必須パラメータを指定します。

   たとえば、項目をアップロードするときは `PutItemRequest` リクエストオブジェクトを使用し、既存の項目を取得するときは `GetItemRequest` リクエストオブジェクトを使用します。

   リクエストオブジェクトを使用して、必須パラメータとオプションパラメータの両方を指定できます。

1. 前述のステップで作成したリクエストオブジェクトを渡して、クライアントから提供された適切なメソッドを実行します。

   `AmazonDynamoDBClient` クライアントは、CRUD オペレーションに、`PutItem`、`GetItem`、`UpdateItem` および `DeleteItem` メソッドを提供します。

**Topics**
+ [項目の置換](#PutItemLowLevelAPIDotNet)
+ [項目の取得](#GetItemLowLevelDotNET)
+ [項目の更新](#UpdateItemLowLevelDotNet)
+ [アトミックカウンタ](#AtomicCounterLowLevelDotNet)
+ [項目の削除](#DeleteMidLevelDotNet)
+ [バッチ書き込み: 複数の項目の置換および削除](#BatchWriteLowLevelDotNet)
+ [バッチ取得: 複数の項目の取得](#BatchGetLowLevelDotNet)
+ [例: AWS SDK for .NET 低レベル API を使用した CRUD オペレーション](LowLevelDotNetItemsExample.md)
+ [例: AWS SDK for .NET 低レベル API を使用したバッチオペレーション](batch-operation-lowlevel-dotnet.md)
+ [例: AWS SDK for .NET 低レベル API を使用したバイナリタイプ属性の処理](LowLevelDotNetBinaryTypeExample.md)

## 項目の置換
<a name="PutItemLowLevelAPIDotNet"></a>

`PutItem` メソッドは、項目をテーブルにアップロードします。項目が存在する場合、その項目全体が置き換えられます。

**注記**  
項目全体を置き換える代わりに固有の属性のみを更新する場合は、`UpdateItem` メソッドを使用できます。詳細については、「」を参照してください[項目の更新](#UpdateItemLowLevelDotNet)

以下に、低レベル .NET SDK API を使用して項目をアップロードするステップを示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. `PutItemRequest` クラスのインスタンスを作成して、必要なパラメータを指定します。

   項目を配置するには、テーブル名と項目を指定する必要があります。

1. 前のステップで作成した `PutItemRequest` オブジェクトを指定して、`PutItem` メソッドを実行します。

以下の C\$1 サンプルは、前述のステップの例です。この例では、項目を `ProductCatalog` テーブルにアップロードします。

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new PutItemRequest
{
   TableName = tableName,
   Item = new Dictionary<string, AttributeValue>()
      {
          { "Id", new AttributeValue { N = "201" }},
          { "Title", new AttributeValue { S = "Book 201 Title" }},
          { "ISBN", new AttributeValue { S = "11-11-11-11" }},
          { "Price", new AttributeValue { S = "20.00" }},
          {
            "Authors",
            new AttributeValue
            { SS = new List<string>{"Author1", "Author2"}   }
          }
      }
};
client.PutItem(request);
```

前の例では、`Id`、`Title`、`ISBN` および `Authors` 属性を持つブック項目をアップロードします。`Id` は数値型の属性であり、他のすべての属性は文字列型であることに注意してください。作成者は `String` セットです。

### オプションパラメータの指定
<a name="PutItemLowLevelAPIDotNetOptions"></a>

次の C\$1 例に示すように、`PutItemRequest` オブジェクトを使用してオプションのパラメータを指定することもできます。この例では、次のオプションパラメータが指定されています。
+ `ExpressionAttributeNames`、`ExpressionAttributeValues` および `ConditionExpression` は、既存の項目に特定の値を持つ ISBN 属性がある場合にのみ、項目を置き換えるように指定します。
+ レスポンスで古い項目をリクエストする `ReturnValues` パラメータ。

**Example**  

```
var request = new PutItemRequest
 {
   TableName = tableName,
   Item = new Dictionary<string, AttributeValue>()
               {
                   { "Id", new AttributeValue { N = "104" }},
                   { "Title", new AttributeValue { S = "Book 104  Title" }},
                   { "ISBN", new AttributeValue { S = "444-4444444444" }},
                   { "Authors",
                     new AttributeValue { SS = new List<string>{"Author3"}}}
               },
    // Optional parameters.
    ExpressionAttributeNames = new Dictionary<string,string>()
    {
        {"#I", "ISBN"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":isbn",new AttributeValue {S = "444-4444444444"}}
    },
    ConditionExpression = "#I = :isbn"

};
var response = client.PutItem(request);
```

詳細については、「[PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html)」を参照してください。

## 項目の取得
<a name="GetItemLowLevelDotNET"></a>

`GetItem` メソッドは項目を取得します。

**注記**  
複数の項目を取り出すために、`BatchGetItem` メソッドを使用できます。詳細については、「」を参照してください[バッチ取得: 複数の項目の取得](#BatchGetLowLevelDotNet)

以下に、低レベル AWS SDK for .NET API を使用して既存の項目を取得するステップを示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. `GetItemRequest` クラスのインスタンスを作成して、必要なパラメータを指定します。

   項目を吸いtpィするには、テーブル名と項目のプライマリキーを指定する必要があります。

1. 前のステップで作成した `GetItemRequest` オブジェクトを指定して、`GetItem` メソッドを実行します。

以下の C\$1 サンプルは、前述のステップの例です。例では、`ProductCatalog` テーブルから項目を取得します。

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new GetItemRequest
 {
   TableName = tableName,
   Key = new Dictionary<string,AttributeValue>() { { "Id", new AttributeValue { N = "202" } } },
 };
 var response = client.GetItem(request);

// Check the response.
var result = response.GetItemResult;
var attributeMap = result.Item; // Attribute list in the response.
```

### オプションパラメータの指定
<a name="GetItemLowLevelDotNETOptions"></a>

次の C\$1 例に示すように、`GetItemRequest` オブジェクトを使用してオプションのパラメータを指定することもできます。このサンプルでは、次のオプションパラメータが指定されています。
+ 取得する属性を指定する `ProjectionExpression` パラメータ。
+ 強力な整合性のある読み込みを実行する `ConsistentRead` パラメータ。読み込み整合性の詳細については、「[DynamoDB の読み取り整合性](HowItWorks.ReadConsistency.md)」を参照してください。

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new GetItemRequest
 {
   TableName = tableName,
   Key = new Dictionary<string,AttributeValue>() { { "Id", new AttributeValue { N = "202" } } },
   // Optional parameters.
   ProjectionExpression = "Id, ISBN, Title, Authors",
   ConsistentRead = true
 };

 var response = client.GetItem(request);

// Check the response.
var result = response.GetItemResult;
var attributeMap = result.Item;
```

詳細については、「[GetItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html)」を参照してください。

## 項目の更新
<a name="UpdateItemLowLevelDotNet"></a>

`UpdateItem` メソッドは、既存の項目があればそれを更新します。`UpdateItem` オペレーションを使用して、既存の属性値を更新するか、新しい属性を追加するか、既存のコレクションから属性を削除することができます。プライマリキーが指定されている項目がない場合は、新しい項目が追加されます。

`UpdateItem` オペレーションは、以下のガイドラインに従います。
+ 項目が存在しない場合、`UpdateItem` は入力で指定されたプライマリキーを使用して、新しい項目を追加します。
+ 項目が存在する場合、`UpdateItem` は次のように更新を適用します。
  + 既存の属性値を更新による値に置き換えます。
  + 入力で指定された属性が存在しない場合は、新しい属性を項目に追加します。
  + 入力された属性が null である場合は、属性を削除します。
  + `ADD` に `Action` を使用する場合は、既存のセット (文字列または数値セット) に値を追加するか、既存の数値属性値から数学的に加算 (正の数を使用) または減算 (負の数を使用) することができます。

**注記**  
`PutItem` オペレーションにより、更新を実行できます。詳細については、「」を参照してください[項目の置換](#PutItemLowLevelAPIDotNet) たとえば、`PutItem` をコールして項目をアップロードするときにプライマリキーが存在する場合は、`PutItem` オペレーションによって項目全体が置き換わります。既存の項目内に属性があり、入力でそれらの属性が指定されていない場合、それらの属性は、`PutItem` オペレーションによって削除されます。ただし、`UpdateItem` が更新するのは指定された入力属性だけです。その項目では、その他の既存の属性は変更されません。

以下に、低レベル .NET SDK API を使用して既存の項目を更新するステップを示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. `UpdateItemRequest` クラスのインスタンスを作成して、必要なパラメータを指定します。

   これは、属性の追加、既存の属性の更新、属性の削除など、すべての更新を記述するリクエストオブジェクトです。既存の属性を削除するには、その属性名に null 値を指定します。

1. 前のステップで作成した `UpdateItemRequest` オブジェクトを指定して、`UpdateItem` メソッドを実行します。

以下の C\$1 サンプルコードは、前述のステップの例です。例では、`ProductCatalog` テーブルのブック項目を更新します。この例では、`Authors` のコレクションに著者を追加し、既存の `ISBN` 属性を削除します。また、価格を 1 引き下げます。



```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new UpdateItemRequest
{
    TableName = tableName,
    Key = new Dictionary<string,AttributeValue>() { { "Id", new AttributeValue { N = "202" } } },
    ExpressionAttributeNames = new Dictionary<string,string>()
    {
        {"#A", "Authors"},
        {"#P", "Price"},
        {"#NA", "NewAttribute"},
        {"#I", "ISBN"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":auth",new AttributeValue { SS = {"Author YY","Author ZZ"}}},
        {":p",new AttributeValue {N = "1"}},
        {":newattr",new AttributeValue {S = "someValue"}},
    },

    // This expression does the following:
    // 1) Adds two new authors to the list
    // 2) Reduces the price
    // 3) Adds a new attribute to the item
    // 4) Removes the ISBN attribute from the item
    UpdateExpression = "ADD #A :auth SET #P = #P - :p, #NA = :newattr REMOVE #I"
};
var response = client.UpdateItem(request);
```

### オプションパラメータの指定
<a name="UpdateItemLowLevelDotNETOptions"></a>

次の C\$1 例に示すように、`UpdateItemRequest` オブジェクトを使用してオプションのパラメータを指定することもできます。次のオプションパラメータが指定されています。
+ `ExpressionAttributeValues` および `ConditionExpression` は、既存の料金が 20.00 である場合にのみ料金を更新できるように指定します。
+ レスポンスで更新された項目をリクエストする `ReturnValues` パラメータ。

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new UpdateItemRequest
{
    Key = new Dictionary<string,AttributeValue>() { { "Id", new AttributeValue { N = "202" } } },

    // Update price only if the current price is 20.00.
    ExpressionAttributeNames = new Dictionary<string,string>()
    {
        {"#P", "Price"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":newprice",new AttributeValue {N = "22"}},
        {":currprice",new AttributeValue {N = "20"}}
    },
    UpdateExpression = "SET #P = :newprice",
    ConditionExpression = "#P = :currprice",
    TableName = tableName,
    ReturnValues = "ALL_NEW" // Return all the attributes of the updated item.
};

var response = client.UpdateItem(request);
```

詳細については、「[UpdateItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html)」を参照してください。

## アトミックカウンタ
<a name="AtomicCounterLowLevelDotNet"></a>

`updateItem` を使用してアトミックカウンターを実装できます。アトミックカウンターでは、他の書き込みリクエストを妨げることなく、既存の属性の値をインクリメントまたはデクリメントします。アトミックカウンタを更新するには、`UpdateExpression` パラメータにタイプに `Number` の属性を持つ `updateItem` および `ADD` を、`Action` として使用します。

次のサンプルはこのアトミックカウンターを示しており、`Quantity` 属性を 1 ずつインクリメントさせています。

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new UpdateItemRequest
{
    Key = new Dictionary<string, AttributeValue>() { { "Id", new AttributeValue { N = "121" } } },
    ExpressionAttributeNames = new Dictionary<string, string>()
    {
        {"#Q", "Quantity"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":incr",new AttributeValue {N = "1"}}
    },
    UpdateExpression = "SET #Q = #Q + :incr",
    TableName = tableName
};

var response = client.UpdateItem(request);
```

## 項目の削除
<a name="DeleteMidLevelDotNet"></a>

`DeleteItem` メソッドによって、テーブルから項目を削除します。

以下では、低レベル .NET SDK API を使用して項目を削除するステップを示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. `DeleteItemRequest` クラスのインスタンスを作成して、必要なパラメータを指定します。

    項目を削除するには、テーブル名と項目のプライマリキーが必要です。

1. 前のステップで作成した `DeleteItemRequest` オブジェクトを指定して、`DeleteItem` メソッドを実行します。

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "ProductCatalog";

var request = new DeleteItemRequest
{
    TableName = tableName,
    Key = new Dictionary<string,AttributeValue>() { { "Id", new AttributeValue { N = "201" } } },
};

var response = client.DeleteItem(request);
```

### オプションパラメータの指定
<a name="DeleteItemLowLevelDotNETOptions"></a>

次の C\$1 コード例に示すように、`DeleteItemRequest` オブジェクトを使用して任意のパラメータを指定することもできます。次のオプションパラメータが指定されています。
+ `ExpressionAttributeValues` および `ConditionExpression` は、ブック項目が公開されていない場合にのみ削除できるよう指定します (InPublication 属性値は false です)。
+ レスポンスで削除された項目をリクエストする `ReturnValues` パラメータ。

**Example**  

```
var request = new DeleteItemRequest
{
    TableName = tableName,
    Key = new Dictionary<string,AttributeValue>() { { "Id", new AttributeValue { N = "201" } } },

    // Optional parameters.
    ReturnValues = "ALL_OLD",
    ExpressionAttributeNames = new Dictionary<string, string>()
    {
        {"#IP", "InPublication"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":inpub",new AttributeValue {BOOL = false}}
    },
    ConditionExpression = "#IP = :inpub"
};

var response = client.DeleteItem(request);
```

詳細については、「[DeleteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DeleteItem.html)」を参照してください。

## バッチ書き込み: 複数の項目の置換および削除
<a name="BatchWriteLowLevelDotNet"></a>

*バッチ書き込み*は、複数の項目の書き込みと削除をバッチで行うことを意味します。`BatchWriteItem` メソッドによって、単一の コール内にある 1 つまたは複数のテーブルから複数の項目を置換および削除できます。以下に、低レベル .NET SDK API を使用して複数の項目を取得するステップを示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. `BatchWriteItemRequest` クラスのインスタンスを作成して、すべての入力および削除オペレーションを記述します。

1. 前のステップで作成した `BatchWriteItemRequest` オブジェクトを指定して、`BatchWriteItem` メソッドを実行します。

1. 応答を処理します。返された未処理のリクエスト項目が応答内に存在していたかどうかをチェックする必要があります。これは、プロビジョニングされたスループットクォータまたは他の何らかの一時的エラーに達する場合に、発生する可能性があります。また、DynamoDB によって、リクエストのサイズ、およびリクエスト内で指定できるオペレーションの数が制限されます。これらの制限を超えると、DynamoDB によってリクエストが却下されます。詳細については、[BatchWriteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html) を参照してください。

以下の C\$1 サンプルコードは、前述のステップの例です。この例では、`BatchWriteItemRequest` を作成して次の書き込みオペレーションを実行します。
+ `Forum` テーブル内で項目を配置します。
+ `Thread` テーブルから項目を入力および削除します。

このコードは `BatchWriteItem` を実行してバッチ操作を実行します。

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();

string table1Name = "Forum";
string table2Name = "Thread";

var request = new BatchWriteItemRequest
 {
   RequestItems = new Dictionary<string, List<WriteRequest>>
    {
      {
        table1Name, new List<WriteRequest>
        {
          new WriteRequest
          {
             PutRequest = new PutRequest
             {
                Item = new Dictionary<string,AttributeValue>
                {
                  { "Name", new AttributeValue { S = "Amazon S3 forum" } },
                  { "Threads", new AttributeValue { N = "0" }}
                }
             }
          }
        }
      } ,
      {
        table2Name, new List<WriteRequest>
        {
          new WriteRequest
          {
            PutRequest = new PutRequest
            {
               Item = new Dictionary<string,AttributeValue>
               {
                 { "ForumName", new AttributeValue { S = "Amazon S3 forum" } },
                 { "Subject", new AttributeValue { S = "My sample question" } },
                 { "Message", new AttributeValue { S = "Message Text." } },
                 { "KeywordTags", new AttributeValue { SS = new List<string> { "Amazon S3", "Bucket" }  } }
               }
            }
          },
          new WriteRequest
          {
             DeleteRequest = new DeleteRequest
             {
                Key = new Dictionary<string,AttributeValue>()
                {
                   { "ForumName", new AttributeValue { S = "Some forum name" } },
                   { "Subject", new AttributeValue { S = "Some subject" } }
                }
             }
          }
        }
      }
    }
 };
response = client.BatchWriteItem(request);
```

実例については、「[例: AWS SDK for .NET 低レベル API を使用したバッチオペレーション](batch-operation-lowlevel-dotnet.md)」を参照してください。

## バッチ取得: 複数の項目の取得
<a name="BatchGetLowLevelDotNet"></a>

`BatchGetItem` メソッドによって、1 つまたは複数のテーブルから複数の項目を取得できます。

**注記**  
単一の項目を取り出すために、`GetItem` メソッドを使用できます。

以下に、低レベル AWS SDK for .NET API を使用して複数の項目を取得するステップを示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. `BatchGetItemRequest` クラスのインスタンスを作成して、必要なパラメータを指定します。

   複数の項目を取得するには、テーブル名とプライマリキーバリューのリストが必要です。

1. 前のステップで作成した `BatchGetItemRequest` オブジェクトを指定して、`BatchGetItem` メソッドを実行します。

1. 応答を処理します。未処理キーがなかったかどうかを確認する必要があります。これは、プロビジョニングされたスループットクォータまたは他の何らかの一時的エラーに達する場合に発生する可能性があります。

以下の C\$1 サンプルコードは、前述のステップの例です。この例では、`Forum` および `Thread` の 2 つのテーブルから取得します。リクエストでは、`Forum` テーブルに 2 つの項目、`Thread` テーブルに 3 つの項目を指定します。応答には、両方のテーブルの項目が含まれます。このコードは、応答を処理する方法を示しています。



```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();

string table1Name = "Forum";
string table2Name = "Thread";

var request = new BatchGetItemRequest
{
  RequestItems = new Dictionary<string, KeysAndAttributes>()
  {
    { table1Name,
      new KeysAndAttributes
      {
        Keys = new List<Dictionary<string, AttributeValue>>()
        {
          new Dictionary<string, AttributeValue>()
          {
            { "Name", new AttributeValue { S = "DynamoDB" } }
          },
          new Dictionary<string, AttributeValue>()
          {
            { "Name", new AttributeValue { S = "Amazon S3" } }
          }
        }
      }
    },
    {
      table2Name,
      new KeysAndAttributes
      {
        Keys = new List<Dictionary<string, AttributeValue>>()
        {
          new Dictionary<string, AttributeValue>()
          {
            { "ForumName", new AttributeValue { S = "DynamoDB" } },
            { "Subject", new AttributeValue { S = "DynamoDB Thread 1" } }
          },
          new Dictionary<string, AttributeValue>()
          {
            { "ForumName", new AttributeValue { S = "DynamoDB" } },
            { "Subject", new AttributeValue { S = "DynamoDB Thread 2" } }
          },
          new Dictionary<string, AttributeValue>()
          {
            { "ForumName", new AttributeValue { S = "Amazon S3" } },
            { "Subject", new AttributeValue { S = "Amazon S3 Thread 1" } }
          }
        }
      }
    }
  }
};

var response = client.BatchGetItem(request);

// Check the response.
var result = response.BatchGetItemResult;
var responses = result.Responses; // The attribute list in the response.

var table1Results = responses[table1Name];
Console.WriteLine("Items in table {0}" + table1Name);
foreach (var item1 in table1Results.Items)
{
  PrintItem(item1);
}

var table2Results = responses[table2Name];
Console.WriteLine("Items in table {1}" + table2Name);
foreach (var item2 in table2Results.Items)
{
  PrintItem(item2);
}
// Any unprocessed keys? could happen if you exceed ProvisionedThroughput or some other error.
Dictionary<string, KeysAndAttributes> unprocessedKeys = result.UnprocessedKeys;
foreach (KeyValuePair<string, KeysAndAttributes> pair in unprocessedKeys)
{
    Console.WriteLine(pair.Key, pair.Value);
}
```



### オプションパラメータの指定
<a name="BatchGetItemLowLevelDotNETOptions"></a>

次の C\$1 コード例に示すように、`BatchGetItemRequest` オブジェクトを使用して任意のパラメータを指定することもできます。例では、`Forum` テーブルから 2 つの項目を取得します。次のオプションパラメータが指定されています。
+  取得する属性を指定する `ProjectionExpression` パラメータ。

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();

string table1Name = "Forum";

var request = new BatchGetItemRequest
{
  RequestItems = new Dictionary<string, KeysAndAttributes>()
  {
    { table1Name,
      new KeysAndAttributes
      {
        Keys = new List<Dictionary<string, AttributeValue>>()
        {
          new Dictionary<string, AttributeValue>()
          {
            { "Name", new AttributeValue { S = "DynamoDB" } }
          },
          new Dictionary<string, AttributeValue>()
          {
            { "Name", new AttributeValue { S = "Amazon S3" } }
          }
        }
      },
      // Optional - name of an attribute to retrieve.
      ProjectionExpression = "Title"
    }
  }
};

var response = client.BatchGetItem(request);
```

詳細については、[BatchGetItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html) を参照してください。

# 例: AWS SDK for .NET 低レベル API を使用した CRUD オペレーション
<a name="LowLevelDotNetItemsExample"></a>

以下の C\$1 コード例は、Amazon DynamoDB 項目に対する CRUD オペレーションの例です。この例では、`ProductCatalog` テーブルへの項目の追加、項目の取得、さまざまな更新の実行、さらに項目の削除を行います。このテーブルを作成していない場合は、プログラムで作成することもできます。詳細については、「[AWS SDK for .NET を使用してサンプルテーブルを作成してデータをアップロードする](AppendixSampleDataCodeDotNET.md)」を参照してください。

以下の例をテストするための詳細な手順については、「[.NET コード例](CodeSamples.DotNet.md)」を参照してください。

**Example**  

```
using System;
using System.Collections.Generic;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;
using Amazon.SecurityToken;

namespace com.amazonaws.codesamples
{
    class LowLevelItemCRUDExample
    {
        private static string tableName = "ProductCatalog";
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();

        static void Main(string[] args)
        {
            try
            {
                CreateItem();
                RetrieveItem();

                // Perform various updates.
                UpdateMultipleAttributes();
                UpdateExistingAttributeConditionally();

                // Delete item.
                DeleteItem();
                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
        }

        private static void CreateItem()
        {
            var request = new PutItemRequest
            {
                TableName = tableName,
                Item = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      N = "1000"
                  }},
                { "Title", new AttributeValue {
                      S = "Book 201 Title"
                  }},
                { "ISBN", new AttributeValue {
                      S = "11-11-11-11"
                  }},
                { "Authors", new AttributeValue {
                      SS = new List<string>{"Author1", "Author2" }
                  }},
                { "Price", new AttributeValue {
                      N = "20.00"
                  }},
                { "Dimensions", new AttributeValue {
                      S = "8.5x11.0x.75"
                  }},
                { "InPublication", new AttributeValue {
                      BOOL = false
                  } }
            }
            };
            client.PutItem(request);
        }

        private static void RetrieveItem()
        {
            var request = new GetItemRequest
            {
                TableName = tableName,
                Key = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      N = "1000"
                  } }
            },
                ProjectionExpression = "Id, ISBN, Title, Authors",
                ConsistentRead = true
            };
            var response = client.GetItem(request);

            // Check the response.
            var attributeList = response.Item; // attribute list in the response.
            Console.WriteLine("\nPrinting item after retrieving it ............");
            PrintItem(attributeList);
        }

        private static void UpdateMultipleAttributes()
        {
            var request = new UpdateItemRequest
            {
                Key = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      N = "1000"
                  } }
            },
                // Perform the following updates:
                // 1) Add two new authors to the list
                // 1) Set a new attribute
                // 2) Remove the ISBN attribute
                ExpressionAttributeNames = new Dictionary<string, string>()
            {
                {"#A","Authors"},
                {"#NA","NewAttribute"},
                {"#I","ISBN"}
            },
                ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
            {
                {":auth",new AttributeValue {
                     SS = {"Author YY", "Author ZZ"}
                 }},
                {":new",new AttributeValue {
                     S = "New Value"
                 }}
            },

                UpdateExpression = "ADD #A :auth SET #NA = :new REMOVE #I",

                TableName = tableName,
                ReturnValues = "ALL_NEW" // Give me all attributes of the updated item.
            };
            var response = client.UpdateItem(request);

            // Check the response.
            var attributeList = response.Attributes; // attribute list in the response.
                                                     // print attributeList.
            Console.WriteLine("\nPrinting item after multiple attribute update ............");
            PrintItem(attributeList);
        }

        private static void UpdateExistingAttributeConditionally()
        {
            var request = new UpdateItemRequest
            {
                Key = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      N = "1000"
                  } }
            },
                ExpressionAttributeNames = new Dictionary<string, string>()
            {
                {"#P", "Price"}
            },
                ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
            {
                {":newprice",new AttributeValue {
                     N = "22.00"
                 }},
                {":currprice",new AttributeValue {
                     N = "20.00"
                 }}
            },
                // This updates price only if current price is 20.00.
                UpdateExpression = "SET #P = :newprice",
                ConditionExpression = "#P = :currprice",

                TableName = tableName,
                ReturnValues = "ALL_NEW" // Give me all attributes of the updated item.
            };
            var response = client.UpdateItem(request);

            // Check the response.
            var attributeList = response.Attributes; // attribute list in the response.
            Console.WriteLine("\nPrinting item after updating price value conditionally ............");
            PrintItem(attributeList);
        }

        private static void DeleteItem()
        {
            var request = new DeleteItemRequest
            {
                TableName = tableName,
                Key = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      N = "1000"
                  } }
            },

                // Return the entire item as it appeared before the update.
                ReturnValues = "ALL_OLD",
                ExpressionAttributeNames = new Dictionary<string, string>()
            {
                {"#IP", "InPublication"}
            },
                ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
            {
                {":inpub",new AttributeValue {
                     BOOL = false
                 }}
            },
                ConditionExpression = "#IP = :inpub"
            };

            var response = client.DeleteItem(request);

            // Check the response.
            var attributeList = response.Attributes; // Attribute list in the response.
                                                     // Print item.
            Console.WriteLine("\nPrinting item that was just deleted ............");
            PrintItem(attributeList);
        }

        private static void PrintItem(Dictionary<string, AttributeValue> attributeList)
        {
            foreach (KeyValuePair<string, AttributeValue> kvp in attributeList)
            {
                string attributeName = kvp.Key;
                AttributeValue value = kvp.Value;

                Console.WriteLine(
                    attributeName + " " +
                    (value.S == null ? "" : "S=[" + value.S + "]") +
                    (value.N == null ? "" : "N=[" + value.N + "]") +
                    (value.SS == null ? "" : "SS=[" + string.Join(",", value.SS.ToArray()) + "]") +
                    (value.NS == null ? "" : "NS=[" + string.Join(",", value.NS.ToArray()) + "]")
                    );
            }
            Console.WriteLine("************************************************");
        }
    }
}
```

# 例: AWS SDK for .NET 低レベル API を使用したバッチオペレーション
<a name="batch-operation-lowlevel-dotnet"></a>

**Topics**
+ [例: AWS SDK for .NET 低レベル API を使用したバッチ書き込みオペレーション](#batch-write-low-level-dotnet)
+ [例: AWS SDK for .NET 低レベル API を使用したバッチ取得オペレーション](#LowLevelDotNetBatchGet)

このセクションでは、Amazon DynamoDB がサポートするバッチ操作、*バッチ書き込み*、および*バッチ取得*の例を示します。

## 例: AWS SDK for .NET 低レベル API を使用したバッチ書き込みオペレーション
<a name="batch-write-low-level-dotnet"></a>

以下の C\$1 コード例では、`BatchWriteItem` メソッドを使用して、以下の置換および削除のオペレーションを実行します。
+ `Forum` テーブル内で 1 つの項目を配置します。
+ `Thread` テーブルに対して 1 つの項目を配置および削除します。

バッチの書き込みリクエストを作成すると、1 つまたは複数のテーブルに対して多数の置換リクエストと削除リクエストを指定できます。ただし、DynamoDB `BatchWriteItem` では、1 回のバッチ書き込みオペレーションで可能なバッチ書き込みリクエストのサイズ、置換および削除のオペレーションの数を制限しています。詳細については、[BatchWriteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html) を参照してください。これらの制限を超えるリクエストは却下されます。プロビジョニングされたスループットがテーブルに不足しているためにこのリクエストを処理できない場合は、応答時に未処理のリクエスト項目が返されます。

以下の例では、未処理のリクエスト項目がないか、応答を確認します。未処理のリクエスト項目がある場合は、`BatchWriteItem` リクエストをループバックして再送信します。プログラムで、これらのサンプルテーブルを作成し、サンプルデータをアップロードすることもできます。詳細については、「」を参照してください[AWS SDK for .NET を使用してサンプルテーブルを作成してデータをアップロードする](AppendixSampleDataCodeDotNET.md)

以下の例をテストするための詳細な手順については、「[.NET コード例](CodeSamples.DotNet.md)」を参照してください。

**Example**  

```
using System;
using System.Collections.Generic;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;

namespace com.amazonaws.codesamples
{
    class LowLevelBatchWrite
    {
        private static string table1Name = "Forum";
        private static string table2Name = "Thread";
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();

        static void Main(string[] args)
        {
            try
            {
                TestBatchWrite();
            }
            catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
            catch (Exception e) { Console.WriteLine(e.Message); }

            Console.WriteLine("To continue, press Enter");
            Console.ReadLine();
        }

        private static void TestBatchWrite()
        {
            var request = new BatchWriteItemRequest
            {
                ReturnConsumedCapacity = "TOTAL",
                RequestItems = new Dictionary<string, List<WriteRequest>>
            {
                {
                    table1Name, new List<WriteRequest>
                    {
                        new WriteRequest
                        {
                            PutRequest = new PutRequest
                            {
                                Item = new Dictionary<string, AttributeValue>
                                {
                                    { "Name", new AttributeValue {
                                          S = "S3 forum"
                                      } },
                                    { "Threads", new AttributeValue {
                                          N = "0"
                                      }}
                                }
                            }
                        }
                    }
                },
                {
                    table2Name, new List<WriteRequest>
                    {
                        new WriteRequest
                        {
                            PutRequest = new PutRequest
                            {
                                Item = new Dictionary<string, AttributeValue>
                                {
                                    { "ForumName", new AttributeValue {
                                          S = "S3 forum"
                                      } },
                                    { "Subject", new AttributeValue {
                                          S = "My sample question"
                                      } },
                                    { "Message", new AttributeValue {
                                          S = "Message Text."
                                      } },
                                    { "KeywordTags", new AttributeValue {
                                          SS = new List<string> { "S3", "Bucket" }
                                      } }
                                }
                            }
                        },
                        new WriteRequest
                        {
                            // For the operation to delete an item, if you provide a primary key value
                            // that does not exist in the table, there is no error, it is just a no-op.
                            DeleteRequest = new DeleteRequest
                            {
                                Key = new Dictionary<string, AttributeValue>()
                                {
                                    { "ForumName",  new AttributeValue {
                                          S = "Some partition key value"
                                      } },
                                    { "Subject", new AttributeValue {
                                          S = "Some sort key value"
                                      } }
                                }
                            }
                        }
                    }
                }
            }
            };

            CallBatchWriteTillCompletion(request);
        }

        private static void CallBatchWriteTillCompletion(BatchWriteItemRequest request)
        {
            BatchWriteItemResponse response;

            int callCount = 0;
            do
            {
                Console.WriteLine("Making request");
                response = client.BatchWriteItem(request);
                callCount++;

                // Check the response.

                var tableConsumedCapacities = response.ConsumedCapacity;
                var unprocessed = response.UnprocessedItems;

                Console.WriteLine("Per-table consumed capacity");
                foreach (var tableConsumedCapacity in tableConsumedCapacities)
                {
                    Console.WriteLine("{0} - {1}", tableConsumedCapacity.TableName, tableConsumedCapacity.CapacityUnits);
                }

                Console.WriteLine("Unprocessed");
                foreach (var unp in unprocessed)
                {
                    Console.WriteLine("{0} - {1}", unp.Key, unp.Value.Count);
                }
                Console.WriteLine();

                // For the next iteration, the request will have unprocessed items.
                request.RequestItems = unprocessed;
            } while (response.UnprocessedItems.Count > 0);

            Console.WriteLine("Total # of batch write API calls made: {0}", callCount);
        }
    }
}
```

## 例: AWS SDK for .NET 低レベル API を使用したバッチ取得オペレーション
<a name="LowLevelDotNetBatchGet"></a>

次の C\$1 コード例では、`BatchGetItem` メソッドを使用して、Amazon DynamoDB の `Forum` テーブルおよび `Thread` テーブルから複数の項目を取り出します。`BatchGetItemRequest` は、各テーブルのテーブル名とプライマリキーのリストを指定します。この例では、取得した項目を印刷して応答を処理します。

以下の例をテストするための詳細な手順については、「[.NET コード例](CodeSamples.DotNet.md)」を参照してください。

**Example**  

```
using System;
using System.Collections.Generic;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;

namespace com.amazonaws.codesamples
{
    class LowLevelBatchGet
    {
        private static string table1Name = "Forum";
        private static string table2Name = "Thread";
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();

        static void Main(string[] args)
        {
            try
            {
                RetrieveMultipleItemsBatchGet();

                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
            catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
            catch (Exception e) { Console.WriteLine(e.Message); }
        }

        private static void RetrieveMultipleItemsBatchGet()
        {
            var request = new BatchGetItemRequest
            {
                RequestItems = new Dictionary<string, KeysAndAttributes>()
            {
                { table1Name,
                  new KeysAndAttributes
                  {
                      Keys = new List<Dictionary<string, AttributeValue> >()
                      {
                          new Dictionary<string, AttributeValue>()
                          {
                              { "Name", new AttributeValue {
                            S = "Amazon DynamoDB"
                        } }
                          },
                          new Dictionary<string, AttributeValue>()
                          {
                              { "Name", new AttributeValue {
                            S = "Amazon S3"
                        } }
                          }
                      }
                  }},
                {
                    table2Name,
                    new KeysAndAttributes
                    {
                        Keys = new List<Dictionary<string, AttributeValue> >()
                        {
                            new Dictionary<string, AttributeValue>()
                            {
                                { "ForumName", new AttributeValue {
                                      S = "Amazon DynamoDB"
                                  } },
                                { "Subject", new AttributeValue {
                                      S = "DynamoDB Thread 1"
                                  } }
                            },
                            new Dictionary<string, AttributeValue>()
                            {
                                { "ForumName", new AttributeValue {
                                      S = "Amazon DynamoDB"
                                  } },
                                { "Subject", new AttributeValue {
                                      S = "DynamoDB Thread 2"
                                  } }
                            },
                            new Dictionary<string, AttributeValue>()
                            {
                                { "ForumName", new AttributeValue {
                                      S = "Amazon S3"
                                  } },
                                { "Subject", new AttributeValue {
                                      S = "S3 Thread 1"
                                  } }
                            }
                        }
                    }
                }
            }
            };

            BatchGetItemResponse response;
            do
            {
                Console.WriteLine("Making request");
                response = client.BatchGetItem(request);

                // Check the response.
                var responses = response.Responses; // Attribute list in the response.

                foreach (var tableResponse in responses)
                {
                    var tableResults = tableResponse.Value;
                    Console.WriteLine("Items retrieved from table {0}", tableResponse.Key);
                    foreach (var item1 in tableResults)
                    {
                        PrintItem(item1);
                    }
                }

                // Any unprocessed keys? could happen if you exceed ProvisionedThroughput or some other error.
                Dictionary<string, KeysAndAttributes> unprocessedKeys = response.UnprocessedKeys;
                foreach (var unprocessedTableKeys in unprocessedKeys)
                {
                    // Print table name.
                    Console.WriteLine(unprocessedTableKeys.Key);
                    // Print unprocessed primary keys.
                    foreach (var key in unprocessedTableKeys.Value.Keys)
                    {
                        PrintItem(key);
                    }
                }

                request.RequestItems = unprocessedKeys;
            } while (response.UnprocessedKeys.Count > 0);
        }

        private static void PrintItem(Dictionary<string, AttributeValue> attributeList)
        {
            foreach (KeyValuePair<string, AttributeValue> kvp in attributeList)
            {
                string attributeName = kvp.Key;
                AttributeValue value = kvp.Value;

                Console.WriteLine(
                    attributeName + " " +
                    (value.S == null ? "" : "S=[" + value.S + "]") +
                    (value.N == null ? "" : "N=[" + value.N + "]") +
                    (value.SS == null ? "" : "SS=[" + string.Join(",", value.SS.ToArray()) + "]") +
                    (value.NS == null ? "" : "NS=[" + string.Join(",", value.NS.ToArray()) + "]")
                    );
            }
            Console.WriteLine("************************************************");
        }
    }
}
```

# 例: AWS SDK for .NET 低レベル API を使用したバイナリタイプ属性の処理
<a name="LowLevelDotNetBinaryTypeExample"></a>

以下の C\$1 コード例は、バイナリタイプ属性の処理の例です。この例では、`Reply` テーブルに項目を追加します。この項目には、圧縮データを格納するバイナリタイプ属性（`ExtendedMessage`）などがあります。また、この例では、項目を取得し、すべての属性値を印刷します。説明のため、この例では `GZipStream` クラスを使用して、サンプルストリームを圧縮し、圧縮したデータを `ExtendedMessage` 属性に割り当て、属性値を出力するときに解凍します。

以下の例をテストするための詳細な手順については、「[.NET コード例](CodeSamples.DotNet.md)」を参照してください。

**Example**  

```
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;

namespace com.amazonaws.codesamples
{
    class LowLevelItemBinaryExample
    {
        private static string tableName = "Reply";
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();

        static void Main(string[] args)
        {
            // Reply table primary key.
            string replyIdPartitionKey = "Amazon DynamoDB#DynamoDB Thread 1";
            string replyDateTimeSortKey = Convert.ToString(DateTime.UtcNow);

            try
            {
                CreateItem(replyIdPartitionKey, replyDateTimeSortKey);
                RetrieveItem(replyIdPartitionKey, replyDateTimeSortKey);
                // Delete item.
                DeleteItem(replyIdPartitionKey, replyDateTimeSortKey);
                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
            catch (AmazonDynamoDBException e) { Console.WriteLine(e.Message); }
            catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
            catch (Exception e) { Console.WriteLine(e.Message); }
        }

        private static void CreateItem(string partitionKey, string sortKey)
        {
            MemoryStream compressedMessage = ToGzipMemoryStream("Some long extended message to compress.");
            var request = new PutItemRequest
            {
                TableName = tableName,
                Item = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      S = partitionKey
                  }},
                { "ReplyDateTime", new AttributeValue {
                      S = sortKey
                  }},
                { "Subject", new AttributeValue {
                      S = "Binary type "
                  }},
                { "Message", new AttributeValue {
                      S = "Some message about the binary type"
                  }},
                { "ExtendedMessage", new AttributeValue {
                      B = compressedMessage
                  }}
            }
            };
            client.PutItem(request);
        }

        private static void RetrieveItem(string partitionKey, string sortKey)
        {
            var request = new GetItemRequest
            {
                TableName = tableName,
                Key = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      S = partitionKey
                  } },
                { "ReplyDateTime", new AttributeValue {
                      S = sortKey
                  } }
            },
                ConsistentRead = true
            };
            var response = client.GetItem(request);

            // Check the response.
            var attributeList = response.Item; // attribute list in the response.
            Console.WriteLine("\nPrinting item after retrieving it ............");

            PrintItem(attributeList);
        }

        private static void DeleteItem(string partitionKey, string sortKey)
        {
            var request = new DeleteItemRequest
            {
                TableName = tableName,
                Key = new Dictionary<string, AttributeValue>()
            {
                { "Id", new AttributeValue {
                      S = partitionKey
                  } },
                { "ReplyDateTime", new AttributeValue {
                      S = sortKey
                  } }
            }
            };
            var response = client.DeleteItem(request);
        }

        private static void PrintItem(Dictionary<string, AttributeValue> attributeList)
        {
            foreach (KeyValuePair<string, AttributeValue> kvp in attributeList)
            {
                string attributeName = kvp.Key;
                AttributeValue value = kvp.Value;

                Console.WriteLine(
                    attributeName + " " +
                    (value.S == null ? "" : "S=[" + value.S + "]") +
                    (value.N == null ? "" : "N=[" + value.N + "]") +
                    (value.SS == null ? "" : "SS=[" + string.Join(",", value.SS.ToArray()) + "]") +
                    (value.NS == null ? "" : "NS=[" + string.Join(",", value.NS.ToArray()) + "]") +
                    (value.B == null ? "" : "B=[" + FromGzipMemoryStream(value.B) + "]")
                    );
            }
            Console.WriteLine("************************************************");
        }

        private static MemoryStream ToGzipMemoryStream(string value)
        {
            MemoryStream output = new MemoryStream();
            using (GZipStream zipStream = new GZipStream(output, CompressionMode.Compress, true))
            using (StreamWriter writer = new StreamWriter(zipStream))
            {
                writer.Write(value);
            }
            return output;
        }

        private static string FromGzipMemoryStream(MemoryStream stream)
        {
            using (GZipStream zipStream = new GZipStream(stream, CompressionMode.Decompress))
            using (StreamReader reader = new StreamReader(zipStream))
            {
                return reader.ReadToEnd();
            }
        }
    }
}
```

# DynamoDB でのセカンダリインデックスを使用したデータアクセス性の向上
<a name="SecondaryIndexes"></a>

Amazon DynamoDB によって、プライマリキーの値を指定して、テーブルの項目に高速アクセスすることが可能になります。しかし多くのアプリケーションでは、プライマリキー以外の属性を使って、データに効率的にアクセスできるようにセカンダリ（または代替）キーを 1 つ以上設定することで、メリットが得られることがあります。これに対応するために、1 つのテーブルで 1 つ以上のセカンダリインデックスを作成して、それらのインデックスに対して `Query` または `Scan` リクエストを実行することができます。

*セカンダリインデックス* は、テーブルからの属性のサブセットと、`Query` オペレーションをサポートする代替キーで構成されるデータ構造です。`Query` をテーブルで使用する場合と同じように、`Query` を使用してインデックスからデータを取得できます。テーブルには、複数のセカンダリインデックスを含めることができます。これにより、アプリケーションは複数の異なるクエリパターンにアクセスできます。

**注記**  
また、テーブルを `Scan` するのと同じように、インデックスも `Scan` できます。  
セカンダリインデックススキャンオペレーションのクロスアカウントアクセスは、現在、[リソースベースのポリシー](access-control-resource-based.md)ではサポートされていません。

すべてのセカンダリインデックスは 1 つのテーブルのみに関連付けられ、そこからデータを取得します。これはインデックスの*ベーステーブル*と呼ばれます。インデックスを作成する場合は、インデックスの代替キー (パーティションキーおよびソートキー) を定義します。また、ベーステーブルからインデックスに*射影* (コピー) したい属性を定義します。DynamoDB では、これらの属性とベーステーブルからのプライマリキー属性がインデックスにコピーされます。次に、テーブルに対してクエリまたはスキャンを実行する場合と同様に、インデックスに対してクエリまたはスキャンを実行します。

すべてのセカンダリインデックスは、DynamoDB によって自動的にメンテナンスされます。ベーステーブルの項目を追加、変更、または削除すると、そのテーブルのインデックスも更新され、この変更が反映されます。

DynamoDB は、次の 2 種類のセカンダリインデックスをサポートしています。
+ **[グローバルセカンダリインデックス](GSI.html) — **パーティションキーおよびソートキーを持つインデックス。ベーステーブルのものとは異なる場合があります。このインデックスに関するクエリがすべてのパーティションにまたがり、ベーステーブル内のすべてのデータを対象とする可能性があるため、グローバルセカンダリインデックスは「グローバル」と見なされます。グローバルセカンダリインデックスは、ベーステーブルとは別に独自のパーティション領域に保存され、ベーステーブルとは別にスケーリングします。
+ **[ローカルセカンダリインデックス](LSI.html) — **パーティションキーはベーステーブルと同じで、ソートキーが異なるインデックス。ローカルセカンダリインデックスは、ローカルセカンダリインデックスのすべてのパーティションの範囲が同じパーティションキーバリューを持つベーステーブルのパーティションに限定されるという意味で「ローカル」です。

グローバルセカンダリインデックスとローカルセカンダリインデックスの比較については、この動画を参照してください。

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/BkEu7zBWge8/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/BkEu7zBWge8)


**Topics**
+ [DynamoDB のグローバルセカンダリインデックスの使用](GSI.md)
+ [ローカルセカンダリインデックス](LSI.md)

使用するインデックスの種類を決定するときは、アプリケーションの要件を考慮する必要があります。次の表に、グローバルセカンダリインデックスとローカルセカンダリインデックスの主な違いを示します。


****  

| 特徴 | グローバルセカンダリインデックス | ローカルセカンダリインデックス | 
| --- | --- | --- | 
| キースキーマ | グローバルセカンダリインデックスのプライマリキーはシンプル (パーティションキー) または複合 (パーティションキーとソートキー) のいずれかとすることができます。 | ローカルセカンダリインデックスのプライマリキーは複合 (パーティションキーとソートキー) である必要があります。 | 
| キーの属性 | インデックスパーティションキーとソートキー (存在する場合) は、文字列、数値、またはバイナリ型の任意のベーステーブル属性とすることができます。 | インデックスのパーティションキーは、ベーステーブルのパーティションキーと同じ属性です。ソートキーは、文字列、数値、またはバイナリ型の任意のベーステーブル属性とすることができます。 | 
| パーティションキー値ごとのサイズ制限 | グローバルセカンダリインデックスのサイズ制限はありません。 | パーティションキーの値ごとに、すべてのインデックス付き項目の合計サイズが、10 GB 以下である必要があります。 | 
| オンラインインデックスオペレーション | グローバルセカンダリインデックスは、テーブルの作成と同時に作成できます。また、新しいグローバルセカンダリインデックスを既存のテーブルに追加したり、既存のグローバルセカンダリインデックスを削除したりすることもできます。詳細については、「」を参照してください[DynamoDB のグローバルセカンダリインデックスの管理](GSI.OnlineOps.md)  | ローカルセカンダリインデックスは、テーブルの作成と同時に作成されます。ローカルセカンダリインデックスを既存のテーブルに追加したり、既存のローカルセカンダリインデックスを削除したりすることはできません。 | 
| クエリとパーティション | グローバルセカンダリインデックスでは、すべてのパーティションでテーブル全体に対してクエリを実行できます。 | ローカルセカンダリインデックスでは、クエリのパーティションキー値で指定された 1 つのパーティションに対してクエリを実行できます。 | 
| 読み込み整合性 | グローバルセカンダリインデックスのクエリは結果整合性をサポートします。 | ローカルセカンダリインデックスのクエリを実行するとき、結果整合性または強い整合性のどちらかを選択できます。 | 
| プロビジョニングされたスループットの消費 | 各グローバルセカンダリインデックスには、読み込み/書き込みアクティビティに対する独自のプロビジョニングされたスループット設定があります。グローバルセカンダリインデックスのクエリまたはスキャンでは、ベーステーブルからではなく、インデックスからキャパシティーユニットを使用します。同じことが、テーブルへの書き込みによるグローバルセカンダリインデックスの更新にも当てはまります。グローバルテーブルに関連付けられたグローバルセカンダリインデックスは、書き込みキャパシティユニットを使用します。 | ローカルセカンダリインデックスのクエリまたはスキャンでは、ベーステーブルから読み込みキャパシティーユニットを使用します。テーブルに書き込むと、そのローカルセカンダリインデックスも更新されます。この更新では、ベーステーブルから書き込みキャパシティユニットを使用します。グローバルテーブルに関連付けられたローカルセカンダリインデックスは、レプリケートされた書き込みキャパシティユニットを使用します。 | 
| 射影される属性 | グローバルセカンダリインデックスのクエリまたはスキャンでは、インデックスに射影された属性のみをリクエストできます。DynamoDB では、テーブルから属性をフェッチしません。 | ローカルセカンダリインデックスをクエリまたはスキャンする場合、インデックスに射影されていない属性をリクエストできます。DynamoDB は、これらの属性をテーブルから自動的に取得します。 | 

セカンダリインデックスを持つテーブルを複数作成する場合は、順次作成する必要があります。たとえば、最初のテーブルを作成し、そのテーブルが `ACTIVE` になるまで待ちます。次のテーブルを作成し、そのテーブルが `ACTIVE` になるまで待ちます。セカンダリインデックスを持つ複数のテーブルを同時に作成しようとすると、DynamoDB は `LimitExceededException` を返します。

各セカンダリインデックスは、関連付けられているベーステーブルと同じ[テーブルクラス](HowItWorks.TableClasses.html)と[キャパシティモード](capacity-mode.md)を使用します。各セカンダリインデックスには、以下を指定する必要があります。
+ 作成するインデックスのタイプ – グローバルセカンダリインデックスまたはローカルセカンダリインデックスのいずれか。
+ インデックスの名前。インデックスの名前付けルールは、「[Amazon DynamoDB のクォータ](ServiceQuotas.md)」に示すようにテーブルの場合と同じです。名前は関連付けられているベーステーブルに対して一意である必要がありますが、別のベーステーブルに関連付けられているインデックスでも同じ名前を使用できます。
+ インデックスのキースキーマ。インデックスキースキーマの各属性は、型が `String`、`Number`、または `Binary` の最上位属性である必要があります。ドキュメントとセットを含むその他のデータ型は使用できません。キースキーマのその他の要件は、インデックスの種類によって異なります。
  + グローバルセカンダリインデックスの場合、パーティションキーはベーステーブルの任意のスカラー属性にすることができます。ソートキーはオプションです。このキーもベーステーブルの任意のスカラー属性にすることができます。
  + ローカルセカンダリインデックスの場合、パーティションキーは、ベーステーブルのパーティションキーと同じである必要があります。ソートキーは、非キーベーステーブル属性である必要があります。
+ ベーステーブルからインデックスに射影する追加の属性 (ある場合)。この属性は、すべてのインデックスに自動的に射影されるテーブルのキー属性とは別の属性です。スカラー、ドキュメント、およびセットを含む任意のデータ型の属性を射影できます。
+ インデックスのプロビジョニングされたスループット設定（必要な場合）:
  + グローバルセカンダリインデックスの場合、読み込み/書き込みキャパシティーユニット設定を指定する必要があります。このプロビジョニングされたスループット設定は、ベーステーブルの設定から独立しています。
  + ローカルセカンダリインデックスの場合、読み込み/書き込みキャパシティーユニット設定を指定する必要はありません。ローカルセカンダリインデックスに対する読み込み/書き込みオペレーションは、そのベーステーブルのプロビジョニングされたスループット設定から使用します。

クエリに最大限の柔軟性を持たせるために、テーブルごとに最大 20 個のグローバルセカンダリインデックス (デフォルトのクォータ) および最大 5 個のローカルセカンダリインデックスを作成できます。

テーブルのセカンダリインデックスの詳細なリストを取得するには、`DescribeTable` オペレーションを使用します。`DescribeTable` は、テーブルのすべてのセカンダリインデックスの名前、ストレージサイズ、および項目数を返します。これらの値はリアルタイムでは更新されませんが、約 6 時間ごとに更新されます。

セカンダリインデックスのデータには、`Query` または `Scan` オペレーションを使用してアクセスできます。使用するベーステーブル名とインデックス名、結果で返される属性、および適用する条件式またはフィルタを指定する必要があります。DynamoDB は、結果を昇順で返すことも降順で返すこともできます。

テーブルを削除すると、そのテーブルに関連付けられているすべてのインデックスも削除されます。

ベストプラクティスについては、[DynamoDB でセカンダリインデックスを使用するためのベストプラクティス。](bp-indexes.md) を参照してください。

# DynamoDB のグローバルセカンダリインデックスの使用
<a name="GSI"></a>

アプリケーションによっては、さまざまな属性をクエリ基準に使用して、いろいろな種類のクエリを実行する必要があります。このような要件に対応するために、1 つまたは複数の*グローバルセカンダリインデックス*を作成して、Amazon DynamoDB でこれらのインデックスに対して `Query` リクエストを発行できます。

**Topics**
+ [シナリオ: グローバルセカンダリインデックスの使用](#GSI.scenario)
+ [属性の射影](#GSI.Projections)
+ [マルチ属性キースキーマ](#GSI.MultiAttributeKeys)
+ [グローバルセカンダリインデックスからのデータの読み込み](#GSI.Reading)
+ [テーブルとグローバルセカンダリインデックス間のデータ同期](#GSI.Writes)
+ [グローバルセカンダリインデックスがあるテーブルクラス](#GSI.tableclasses)
+ [グローバルセカンダリインデックスに対するプロビジョニングされたスループットに関する考慮事項](#GSI.ThroughputConsiderations)
+ [グローバルセカンダリインデックスのストレージに関する考慮事項](#GSI.StorageConsiderations)
+ [パターンの設計](GSI.DesignPatterns.md)
+ [DynamoDB のグローバルセカンダリインデックスの管理](GSI.OnlineOps.md)
+ [DynamoDB でのインデックスキー違反の検出と修正](GSI.OnlineOps.ViolationDetection.md)
+ [グローバルセカンダリインデックスの操作: Java](GSIJavaDocumentAPI.md)
+ [グローバルセカンダリインデックスの操作: .NET](GSILowLevelDotNet.md)
+ [AWS CLI を使用した DynamoDBでのグローバルセカンダリインデックスの操作](GCICli.md)

## シナリオ: グローバルセカンダリインデックスの使用
<a name="GSI.scenario"></a>

たとえば、`GameScores` という名前のテーブルがあり、モバイルゲームアプリケーションのユーザーとスコアを記録しているとします。`GameScores` の各項目は、パーティションキー (`UserId`) およびソートキー (`GameTitle`) で特定されます。次の図は、テーブル内の項目の構成を示しています。一部表示されていない属性もあります。

![\[ユーザー ID、タイトル、スコア、日付、および勝敗のリストを含む GameScores テーブル。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/GSI_01.png)


ここで、各ゲームの最高スコアを表示する順位表アプリケーションを作成すると仮定します。キー属性 (`UserId` と `GameTitle`) を指定したクエリは非常に効率的です。ただし、アプリケーションが `GameScores` だけに基づいて `GameTitle` からデータを取り出す必要がある場合、`Scan` オペレーションを使用する必要があります。テーブルに追加される項目が増えるにつれ、すべてのデータのスキャンは低速で非効率的になります。このため、次のような質問に答えることが難しくなります。
+ ゲーム Meteor Blasters で記録された最高スコアはいくつですか?
+ Galaxy Invaders で最高スコアを獲得したユーザーは誰ですか?
+ 勝敗の最も高い比率は何ですか?

グローバルセカンダリインデックスを作成して、非キー属性に対するクエリの速度を上げることができます。グローバルセカンダリインデックスには、ベーステーブルからの属性の一部が格納されますが、それらはテーブルのプライマリキーとは異なるプライマリキーによって構成されます。インデックスキーは、テーブルからのキー属性を持つ必要がありません。また、テーブルと同じキースキーマを使用する必要もありません。

例えば、パーティションキーとして `GameTitle`、ソートキーとして `TopScore` を使用して、`GameTitleIndex` という名前のグローバルセカンダリインデックスを作成できます。ベーステーブルのプライマリキー属性は必ずインデックスに射影されるので、`UserId` 属性も存在します。次の図は、`GameTitleIndex` インデックスを示しています。

![\[タイトル、スコア、およびユーザー ID のリストを含む GameTitleIndex テーブル。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/GSI_02.png)


これで、`GameTitleIndex` に対してクエリを実行し、Meteor Blasters のスコアを簡単に入手できるようになります。結果は、ソートキーの値 `TopScore` 別に並べられます。`ScanIndexForward` パラメータを false に設定した場合、結果は降順で返されます。つまり、最高スコアが最初に返されます。

すべてのグローバルセカンダリインデックスには、パーティションキーが必要で、オプションのソートキーを指定できます。インデックスキースキーマは、テーブルスキーマとは異なるものにすることができます。シンプルなプライマリキー (パーティションキー) を持つテーブルを作成し、複合プライマリキー (パーティションキーおよびソートキー) を使用してグローバルセカンダリインデックスを作成できます。またはその逆もあります。インデックスキー属性は、ベーステーブルからの任意の最上位 `String`、`Number`、または `Binary` 属性で構成できます。その他のスカラー型、ドキュメント型、およびセット型は許可されません。

必要な場合、他のベーステーブル属性をインデックスに射影できます。インデックスのクエリを行うと、DynamoDB ではこれらの射影された属性を効率的に取り出すことができます。ただし、グローバルセカンダリインデックス のクエリでは、ベーステーブルから属性をフェッチできません。たとえば、上記の図に示すように、`GameTitleIndex` にクエリを実行した場合、クエリは `TopScore` 以外の非キー属性にアクセスすることはできません (キー属性 `GameTitle` と `UserId` は自動的に射影されます)。

DynamoDB テーブルでは、各キー値は一意である必要があります。ただし、グローバルセカンダリインデックスのキー値は一意である必要はありません。たとえば、Comet Quest という名前のゲームが特に難しく、多くの新しいユーザーが試しても、ゼロを上回るスコアを獲得することができないとします。以下にこれを表すいくつかのデータを示します。


****  

| UserId | GameTitle | TopScore | 
| --- | --- | --- | 
| 123 | Comet Quest | 0 | 
| 201 | Comet Quest | 0 | 
| 301 | Comet Quest | 0 | 

このデータを `GameScores` テーブルに追加すると、DynamoDB はそのデータを `GameTitleIndex` に伝達します。`GameTitle` として Comet Quest を `TopScore` として 0 を指定してインデックスに対するクエリを実行すると、次のデータが返されます。

![\[タイトル、最上位スコア、およびユーザー ID のリストを含むテーブル。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/GSI_05.png)


指定したキー値を持つ項目だけがレスポンスに表示されます。その一連のデータ内では、項目は特定の順に並んでいません。

グローバルセカンダリインデックスは、キー属性が実際に存在するデータ項目のみを追跡します。たとえば、別の新しい項目を `GameScores` テーブルに追加し、必須のプライマリキー属性だけを指定したとします。


****  

| UserId | GameTitle | 
| --- | --- | 
| 400 | Comet Quest | 

`TopScore` 属性を指定していないので、DynamoDB は、この項目を `GameTitleIndex` に伝達しません。このため、すべての Comet Quest 項目を対象に、`GameScores` に対してクエリを実行した場合、次の 4 つの項目が返されます。

![\[4 つのタイトル、最上位スコア、およびユーザー ID のリストを含むテーブル。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/GSI_04.png)


`GameTitleIndex` に対して同様のクエリを実行すると、4 つではなく 3 つの項目が返されます。これは、`TopScore` が存在しない項目はインデックスに反映されないためです。

![\[3 つのタイトル、最上位スコア、およびユーザー ID のリストを含むテーブル。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/GSI_05.png)


## 属性の射影
<a name="GSI.Projections"></a>

*射影*とは、テーブルからセカンダリインデックスにコピーされる属性のセットです。テーブルのパーティションキーとソートキーは常にインデックスに射影されます。アプリケーションのクエリ要件をサポートするために、他の属性を射影できます。インデックスをクエリすると、Amazon DynamoDB は、その属性が自身のテーブル内にあるかのように、プロジェクション内の任意の属性にアクセスできます。

セカンダリインデックスを作成するときは、インデックスに射影される属性を指定する必要があります。DynamoDB には、そのために次の 3 つのオプションが用意されています。
+ *KEYS\$1ONLY* – インデックス内の各項目は、テーブルパーティションキーとソートキーの値、およびインデックスキーの値のみで構成されます。`KEYS_ONLY` オプションを指定すると、セカンダリインデックスが最小になります。
+ *INCLUDE* – `KEYS_ONLY` の属性に加えて、セカンダリインデックスにその他の非キー属性が含まれるように指定できます。
+ *ALL* – セカンダリインデックスには、ソーステーブルのすべての属性が含まれます。すべてのテーブルデータがインデックスに複製されるため、`ALL` の射影にすると、セカンダリインデックスが最大になります。

前の図では、`GameTitleIndex` には 1 つの射影された属性 `UserId` のみがあります。そのため、アプリケーションはクエリで `UserId` および `GameTitle` を使用して各ゲームのトップスコアラーの `TopScore` を効率的に判断できますが、トップスコアラーの勝敗の最も高い比率は効率的に判断できません。そのためには、アプリケーションは基本テーブルで追加のクエリを実行して、各トップスコアラーの勝敗を取得する必要があります。このデータに対するクエリのサポートを効率化する方法として、次の図に示すように、これらの属性をベーステーブルからグローバルセカンダリインデックスに射影する方法があります。

![\[効率的なクエリのサポートのために非キー属性を GSI に射影する説明図。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/GSI_06.png)


非キー属性 `Wins` と `Losses` がインデックスに射影されるので、アプリケーションは、任意のゲーム、またはゲームとユーザー ID の任意の組み合わせに対して、勝敗の比率を特定できます。

グローバルセカンダリインデックスに射影する属性を選択する場合には、プロビジョニングされるスループットコストとストレージコストのトレードオフを考慮する必要があります。
+ ごく一部の属性だけに最小のレイテンシーでアクセスする必要がある場合は、それらの属性だけをグローバルセカンダリインデックスに射影することを検討してください。インデックスが小さいほど少ないコストで格納でき、書き込みコストも低くなります。
+ アプリケーションが非キー属性に頻繁にアクセスする場合には、それらの属性をグローバルセカンダリインデックスに射影することを検討してください。グローバルセカンダリインデックスのストレージコストの増加は、頻繁にテーブルスキャンを実行するコストの減少で相殺されます。
+ ほとんどの非キー属性に頻繁にアクセスする場合は、それらの属性を (場合によっては、ベーステーブル全体を) グローバルセカンダリインデックスに射影することができます。これにより、最大限の柔軟性が得られます。ただし、ストレージコストは増加するか、倍になります。
+ アプリケーションでテーブルのクエリを頻繁に行う必要がなく、テーブル内のデータに対する書き込みや更新が多数になる場合は、`KEYS_ONLY` を射影することを検討してください。グローバルセカンダリインデックスは、最小サイズになりますが、それでもクエリのアクティビティに必要な場合は利用可能です。

## マルチ属性キースキーマ
<a name="GSI.MultiAttributeKeys"></a>

グローバルセカンダリインデックスはマルチ属性キーをサポートしているため、複数の属性からパーティションキーとソートキーを作成できます。マルチ属性キーを使用すると、最大 4 つの属性からパーティションキーを、最大 4 つの属性からソートキーを、キースキーマごとに合計で最大 8 つの属性に作成できます。

マルチ属性キーは、属性を合成キーに手動で連結する必要がなくなるため、データモデルを簡素化します。`TOURNAMENT#WINTER2024#REGION#NA-EAST` のような複合文字列を作成する代わりに、ドメインモデルから自然属性を直接使用できます。DynamoDB は複合キーロジックを自動的に処理し、データ分散のために複数のパーティションキー属性をハッシュし、複数のソートキー属性にわたって階層型のソート順序を維持します。

例えば、試合をトーナメントとリージョン別に整理するゲームトーナメントシステムを考えてみましょう。マルチ属性キーを使用すると、パーティションキーを `tournamentId` と `region` の 2 つの異なる属性として定義できます。同様に、`round`、`bracket`、`matchId` などの複数の属性を使用してソートキーを定義し、自然な階層を作成できます。このアプローチでは、文字列の操作や解析を行わずに、データ型とコードをクリーンに保ちます。

マルチ属性キーを使用してグローバルセカンダリインデックスをクエリする場合は、等号条件を使用してすべてのパーティションキー属性を指定する必要があります。ソートキー属性の場合、キースキーマで定義されている順序で、左から右にクエリを実行できます。つまり、最初のソートキー属性のみ、最初の 2 つの属性を一緒に、またはすべての属性を一緒にクエリできますが、途中で属性をスキップすることはできません。`>`、`<`、`BETWEEN`、`begins_with()` などの不等条件は、クエリの最後の条件である必要があります。

マルチ属性キーは、既存のテーブルにグローバルセカンダリインデックスを作成する場合、特に効果的です。データ全体で合成キーをバックフィルすることなく、テーブルに既に存在する属性を使用できます。これにより、さまざまな属性の組み合わせを使用してデータを再編成するインデックスを作成することで、アプリケーションに新しいクエリパターンを簡単に追加できます。

マルチ属性キーの各属性は、独自のデータ型 `String` (S)、`Number` (N)、または `Binary` (B) を持つことができます。データ型を選択するときは、ゼロパディングを必要とせずに `Number` 属性を数値順にソートし、`String` 属性を辞書順にソートすることを検討してください。例えば、スコア属性に `Number` 型を使用する場合、値 5、50、500、1000 は自然な数値順にソートされます。同じ値を `String` 型とする場合は、先頭にゼロを埋めない限り、「1000」、「5」、「50」、「500」とソートされます。

マルチ属性キーを設計する場合は、属性を最も一般的なものから最も具体的な順に並べてください。パーティションキーの場合、常に一緒にクエリされ、適切なデータ分散を提供する属性を組み合わせます。ソートキーの場合、クエリの柔軟性を最大化するために、頻繁にクエリされる属性を階層の最初に配置します。この順序により、アクセスパターンに一致する任意の詳細度のレベルでクエリを実行できます。

実装例については、「[マルチ属性キー](GSI.DesignPattern.MultiAttributeKeys.md)」を参照してください。

## グローバルセカンダリインデックスからのデータの読み込み
<a name="GSI.Reading"></a>

グローバルセカンダリインデックスから項目を取得するには、`Query` および `Scan` オペレーションを使用します。`GetItem` および `BatchGetItem` オペレーションは、グローバルセカンダリインデックスでは使用できません。

### グローバルセカンダリインデックスのクエリ
<a name="GSI.Querying"></a>

`Query` オペレーションを使用して、グローバルセカンダリインデックスの 1 つ以上の項目にアクセスすることができます。クエリでは、使用するベーステーブル名とインデックス名、クエリ結果で返される属性、および適用するクエリ条件を指定する必要があります。DynamoDB は、結果を昇順で返すことも降順で返すこともできます。

順位表アプリケーションのゲームデータをリクエストする `Query` から返される次のデータについて考えます。

```
{
    "TableName": "GameScores",
    "IndexName": "GameTitleIndex",
    "KeyConditionExpression": "GameTitle = :v_title",
    "ExpressionAttributeValues": {
        ":v_title": {"S": "Meteor Blasters"}
    },
    "ProjectionExpression": "UserId, TopScore",
    "ScanIndexForward": false
}
```

このクエリでは次のようになっています。
+ DynamoDB は、*GameTitle* パーティションキーを使用して *GameTitleIndex* にアクセスし、Meteor Blasters のインデックス項目を特定します。このキーを持つすべてのインデックス項目が、すばやく取り出せるように隣り合わせに格納されます。
+ このゲーム内で、DynamoDB はインデックスを使用して、このゲームのすべてのユーザーの ID と最高スコアにアクセスします。
+ `ScanIndexForward` パラメータが false に設定されているので、結果は降順で返されます。

### グローバルセカンダリインデックスのスキャン
<a name="GSI.Scanning"></a>

`Scan` オペレーションを使用して、グローバルセカンダリインデックスからすべてのデータを取得することができます。リクエストにはベーステーブル名とインデックス名を指定する必要があります。`Scan` では、DynamoDB はインデックスのすべてのデータを読み込み、それをアプリケーションに返します。また、データの一部のみを返し、残りのデータを破棄するようにリクエストすることもできます。これを行うには、`FilterExpression` オペレーションの `Scan` パラメータを使用します。詳細については、「」を参照してください[Scan のフィルタ式](Scan.md#Scan.FilterExpression)

## テーブルとグローバルセカンダリインデックス間のデータ同期
<a name="GSI.Writes"></a>

DynamoDB では、各グローバルセカンダリインデックスをそのベーステーブルと自動的に同期させています。アプリケーションがテーブルに項目を書き込むか、削除すると、そのテーブルのすべてのグローバルセカンダリインデックスは、結果整合性モデルを使用して、非同期で更新されます。アプリケーションがインデックスに直接書き込むことはありません。ただし、DynamoDB でこれらのインデックスがどのように維持されるかを理解することは重要です。

 グローバルセカンダリーインデックスは、基本テーブルから読み込み/書き込みキャパシティーモードを継承します。詳細については、「[DynamoDB でキャパシティモードを切り替える際の考慮事項](bp-switching-capacity-modes.md)」を参照してください。

グローバルセカンダリインデックスを作成するときは、1 つ以上のインデックスキー属性およびそれらのデータ型を指定します。つまり、ベーステーブルに項目を書き込むとき、それらの属性のデータ型が、インデックスキースキーマのデータ型に一致する必要があります。`GameTitleIndex` の場合、インデックス内の `GameTitle` パーティションキーは、`String` データ型として定義されています。インデックス内の `TopScore` ソートキーは、`Number` の型です。`GameScores` テーブルに項目を追加し、`GameTitle` または `TopScore` に対して別のデータ型を指定する場合、データ型の不一致により DynamoDB によって `ValidationException` が返されます。

テーブルに項目を入力するか、削除すると、そのテーブルのグローバルセカンダリインデックスは、結果整合性のある方法で更新されます。テーブルデータへの変更は、通常は、瞬時にグローバルセカンダリインデックスに伝達されます。ただし、万が一障害が発生した場合は、長い伝達遅延が発生することがあります。このため、アプリケーションでは、グローバルセカンダリインデックスに関するクエリに対して、最新でない結果が返される状況を予期し、それに対応する必要があります。

テーブルに項目を書き込む場合には、グローバルセカンダリインデックスのソートキーに対して属性を指定する必要はありません。`GameTitleIndex` を例にとると、`GameScores` テーブルに新しい項目を書き込むために、`TopScore` 属性に値を指定する必要はありません。このケースでは、DynamoDB はこの特定の項目のインデックスにデータを書き込むことはありません。

多数のグローバルセカンダリインデックスがあるテーブルは、インデックス数が少ないテーブルに比べて書き込みアクティビティに多くのコストを要します。詳細については、「[グローバルセカンダリインデックスに対するプロビジョニングされたスループットに関する考慮事項](#GSI.ThroughputConsiderations)」を参照してください。

## グローバルセカンダリインデックスがあるテーブルクラス
<a name="GSI.tableclasses"></a>

グローバルセカンダリインデックスは、常にベーステーブルと同じテーブルクラスを使用します。テーブルに新しいグローバルセカンダリインデックスが追加されるたびに、新しいインデックスはベーステーブルと同じテーブルクラスを使用します。テーブルのテーブルクラスが更新されると、関連するすべてのグローバルセカンダリインデックスも更新されます。

## グローバルセカンダリインデックスに対するプロビジョニングされたスループットに関する考慮事項
<a name="GSI.ThroughputConsiderations"></a>

プロビジョニングモードのテーブルでグローバルセカンダリインデックスを作成するときには、そのインデックスに対して予想されるワークロードに応じた読み込み/書き込みキャパシティーユニットを指定する必要があります。グローバルセカンダリインデックスのプロビジョニングされたスループット設定は、そのベーステーブルの設定とは独立しています。グローバルセカンダリインデックスに対する `Query` オペレーションでは、ベーステーブルではなく、インデックスから読み込みキャパシティーユニットを使用します。テーブルで項目を入力、更新または削除すると、そのテーブルのグローバルセカンダリインデックスも更新されます。これらのインデックス更新は、ベーステーブルからではなく、インデックスから書き込みキャパシティーユニットを消費します。

例えば、グローバルセカンダリインデックスに対して `Query` を実行し、そのプロビジョニングされた読み込みキャパシティーを超えた場合、リクエストは調整されます。多量の書き込みアクティビティをテーブルに対して実行するときに、そのテーブルのグローバルセカンダリインデックスに十分な書き込み容量がない場合、そのテーブルに対する書き込みアクティビティは調整されます。

**重要**  
 スロットリングを避けるため、グローバルセカンダリインデックスのプロビジョニングされた書き込みキャパシティーは、ベーステーブルの書き込みキャパシティーと同じかそれより大きい必要があります。これは、新しい更新ではベーステーブルとグローバルセカンダリインデックスの両方に書き込むためです。

グローバルセカンダリインデックスのプロビジョニングされたスループット設定を表示するには、`DescribeTable` オペレーションを使用します。テーブルのすべてのグローバルセカンダリインデックスに関する詳細情報が返されます。

### 読み込みキャパシティユニット
<a name="GSI.ThroughputConsiderations.Reads"></a>

グローバルセカンダリインデックスでは、結果整合性のある読み込みをサポートしており、各読み込みで、読み込みキャパシティーユニットの半分を消費します。つまり、1 回のグローバルセカンダリインデックスのクエリでは、1 読み込みキャパシティーユニットあたり最大 2 × 4 KB = 8 KB を取り出すことができます。

グローバルセカンダリインデックスのクエリの場合、DynamoDB はプロビジョニングされた読み込みアクティビティをテーブルに対するクエリと同じ方法で計算します。唯一の違いは、ベーステーブル内の項目のサイズではなくインデックスエントリのサイズに基づいて計算が行われることです。読み込みキャパシティーユニットの数は、返されたすべての項目について射影されたすべての属性のサイズの合計です。結果は、次の 4 KB 境界まで切り上げられます。DynamoDB がプロビジョニングされたスループットの利用率を計算する方法の詳細については、「[DynamoDB プロビジョンドキャパシティモード](provisioned-capacity-mode.md)」を参照してください。

`Query` オペレーションによって返される結果の最大サイズは 1 MB です。これには、返されるすべての項目にわたる、すべての属性の名前と値のサイズが含まれます。

例えば、各項目に 2,000 バイトのデータが格納されているグローバルセカンダリインデックスがあるとします。このインデックスに対して `Query` を実行したら、クエリの `KeyConditionExpression` が 8 項目に一致したとします。一致する項目の合計サイズは、2,000 バイト × 8 項目 = 16,000 バイトです。これは、最も近い 4 KB 境界に切り上げられます。グローバルセカンダリインデックスのクエリは結果整合性なので、合計コストは、0.5 × (16 KB / 4 KB)、つまり、2 読み込みキャパシティーユニットです。

### 書き込みキャパシティユニット
<a name="GSI.ThroughputConsiderations.Writes"></a>

テーブルの項目が追加、更新、または削除されたときに、グローバルセカンダリインデックスがこの影響を受ける場合、グローバルセカンダリインデックスは、そのオペレーションに対してプロビジョニングされた書き込みキャパシティーユニットを使用します。書き込み用にプロビジョニングされたスループットの合計コストは、ベーステーブルに対する書き込みと、グローバルセカンダリインデックスの更新で消費された書き込みキャパシティーユニットの合計になります。テーブルへの書き込みには、グローバルセカンダリインデックスの更新は必要ないので、インデックスから使用される書き込み容量はありません。

テーブルへの書き込みに成功するように、テーブルとそのすべてのグローバルセカンダリインデックスに対するプロビジョニングされたスループット設定は、書き込みに対応できるだけの十分な書き込みキャパシティーを備えている必要があります。十分でない場合、書き込みが調整されます。

**重要**  
グローバルセカンダリインデックス (GSI) を作成する際、基本テーブルへの書き込みによって生じる GSI アクティビティが GSI のプロビジョニングされた書き込み容量を超える場合、基本テーブルへの書き込み操作がスロットリングされる可能性があります。このスロットリングは、インデックス作成プロセスから本番ワークロードの中断の可能性に至るまで、すべての書き込み操作に影響します。詳細については、「[Amazon DynamoDB でのスロットリングのトラブルシューティング](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TroubleshootingThrottling.html)」を参照してください。

グローバルセカンダリインデックスに項目を書き込むコストは、いくつかの要因に左右されます。
+ インデックス付き属性が定義されたテーブルに新しい項目を書き込む場合、または既存の項目を更新して未定義のインデックス付き属性を定義する場合には、インデックスへの項目の挿入に 1 回の書き込みオペレーションが必要です。
+ テーブルに対する更新によってインデックス付きキー属性の値が（A から B に）変更された場合には、インデックスから既存の項目を削除し、インデックスに新しい項目を挿入するために、2 回の書き込みが必要です。  
+ インデックス内に既存の項目があって、テーブルに対する書き込みによってインデックス付き属性が削除された場合は、インデックスから古い項目の射影を削除するために、1 回の書き込みが必要です。
+ 項目の更新の前後にインデックス内に項目が存在しない場合は、インデックスで追加の書き込みコストは発生しません。
+ テーブルに対する更新によってインデックスキースキーマの射影された属性の値のみが変更され、インデックス付きキー属性の値は変更されない場合は、インデックスに射影された属性の値を更新するために、1 回の書き込みが必要です。

これらすべての要因は、インデックス内の各項目のサイズが 1 KB 以下であるという前提で書き込みキャパシティーユニット数を算出します。インデックスエントリがそれよりも大きい場合は、書き込みキャパシティーユニットを追加する必要があります。クエリが返す必要がある属性を特定し、それらの属性だけをインデックスに射影することで、書き込みコストは最小になります。

## グローバルセカンダリインデックスのストレージに関する考慮事項
<a name="GSI.StorageConsiderations"></a>

アプリケーションがテーブルに項目を書き込むと、DynamoDB では、正しい属性のサブセットが、それらの属性が現れる必要があるグローバルセカンダリインデックスに自動的にコピーされます。AWS アカウントでは、テーブル内の項目のストレージと、そのベーステーブルのグローバルセカンダリインデックスにある属性のストレージに対して課金されます。

インデックス項目が使用するスペースの量は、次の量の合計になります。
+ ベーステーブルのプライマリキー (パーティションキーとソートキー) のサイズのバイト数
+ インデックスキー属性のサイズのバイト数
+ 射影された属性（存在する場合）のサイズのバイト数
+ インデックス項目あたり 100 バイトのオーバーヘッド

グローバルセカンダリインデックスのストレージ要件の見積もりは、インデックス内の 1 項目の平均サイズの見積もり値に、ベーステーブル内のグローバルセカンダリインデックスキー属性を持つ項目の数を掛けて算出します。

特定の属性が定義されていない項目がテーブルに含まれていて、その属性がインデックスパーティションキーまたはソートキーとして定義されている場合、DynamoDB はその項目のデータをインデックスに書き込みません。

# パターンの設計
<a name="GSI.DesignPatterns"></a>

設計パターンは、グローバルセカンダリインデックスを使用する際の一般的な課題に対する実証済みのソリューションを提供します。これらのパターンは、特定のユースケースに合わせてインデックスを構築する方法を示すことで、効率的でスケーラブルなアプリケーションを構築するのに役立ちます。

各パターンには、独自のアプリケーションにパターンを適用するのに役立つコード例、ベストプラクティス、実際のユースケースを含む完全な実装ガイドが含まれています。

**Topics**
+ [マルチ属性キー](GSI.DesignPattern.MultiAttributeKeys.md)

# マルチ属性キーパターン
<a name="GSI.DesignPattern.MultiAttributeKeys"></a>

## 概要
<a name="GSI.DesignPattern.MultiAttributeKeys.Overview"></a>

マルチ属性キーを使用すると、それぞれ最大 4 つの属性で構成されるグローバルセカンダリインデックス (GSI) パーティションとソートキーを作成できます。これにより、クライアント側のコードが減り、最初にデータをモデル化し、後で新しいアクセスパターンを追加することが容易になります。

一般的なシナリオを考えてみましょう。複数の階層属性で項目をクエリする GSI を作成するには、従来、値を連結して合成キーを作成する必要があります。例えば、ゲームアプリでは、トーナメント、リージョン、ラウンドでトーナメントマッチをクエリするために、TOURNAMENT\$1WINTER2024\$1REGION\$1NA-EAST などの合成 GSI パーティションキーと ROUND\$1SEMIFINALS\$1BRACKET\$1UPPER などの合成ソートキーを作成できます。このアプローチは機能しますが、既存のテーブルに GSI を追加する場合は、データを書き込むときに文字列を連結し、読み取り時に解析し、既存のすべての項目にわたって合成キーをバックフィルする必要があります。これにより、コードが整理整頓され、個々のキーコンポーネントで型の安全性を維持することが困難になります。

マルチ属性キーは、GSI のこの問題を解決します。GSI パーティションキーは、tournamentId や region などの複数の既存の属性を使用して定義します。DynamoDB は複合キーロジックを自動的に処理し、データ分散のためにそれらをハッシュします。ドメインモデルから自然属性を使用して項目を記述すると、GSI は自動的にインデックスを作成します。連結なし、解析なし、バックフィルなし。コードはきれいに保たれ、データは入力され、クエリはシンプルになります。このアプローチは、自然属性のグループ化を含む階層データがある場合に特に役立ちます (トーナメント → リージョン → ラウンド、または組織 → 部門 → チームなど)。

## アプリケーションの例
<a name="GSI.DesignPattern.MultiAttributeKeys.ApplicationExample"></a>

このガイドでは、e スポーツプラットフォームのトーナメントマッチ追跡システムを構築する方法について説明します。プラットフォームは、ブラケット管理のためのトーナメントとリージョン、マッチング履歴のための選手、スケジューリングのための日付など、複数のディメンションにわたって試合を効率的にクエリする必要があります。

## データモデル
<a name="GSI.DesignPattern.MultiAttributeKeys.DataModel"></a>

このチュートリアルでは、トーナメントマッチ追跡システムは 3 つの主要なアクセスパターンをサポートし、それぞれに異なるキー構造が必要です。

**アクセスパターン 1:** 一意の ID で特定の試合を検索する
+ **解決策:** パーティションキーとして `matchId` をベーステーブルと使用する

**アクセスパターン 2:** 特定のトーナメントとリージョンのすべての試合をクエリし、オプションでラウンド、ブラケット、または試合でフィルタリングする
+ **解決策:** マルチ属性パーティションキー (`tournamentId` \$1 `region`) とマルチ属性ソートキー (`round` \$1 `bracket` \$1 `matchId`) を持つグローバルセカンダリインデックス
+ **クエリの例:** 「NA-EAST リージョン内の All WINTER2024 の試合」、または「WINTER2024/NA-EAST の UPPER ブラケット内の All SEMIFINALS の試合」

**アクセスパターン 3:** 選手の試合履歴をクエリし、オプションで日付範囲またはトーナメントラウンドでフィルタリングする
+ **解決策:** 単一パーティションキー (`player1Id`) とマルチ属性ソートキー (`matchDate` \$1 `round`) を持つグローバルセカンダリインデックス
+ **クエリの例:** 「プレイヤー 101 のすべての試合」または「2024 年 1 月の選手 101 の試合」

従来およびマルチ属性アプローチの主な違いは、項目構造を調べるときにはっきりします。

**従来のグローバルセカンダリインデックスのアプローチ (連結キー):**

```
// Manual concatenation required for GSI keys
const item = {
    matchId: 'match-001',                                          // Base table PK
    tournamentId: 'WINTER2024',
    region: 'NA-EAST',
    round: 'SEMIFINALS',
    bracket: 'UPPER',
    player1Id: '101',
    // Synthetic keys needed for GSI
    GSI_PK: `TOURNAMENT#${tournamentId}#REGION#${region}`,       // Must concatenate
    GSI_SK: `${round}#${bracket}#${matchId}`,                    // Must concatenate
    // ... other attributes
};
```

**マルチ属性のグローバルセカンダリインデックスのアプローチ (ネイティブキー):**

```
// Use existing attributes directly - no concatenation needed
const item = {
    matchId: 'match-001',                                          // Base table PK
    tournamentId: 'WINTER2024',
    region: 'NA-EAST',
    round: 'SEMIFINALS',
    bracket: 'UPPER',
    player1Id: '101',
    matchDate: '2024-01-18',
    // No synthetic keys needed - GSI uses existing attributes directly
    // ... other attributes
};
```

マルチ属性キーでは、自然ドメイン属性を使用して項目を 1 回書き込みます。DynamoDB は、合成連結キーを必要とせずに、複数の GSI 間で自動的にインデックスを作成します。

**ベーステーブルスキーマ:**
+ パーティションキー: `matchId` (1 属性)

**グローバルセカンダリインデックススキーマ (マルチ属性キーを持つ TournamentRegionIndex):**
+ パーティションキー: `tournamentId`、`region` (2 属性)
+ ソートキー: `round`、`bracket`、`matchId` (3 属性)

**グローバルセカンダリインデックススキーマ (マルチ属性キーを持つ PlayerMatchHistoryIndex):**
+ パーティションキー: `player1Id` (1 属性)
+ ソートキー: `matchDate`、`round` (2 属性)

### ベーステーブル: TournamentMatches
<a name="GSI.DesignPattern.MultiAttributeKeys.BaseTable"></a>


| matchId (PK) | tournamentId | リージョン | round | ブラケット | player1Id | player2Id | matchDate | 勝者 | score | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| match-001 | WINTER2024 | NA-EAST | FINALS | CHAMPIONSHIP | 101 | 103 | 2024-01-20 | 101 | 3-1 | 
| match-002 | WINTER2024 | NA-EAST | SEMIFINALS | UPPER | 101 | 105 | 2024-01-18 | 101 | 3-2 | 
| match-003 | WINTER2024 | NA-EAST | SEMIFINALS | UPPER | 103 | 107 | 2024-01-18 | 103 | 3-0 | 
| match-004 | WINTER2024 | NA-EAST | QUARTERFINALS | UPPER | 101 | 109 | 2024-01-15 | 101 | 3-1 | 
| match-005 | WINTER2024 | NA-WEST | FINALS | CHAMPIONSHIP | 102 | 104 | 2024-01-20 | 102 | 3-2 | 
| match-006 | WINTER2024 | NA-WEST | SEMIFINALS | UPPER | 102 | 106 | 2024-01-18 | 102 | 3-1 | 
| match-007 | SPRING2024 | NA-EAST | QUARTERFINALS | UPPER | 101 | 108 | 2024-03-15 | 101 | 3-0 | 
| match-008 | SPRING2024 | NA-EAST | QUARTERFINALS | LOWER | 103 | 110 | 2024-03-15 | 103 | 3-2 | 

### GSI: TournamentRegionIndex (マルチ属性キー)
<a name="GSI.DesignPattern.MultiAttributeKeys.TournamentRegionIndexTable"></a>


| tournamentId (PK) | リージョン (PK) | ラウンド (SK) | ブラケット (SK) | matchId (SK) | player1Id | player2Id | matchDate | 勝者 | score | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| WINTER2024 | NA-EAST | FINALS | CHAMPIONSHIP | match-001 | 101 | 103 | 2024-01-20 | 101 | 3-1 | 
| WINTER2024 | NA-EAST | QUARTERFINALS | UPPER | match-004 | 101 | 109 | 2024-01-15 | 101 | 3-1 | 
| WINTER2024 | NA-EAST | SEMIFINALS | UPPER | match-002 | 101 | 105 | 2024-01-18 | 101 | 3-2 | 
| WINTER2024 | NA-EAST | SEMIFINALS | UPPER | match-003 | 103 | 107 | 2024-01-18 | 103 | 3-0 | 
| WINTER2024 | NA-WEST | FINALS | CHAMPIONSHIP | match-005 | 102 | 104 | 2024-01-20 | 102 | 3-2 | 
| WINTER2024 | NA-WEST | SEMIFINALS | UPPER | match-006 | 102 | 106 | 2024-01-18 | 102 | 3-1 | 
| SPRING2024 | NA-EAST | QUARTERFINALS | LOWER | match-008 | 103 | 110 | 2024-03-15 | 103 | 3-2 | 
| SPRING2024 | NA-EAST | QUARTERFINALS | UPPER | match-007 | 101 | 108 | 2024-03-15 | 101 | 3-0 | 

### GSI: PlayerMatchHistoryIndex (マルチ属性キー)
<a name="GSI.DesignPattern.MultiAttributeKeys.PlayerMatchHistoryIndexTable"></a>


| player1Id (PK) | matchDate (SK) | ラウンド (SK) | tournamentId | リージョン | ブラケット | matchId | player2Id | 勝者 | score | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| 101 | 2024-01-15 | QUARTERFINALS | WINTER2024 | NA-EAST | UPPER | match-004 | 109 | 101 | 3-1 | 
| 101 | 2024-01-18 | SEMIFINALS | WINTER2024 | NA-EAST | UPPER | match-002 | 105 | 101 | 3-2 | 
| 101 | 2024-01-20 | FINALS | WINTER2024 | NA-EAST | CHAMPIONSHIP | match-001 | 103 | 101 | 3-1 | 
| 101 | 2024-03-15 | QUARTERFINALS | SPRING2024 | NA-EAST | UPPER | match-007 | 108 | 101 | 3-0 | 
| 102 | 2024-01-18 | SEMIFINALS | WINTER2024 | NA-WEST | UPPER | match-006 | 106 | 102 | 3-1 | 
| 102 | 2024-01-20 | FINALS | WINTER2024 | NA-WEST | CHAMPIONSHIP | match-005 | 104 | 102 | 3-2 | 
| 103 | 2024-01-18 | SEMIFINALS | WINTER2024 | NA-EAST | UPPER | match-003 | 107 | 103 | 3-0 | 
| 103 | 2024-03-15 | QUARTERFINALS | SPRING2024 | NA-EAST | LOWER | match-008 | 110 | 103 | 3-2 | 

## 前提条件
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites"></a>

開始する前に、以下を確認してください。

### アカウントとアクセス許可
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.AWSAccount"></a>
+ アクティブな AWS アカウント (必要に応じて[ここで作成](https://aws.amazon.com/free/))
+ DynamoDB オペレーションの IAM アクセス許可:
  + `dynamodb:CreateTable`
  + `dynamodb:DeleteTable`
  + `dynamodb:DescribeTable`
  + `dynamodb:PutItem`
  + `dynamodb:Query`
  + `dynamodb:BatchWriteItem`

**注記**  
**セキュリティ上の注意:** 本番稼働用には、必要なアクセス許可のみを持つカスタム IAM ポリシーを作成します。このチュートリアルでは、AWS 管理ポリシー `AmazonDynamoDBFullAccessV2` を使用できます。

### 開発環境
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.DevEnvironment"></a>
+ マシンにインストールされている「Node.js」
+ 次のいずれかの方法を使用して設定される AWS 認証情報

**オプション 1: AWS CLI**

```
aws configure
```

**オプション 2: 環境変数**

```
export AWS_ACCESS_KEY_ID=your_access_key_here
export AWS_SECRET_ACCESS_KEY=your_secret_key_here
export AWS_DEFAULT_REGION=us-east-1
```

### 必要なパッケージのインストール
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.InstallPackages"></a>

```
npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
```

## 実装
<a name="GSI.DesignPattern.MultiAttributeKeys.Implementation"></a>

### ステップ 1: マルチ属性キーを使用して GSI でテーブルを作成する
<a name="GSI.DesignPattern.MultiAttributeKeys.CreateTable"></a>

シンプルなベースキー構造と、マルチ属性キーを使用する GSI を持つテーブルを作成します。

#### コード例
<a name="w2aac19c13c45c23b9c11b3b5b1"></a>

```
import { DynamoDBClient, CreateTableCommand } from "@aws-sdk/client-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });

const response = await client.send(new CreateTableCommand({
    TableName: 'TournamentMatches',
    
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'matchId', KeyType: 'HASH' }              // Simple PK
    ],
    
    AttributeDefinitions: [
        { AttributeName: 'matchId', AttributeType: 'S' },
        { AttributeName: 'tournamentId', AttributeType: 'S' },
        { AttributeName: 'region', AttributeType: 'S' },
        { AttributeName: 'round', AttributeType: 'S' },
        { AttributeName: 'bracket', AttributeType: 'S' },
        { AttributeName: 'player1Id', AttributeType: 'S' },
        { AttributeName: 'matchDate', AttributeType: 'S' }
    ],
    
    // GSIs with multi-attribute keys
    GlobalSecondaryIndexes: [
        {
            IndexName: 'TournamentRegionIndex',
            KeySchema: [
                { AttributeName: 'tournamentId', KeyType: 'HASH' },    // GSI PK attribute 1
                { AttributeName: 'region', KeyType: 'HASH' },          // GSI PK attribute 2
                { AttributeName: 'round', KeyType: 'RANGE' },          // GSI SK attribute 1
                { AttributeName: 'bracket', KeyType: 'RANGE' },        // GSI SK attribute 2
                { AttributeName: 'matchId', KeyType: 'RANGE' }         // GSI SK attribute 3
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'PlayerMatchHistoryIndex',
            KeySchema: [
                { AttributeName: 'player1Id', KeyType: 'HASH' },       // GSI PK
                { AttributeName: 'matchDate', KeyType: 'RANGE' },      // GSI SK attribute 1
                { AttributeName: 'round', KeyType: 'RANGE' }           // GSI SK attribute 2
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    
    BillingMode: 'PAY_PER_REQUEST'
}));

console.log("Table with multi-attribute GSI keys created successfully");
```

**主要な設計上の決定事項:**

**ベーステーブル:** ベーステーブルは、直接一致検索にシンプルな `matchId` パーティションキーを使用し、GSI が複雑なクエリパターンを提供する間、ベーステーブル構造を分かりやすく維持します。

**TournamentRegionIndex グローバルセカンダリインデックス:** `TournamentRegionIndex` グローバルセカンダリインデックスは、`tournamentId` \$1 `region` をマルチ属性パーティションキーとして使用し、両方の属性を組み合わせたハッシュによってデータが分散されるトーナメントリージョンの分離を作成し、特定のトーナメントリージョンのコンテキスト内で効率的なクエリを可能にします。マルチ属性ソートキー (`round` \$1 `bracket` \$1 `matchId`) は、階層の任意のレベルでクエリをサポートする階層ソートを提供し、一般的な (ラウンド) から特定の (マッチ ID) へ自然な順序で並べ替えます。

**PlayerMatchHistoryIndex グローバルセカンダリインデックス:** `PlayerMatchHistoryIndex` グローバルセカンダリインデックスは、パーティションキーとして `player1Id` を使用して選手ごとにデータを再編成し、特定の選手のクロストーナメントクエリを有効にします。マルチ属性ソートキー (`matchDate` \$1 `round`) は、日付範囲または特定のトーナメントラウンドでフィルタリングできる時系列の順序を提供します。

### ステップ 2: ネイティブ属性を使用してデータを挿入する
<a name="GSI.DesignPattern.MultiAttributeKeys.InsertData"></a>

自然属性を使用してトーナメントマッチデータを追加します。GSI は、合成キーを必要とせずに、これらの属性を自動的にインデックス化します。

#### コード例
<a name="w2aac19c13c45c23b9c11b5b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Tournament match data - no synthetic keys needed for GSIs
const matches = [
    // Winter 2024 Tournament, NA-EAST region
    {
        matchId: 'match-001',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'FINALS',
        bracket: 'CHAMPIONSHIP',
        player1Id: '101',
        player2Id: '103',
        matchDate: '2024-01-20',
        winner: '101',
        score: '3-1'
    },
    {
        matchId: 'match-002',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'SEMIFINALS',
        bracket: 'UPPER',
        player1Id: '101',
        player2Id: '105',
        matchDate: '2024-01-18',
        winner: '101',
        score: '3-2'
    },
    {
        matchId: 'match-003',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'SEMIFINALS',
        bracket: 'UPPER',
        player1Id: '103',
        player2Id: '107',
        matchDate: '2024-01-18',
        winner: '103',
        score: '3-0'
    },
    {
        matchId: 'match-004',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'QUARTERFINALS',
        bracket: 'UPPER',
        player1Id: '101',
        player2Id: '109',
        matchDate: '2024-01-15',
        winner: '101',
        score: '3-1'
    },
    
    // Winter 2024 Tournament, NA-WEST region
    {
        matchId: 'match-005',
        tournamentId: 'WINTER2024',
        region: 'NA-WEST',
        round: 'FINALS',
        bracket: 'CHAMPIONSHIP',
        player1Id: '102',
        player2Id: '104',
        matchDate: '2024-01-20',
        winner: '102',
        score: '3-2'
    },
    {
        matchId: 'match-006',
        tournamentId: 'WINTER2024',
        region: 'NA-WEST',
        round: 'SEMIFINALS',
        bracket: 'UPPER',
        player1Id: '102',
        player2Id: '106',
        matchDate: '2024-01-18',
        winner: '102',
        score: '3-1'
    },
    
    // Spring 2024 Tournament, NA-EAST region
    {
        matchId: 'match-007',
        tournamentId: 'SPRING2024',
        region: 'NA-EAST',
        round: 'QUARTERFINALS',
        bracket: 'UPPER',
        player1Id: '101',
        player2Id: '108',
        matchDate: '2024-03-15',
        winner: '101',
        score: '3-0'
    },
    {
        matchId: 'match-008',
        tournamentId: 'SPRING2024',
        region: 'NA-EAST',
        round: 'QUARTERFINALS',
        bracket: 'LOWER',
        player1Id: '103',
        player2Id: '110',
        matchDate: '2024-03-15',
        winner: '103',
        score: '3-2'
    }
];

// Insert all matches
for (const match of matches) {
    await docClient.send(new PutCommand({
        TableName: 'TournamentMatches',
        Item: match
    }));
    
    console.log(`Added: ${match.matchId} - ${match.tournamentId}/${match.region} - ${match.round} ${match.bracket}`);
}

console.log(`\nInserted ${matches.length} tournament matches`);
console.log("No synthetic keys created - GSIs use native attributes automatically");
```

**データ構造の説明:**

**自然属性の使用:** 各属性は、文字列の連結や解析を必要とせずに実際のトーナメントの概念を表し、ドメインモデルへの直接マッピングを提供します。

**自動グローバルセカンダリインデックスの作成:** GSI、合成連結キーを必要とせずに、既存の属性 (TournamentRegionIndex `matchId`の場合は `tournamentId`、`region`、`round`、`bracket`、PlayerMatchHistoryIndex の場合は `player1Id`、`matchDate`、`round`) を使用して項目を自動的にインデックス作成します。

**バックフィルが不要:** マルチ属性キーを持つ新しいグローバルセカンダリインデックスを既存のテーブルに追加すると、DynamoDB は自然属性を使用して既存のすべての項目を自動的にインデックス化します。合成キーを使用して項目を更新する必要はありません。

### ステップ 3: すべてのパーティションキー属性を使用して TournamentRegionIndex グローバルセカンダリインデックスをクエリする
<a name="GSI.DesignPattern.MultiAttributeKeys.QueryAllPartitionKeys"></a>

この例では、マルチ属性パーティションキー (`tournamentId` \$1 `region`) を持つ TournamentRegionIndex グローバルセカンダリインデックスをクエリします。すべてのパーティションキー属性は、クエリで等号条件で指定する必要があります。`tournamentId` 単独でクエリを実行したり、パーティションキー属性で不等価演算子を使用したりすることはできません。

#### コード例
<a name="w2aac19c13c45c23b9c11b7b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query GSI: All matches for WINTER2024 tournament in NA-EAST region
const response = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region',
    ExpressionAttributeNames: {
        '#region': 'region',  // 'region' is a reserved keyword
        '#tournament': 'tournament'
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST'
    }
}));

console.log(`Found ${response.Items.length} matches for WINTER2024/NA-EAST:\n`);
response.Items.forEach(match => {
    console.log(`  ${match.round} | ${match.bracket} | ${match.matchId}`);
    console.log(`    Players: ${match.player1Id} vs ${match.player2Id}`);
    console.log(`    Winner: ${match.winner}, Score: ${match.score}\n`);
});
```

**正常な出力:**

```
Found 4 matches for WINTER2024/NA-EAST:

  FINALS | CHAMPIONSHIP | match-001
    Players: 101 vs 103
    Winner: 101, Score: 3-1

  QUARTERFINALS | UPPER | match-004
    Players: 101 vs 109
    Winner: 101, Score: 3-1

  SEMIFINALS | UPPER | match-002
    Players: 101 vs 105
    Winner: 101, Score: 3-2

  SEMIFINALS | UPPER | match-003
    Players: 103 vs 107
    Winner: 103, Score: 3-0
```

**無効なクエリ:**

```
// Missing region attribute
KeyConditionExpression: 'tournamentId = :tournament'

// Using inequality on partition key attribute
KeyConditionExpression: 'tournamentId = :tournament AND #region > :region'
```

**パフォーマンス:** マルチ属性パーティションキーは共にハッシュされ、単一属性キーと同じ O(1) 検索パフォーマンスを提供します。

### ステップ 4: グローバルセカンダリインデックスのソートキー left-to-right をクエリする
<a name="GSI.DesignPattern.MultiAttributeKeys.QuerySortKeysLeftToRight"></a>

ソートキー属性は、グローバルセカンダリインデックスで定義されている順序で左から右にクエリする必要があります。この例では、TournamentRegionIndex をさまざまな階層レベルでクエリする方法を示します。フィルタリングは、`round` のみ、`round` \$1 `bracket`、または 3 つのソートキー属性すべてで行います。途中で属性をスキップすることはできません。例えば、`bracket` のスキップ中に `round` や `matchId` でクエリを実行することはできません。

#### コード例
<a name="w2aac19c13c45c23b9c11b9b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query 1: Filter by first sort key attribute (round)
console.log("Query 1: All SEMIFINALS matches");
const query1 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS'
    }
}));
console.log(`  Found ${query1.Items.length} matches\n`);

// Query 2: Filter by first two sort key attributes (round + bracket)
console.log("Query 2: SEMIFINALS UPPER bracket matches");
const query2 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round AND bracket = :bracket',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS',
        ':bracket': 'UPPER'
    }
}));
console.log(`  Found ${query2.Items.length} matches\n`);

// Query 3: Filter by all three sort key attributes (round + bracket + matchId)
console.log("Query 3: Specific match in SEMIFINALS UPPER bracket");
const query3 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round AND bracket = :bracket AND matchId = :matchId',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS',
        ':bracket': 'UPPER',
        ':matchId': 'match-002'
    }
}));
console.log(`  Found ${query3.Items.length} matches\n`);

// Query 4: INVALID - skipping round
console.log("Query 4: Attempting to skip first sort key attribute (WILL FAIL)");
try {
    const query4 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'TournamentRegionIndex',
        KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND bracket = :bracket',
        ExpressionAttributeNames: {
            '#region': 'region'  // 'region' is a reserved keyword
        },
        ExpressionAttributeValues: {
            ':tournament': 'WINTER2024',
            ':region': 'NA-EAST',
            ':bracket': 'UPPER'
        }
    }));
} catch (error) {
    console.log(`  Error: ${error.message}`);
    console.log(`  Cannot skip sort key attributes - must query left-to-right\n`);
}
```

**正常な出力:**

```
Query 1: All SEMIFINALS matches
  Found 2 matches

Query 2: SEMIFINALS UPPER bracket matches
  Found 2 matches

Query 3: Specific match in SEMIFINALS UPPER bracket
  Found 1 matches

Query 4: Attempting to skip first sort key attribute (WILL FAIL)
  Error: Query key condition not supported
  Cannot skip sort key attributes - must query left-to-right
```

**Left-to-right クエリルール:** なにもスキップせずに左から右の順にクエリする必要があります。

**有効なパターン:**
+ 最初の属性のみ: `round = 'SEMIFINALS'`
+ 最初の 2 つの属性: `round = 'SEMIFINALS' AND bracket = 'UPPER'`
+ 3 つの属性すべて: `round = 'SEMIFINALS' AND bracket = 'UPPER' AND matchId = 'match-002'`

**無効なパターン:**
+ 最初の属性をスキップする: `bracket = 'UPPER'` (ラウンドをスキップ)
+ 順不同のクエリ: `matchId = 'match-002' AND round = 'SEMIFINALS'`
+ ギャップを残す: `round = 'SEMIFINALS' AND matchId = 'match-002'` (ブラケットをスキップ)

**注記**  
**設計のヒント:** クエリの柔軟性を最大化するために、ソートキー属性を最も一般的なものから最も具体的な順に並べます。

### ステップ 5: グローバルセカンダリインデックスのソートキーで不等価条件を使用する
<a name="GSI.DesignPattern.MultiAttributeKeys.InequalityConditions"></a>

不等価条件は、クエリの最後の条件である必要があります。この例では、ソートキー属性で比較演算子 (`>=`、`BETWEEN`) とプレフィックスマッチング (`begins_with()`) を使用する方法を示します。不等価演算子を使用すると、その後にソートキー条件を追加することはできません。不等価はキー条件式の最終条件である必要があります。

#### コード例
<a name="w2aac19c13c45c23b9c11c11b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query 1: Round comparison (inequality on first sort key attribute)
console.log("Query 1: Matches from QUARTERFINALS onwards");
const query1 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round >= :round',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'QUARTERFINALS'
    }
}));
console.log(`  Found ${query1.Items.length} matches\n`);

// Query 2: Round range with BETWEEN
console.log("Query 2: Matches between QUARTERFINALS and SEMIFINALS");
const query2 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round BETWEEN :start AND :end',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':start': 'QUARTERFINALS',
        ':end': 'SEMIFINALS'
    }
}));
console.log(`  Found ${query2.Items.length} matches\n`);

// Query 3: Prefix matching with begins_with (treated as inequality)
console.log("Query 3: Matches in brackets starting with 'U'");
const query3 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round AND begins_with(bracket, :prefix)',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS',
        ':prefix': 'U'
    }
}));
console.log(`  Found ${query3.Items.length} matches\n`);

// Query 4: INVALID - condition after inequality
console.log("Query 4: Attempting condition after inequality (WILL FAIL)");
try {
    const query4 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'TournamentRegionIndex',
        KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round > :round AND bracket = :bracket',
        ExpressionAttributeNames: {
            '#region': 'region'  // 'region' is a reserved keyword
        },
        ExpressionAttributeValues: {
            ':tournament': 'WINTER2024',
            ':region': 'NA-EAST',
            ':round': 'QUARTERFINALS',
            ':bracket': 'UPPER'
        }
    }));
} catch (error) {
    console.log(`  Error: ${error.message}`);
    console.log(`  Cannot add conditions after inequality - it must be last\n`);
}
```

**不等価演算子ルール:** 比較演算子 (`>`、`>=`、`<`、`<=`) を使用できます。`BETWEEN` は範囲クエリに、`begins_with()` はプレフィックスマッチングに使用できます。不等価は、クエリの最後の条件である必要があります。

**有効なパターン:**
+ 等価条件の後に不等価が続きます: `round = 'SEMIFINALS' AND bracket = 'UPPER' AND matchId > 'match-001'`
+ 最初の属性の不等価: `round BETWEEN 'QUARTERFINALS' AND 'SEMIFINALS'`
+ 最終条件としてのプレフィックスマッチング: `round = 'SEMIFINALS' AND begins_with(bracket, 'U')`

**無効なパターン:**
+ 不等価の後に条件を追加する: `round > 'QUARTERFINALS' AND bracket = 'UPPER'`
+ 複数の不等価の使用: `round > 'QUARTERFINALS' AND bracket > 'L'`

**重要**  
`begins_with()` は不等価条件として扱われるため、追加のソートキー条件をフォローすることはできません。

### ステップ 6: マルチ属性ソートキーを使用して PlayerMatchHistoryIndex グローバルセカンダリインデックスをクエリする
<a name="GSI.DesignPattern.MultiAttributeKeys.QueryPlayerHistory"></a>

この例では、単一のパーティションキー (`player1Id`) とマルチ属性ソートキー (`matchDate` \$1 `round`) を持つ PlayerMatchHistoryIndex をクエリします。これにより、トーナメント ID を知らずに特定の選手のすべてのマッチングをクエリすることで、クロストーナメント分析が可能になります。ベーステーブルでは、トーナメントとリージョンの組み合わせごとに個別のクエリが必要になります。

#### コード例
<a name="w2aac19c13c45c23b9c11c13b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query 1: All matches for Player 101 across all tournaments
console.log("Query 1: All matches for Player 101");
const query1 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player',
    ExpressionAttributeValues: {
        ':player': '101'
    }
}));

console.log(`  Found ${query1.Items.length} matches for Player 101:`);
query1.Items.forEach(match => {
    console.log(`    ${match.tournamentId}/${match.region} - ${match.matchDate} - ${match.round}`);
});
console.log();

// Query 2: Player 101 matches on specific date
console.log("Query 2: Player 101 matches on 2024-01-18");
const query2 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player AND matchDate = :date',
    ExpressionAttributeValues: {
        ':player': '101',
        ':date': '2024-01-18'
    }
}));

console.log(`  Found ${query2.Items.length} matches\n`);

// Query 3: Player 101 SEMIFINALS matches on specific date
console.log("Query 3: Player 101 SEMIFINALS matches on 2024-01-18");
const query3 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player AND matchDate = :date AND round = :round',
    ExpressionAttributeValues: {
        ':player': '101',
        ':date': '2024-01-18',
        ':round': 'SEMIFINALS'
    }
}));

console.log(`  Found ${query3.Items.length} matches\n`);

// Query 4: Player 101 matches in date range
console.log("Query 4: Player 101 matches in January 2024");
const query4 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player AND matchDate BETWEEN :start AND :end',
    ExpressionAttributeValues: {
        ':player': '101',
        ':start': '2024-01-01',
        ':end': '2024-01-31'
    }
}));

console.log(`  Found ${query4.Items.length} matches\n`);
```

## パターンのバリエーション
<a name="GSI.DesignPattern.MultiAttributeKeys.PatternVariations"></a>

### マルチ属性キーを使用した時系列データ
<a name="GSI.DesignPattern.MultiAttributeKeys.TimeSeries"></a>

階層時間属性を使用して時系列クエリを最適化する

#### コード例
<a name="w2aac19c13c45c23b9c13b3b5b1"></a>

```
{
    TableName: 'IoTReadings',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'readingId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'readingId', AttributeType: 'S' },
        { AttributeName: 'deviceId', AttributeType: 'S' },
        { AttributeName: 'locationId', AttributeType: 'S' },
        { AttributeName: 'year', AttributeType: 'S' },
        { AttributeName: 'month', AttributeType: 'S' },
        { AttributeName: 'day', AttributeType: 'S' },
        { AttributeName: 'timestamp', AttributeType: 'S' }
    ],
    // GSI with multi-attribute keys for time-series queries
    GlobalSecondaryIndexes: [{
        IndexName: 'DeviceLocationTimeIndex',
        KeySchema: [
            { AttributeName: 'deviceId', KeyType: 'HASH' },
            { AttributeName: 'locationId', KeyType: 'HASH' },
            { AttributeName: 'year', KeyType: 'RANGE' },
            { AttributeName: 'month', KeyType: 'RANGE' },
            { AttributeName: 'day', KeyType: 'RANGE' },
            { AttributeName: 'timestamp', KeyType: 'RANGE' }
        ],
        Projection: { ProjectionType: 'ALL' }
    }],
    BillingMode: 'PAY_PER_REQUEST'
}

// Query patterns enabled via GSI:
// - All readings for device in location
// - Readings for specific year
// - Readings for specific month in year
// - Readings for specific day
// - Readings in time range
```

**メリット:** 自然時間階層 (年 → 月 → 日 → タイムスタンプ) を使用すると、日付の解析や操作を行わずに、いつでも効率的にきめ細やかなクエリを実行できます。グローバルセカンダリインデックスは、自然時間属性を使用してすべての読み取り値を自動的にインデックス化します。

### マルチ属性キーを使用した e コマース注文
<a name="GSI.DesignPattern.MultiAttributeKeys.ECommerce"></a>

複数のディメンションを持つ注文を追跡する

#### コード例
<a name="w2aac19c13c45c23b9c13b5b5b1"></a>

```
{
    TableName: 'Orders',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'orderId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'orderId', AttributeType: 'S' },
        { AttributeName: 'sellerId', AttributeType: 'S' },
        { AttributeName: 'region', AttributeType: 'S' },
        { AttributeName: 'orderDate', AttributeType: 'S' },
        { AttributeName: 'category', AttributeType: 'S' },
        { AttributeName: 'customerId', AttributeType: 'S' },
        { AttributeName: 'orderStatus', AttributeType: 'S' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'SellerRegionIndex',
            KeySchema: [
                { AttributeName: 'sellerId', KeyType: 'HASH' },
                { AttributeName: 'region', KeyType: 'HASH' },
                { AttributeName: 'orderDate', KeyType: 'RANGE' },
                { AttributeName: 'category', KeyType: 'RANGE' },
                { AttributeName: 'orderId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'CustomerOrdersIndex',
            KeySchema: [
                { AttributeName: 'customerId', KeyType: 'HASH' },
                { AttributeName: 'orderDate', KeyType: 'RANGE' },
                { AttributeName: 'orderStatus', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// SellerRegionIndex GSI queries:
// - Orders by seller and region
// - Orders by seller, region, and date
// - Orders by seller, region, date, and category

// CustomerOrdersIndex GSI queries:
// - Customer's orders
// - Customer's orders by date
// - Customer's orders by date and status
```

### 階層組織データ
<a name="GSI.DesignPattern.MultiAttributeKeys.Hierarchical"></a>

モデル組織階層

#### コード例
<a name="w2aac19c13c45c23b9c13b7b5b1"></a>

```
{
    TableName: 'Employees',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'employeeId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'employeeId', AttributeType: 'S' },
        { AttributeName: 'companyId', AttributeType: 'S' },
        { AttributeName: 'divisionId', AttributeType: 'S' },
        { AttributeName: 'departmentId', AttributeType: 'S' },
        { AttributeName: 'teamId', AttributeType: 'S' },
        { AttributeName: 'skillCategory', AttributeType: 'S' },
        { AttributeName: 'skillLevel', AttributeType: 'S' },
        { AttributeName: 'yearsExperience', AttributeType: 'N' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'OrganizationIndex',
            KeySchema: [
                { AttributeName: 'companyId', KeyType: 'HASH' },
                { AttributeName: 'divisionId', KeyType: 'HASH' },
                { AttributeName: 'departmentId', KeyType: 'RANGE' },
                { AttributeName: 'teamId', KeyType: 'RANGE' },
                { AttributeName: 'employeeId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'SkillsIndex',
            KeySchema: [
                { AttributeName: 'skillCategory', KeyType: 'HASH' },
                { AttributeName: 'skillLevel', KeyType: 'RANGE' },
                { AttributeName: 'yearsExperience', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'INCLUDE', NonKeyAttributes: ['employeeId', 'name'] }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// OrganizationIndex GSI query patterns:
// - All employees in company/division
// - Employees in specific department
// - Employees in specific team

// SkillsIndex GSI query patterns:
// - Employees by skill and experience level
```

### スパースマルチ属性キー
<a name="GSI.DesignPattern.MultiAttributeKeys.Sparse"></a>

マルチ属性キーを組み合わせてスパース GSI を作成する

#### コード例
<a name="w2aac19c13c45c23b9c13b9b5b1"></a>

```
{
    TableName: 'Products',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'productId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'productId', AttributeType: 'S' },
        { AttributeName: 'categoryId', AttributeType: 'S' },
        { AttributeName: 'subcategoryId', AttributeType: 'S' },
        { AttributeName: 'averageRating', AttributeType: 'N' },
        { AttributeName: 'reviewCount', AttributeType: 'N' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'CategoryIndex',
            KeySchema: [
                { AttributeName: 'categoryId', KeyType: 'HASH' },
                { AttributeName: 'subcategoryId', KeyType: 'HASH' },
                { AttributeName: 'productId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'ReviewedProductsIndex',
            KeySchema: [
                { AttributeName: 'categoryId', KeyType: 'HASH' },
                { AttributeName: 'averageRating', KeyType: 'RANGE' },  // Optional attribute
                { AttributeName: 'reviewCount', KeyType: 'RANGE' }     // Optional attribute
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// Only products with reviews appear in ReviewedProductsIndex GSI
// Automatic filtering without application logic
// Multi-attribute sort key enables rating and count queries
```

### SaaS とマルチテナンシー
<a name="GSI.DesignPattern.MultiAttributeKeys.SaaS"></a>

顧客分離を備えたマルチテナント SaaS プラットフォーム

#### コード例
<a name="w2aac19c13c45c23b9c13c11b5b1"></a>

```
// Table design
{
    TableName: 'SaasData',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'resourceId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'resourceId', AttributeType: 'S' },
        { AttributeName: 'tenantId', AttributeType: 'S' },
        { AttributeName: 'customerId', AttributeType: 'S' },
        { AttributeName: 'resourceType', AttributeType: 'S' }
    ],
    // GSI with multi-attribute keys for tenant-customer isolation
    GlobalSecondaryIndexes: [{
        IndexName: 'TenantCustomerIndex',
        KeySchema: [
            { AttributeName: 'tenantId', KeyType: 'HASH' },
            { AttributeName: 'customerId', KeyType: 'HASH' },
            { AttributeName: 'resourceType', KeyType: 'RANGE' },
            { AttributeName: 'resourceId', KeyType: 'RANGE' }
        ],
        Projection: { ProjectionType: 'ALL' }
    }],
    BillingMode: 'PAY_PER_REQUEST'
}

// Query GSI: All resources for tenant T001, customer C001
const resources = await docClient.send(new QueryCommand({
    TableName: 'SaasData',
    IndexName: 'TenantCustomerIndex',
    KeyConditionExpression: 'tenantId = :tenant AND customerId = :customer',
    ExpressionAttributeValues: {
        ':tenant': 'T001',
        ':customer': 'C001'
    }
}));

// Query GSI: Specific resource type for tenant/customer
const documents = await docClient.send(new QueryCommand({
    TableName: 'SaasData',
    IndexName: 'TenantCustomerIndex',
    KeyConditionExpression: 'tenantId = :tenant AND customerId = :customer AND resourceType = :type',
    ExpressionAttributeValues: {
        ':tenant': 'T001',
        ':customer': 'C001',
        ':type': 'document'
    }
}));
```

**メリット:** テナントと顧客のコンテキストと自然データ組織内の効率的なクエリ。

### 金融取引
<a name="GSI.DesignPattern.MultiAttributeKeys.Financial"></a>

GSI を使用した銀行システム追跡アカウントトランザクション

#### コード例
<a name="w2aac19c13c45c23b9c13c13b5b1"></a>

```
// Table design
{
    TableName: 'BankTransactions',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'transactionId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'transactionId', AttributeType: 'S' },
        { AttributeName: 'accountId', AttributeType: 'S' },
        { AttributeName: 'year', AttributeType: 'S' },
        { AttributeName: 'month', AttributeType: 'S' },
        { AttributeName: 'day', AttributeType: 'S' },
        { AttributeName: 'transactionType', AttributeType: 'S' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'AccountTimeIndex',
            KeySchema: [
                { AttributeName: 'accountId', KeyType: 'HASH' },
                { AttributeName: 'year', KeyType: 'RANGE' },
                { AttributeName: 'month', KeyType: 'RANGE' },
                { AttributeName: 'day', KeyType: 'RANGE' },
                { AttributeName: 'transactionId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'TransactionTypeIndex',
            KeySchema: [
                { AttributeName: 'accountId', KeyType: 'HASH' },
                { AttributeName: 'transactionType', KeyType: 'RANGE' },
                { AttributeName: 'year', KeyType: 'RANGE' },
                { AttributeName: 'month', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// Query AccountTimeIndex GSI: All transactions for account in 2023
const yearTransactions = await docClient.send(new QueryCommand({
    TableName: 'BankTransactions',
    IndexName: 'AccountTimeIndex',
    KeyConditionExpression: 'accountId = :account AND #year = :year',
    ExpressionAttributeNames: { '#year': 'year' },
    ExpressionAttributeValues: {
        ':account': 'ACC-12345',
        ':year': '2023'
    }
}));

// Query AccountTimeIndex GSI: Transactions in specific month
const monthTransactions = await docClient.send(new QueryCommand({
    TableName: 'BankTransactions',
    IndexName: 'AccountTimeIndex',
    KeyConditionExpression: 'accountId = :account AND #year = :year AND #month = :month',
    ExpressionAttributeNames: { '#year': 'year', '#month': 'month' },
    ExpressionAttributeValues: {
        ':account': 'ACC-12345',
        ':year': '2023',
        ':month': '11'
    }
}));

// Query TransactionTypeIndex GSI: Deposits in 2023
const deposits = await docClient.send(new QueryCommand({
    TableName: 'BankTransactions',
    IndexName: 'TransactionTypeIndex',
    KeyConditionExpression: 'accountId = :account AND transactionType = :type AND #year = :year',
    ExpressionAttributeNames: { '#year': 'year' },
    ExpressionAttributeValues: {
        ':account': 'ACC-12345',
        ':type': 'deposit',
        ':year': '2023'
    }
}));
```

## 完全な例
<a name="GSI.DesignPattern.MultiAttributeKeys.CompleteExample"></a>

次の例は、セットアップからクリーンアップまでのマルチ属性キーを示しています。

### コード例
<a name="w2aac19c13c45c23b9c15b5b1"></a>

```
import { 
    DynamoDBClient, 
    CreateTableCommand, 
    DeleteTableCommand, 
    waitUntilTableExists 
} from "@aws-sdk/client-dynamodb";
import { 
    DynamoDBDocumentClient, 
    PutCommand, 
    QueryCommand 
} from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

async function multiAttributeKeysDemo() {
    console.log("Starting Multi-Attribute GSI Keys Demo\n");
    
    // Step 1: Create table with GSIs using multi-attribute keys
    console.log("1. Creating table with multi-attribute GSI keys...");
    await client.send(new CreateTableCommand({
        TableName: 'TournamentMatches',
        KeySchema: [
            { AttributeName: 'matchId', KeyType: 'HASH' }
        ],
        AttributeDefinitions: [
            { AttributeName: 'matchId', AttributeType: 'S' },
            { AttributeName: 'tournamentId', AttributeType: 'S' },
            { AttributeName: 'region', AttributeType: 'S' },
            { AttributeName: 'round', AttributeType: 'S' },
            { AttributeName: 'bracket', AttributeType: 'S' },
            { AttributeName: 'player1Id', AttributeType: 'S' },
            { AttributeName: 'matchDate', AttributeType: 'S' }
        ],
        GlobalSecondaryIndexes: [
            {
                IndexName: 'TournamentRegionIndex',
                KeySchema: [
                    { AttributeName: 'tournamentId', KeyType: 'HASH' },
                    { AttributeName: 'region', KeyType: 'HASH' },
                    { AttributeName: 'round', KeyType: 'RANGE' },
                    { AttributeName: 'bracket', KeyType: 'RANGE' },
                    { AttributeName: 'matchId', KeyType: 'RANGE' }
                ],
                Projection: { ProjectionType: 'ALL' }
            },
            {
                IndexName: 'PlayerMatchHistoryIndex',
                KeySchema: [
                    { AttributeName: 'player1Id', KeyType: 'HASH' },
                    { AttributeName: 'matchDate', KeyType: 'RANGE' },
                    { AttributeName: 'round', KeyType: 'RANGE' }
                ],
                Projection: { ProjectionType: 'ALL' }
            }
        ],
        BillingMode: 'PAY_PER_REQUEST'
    }));
    
    await waitUntilTableExists({ client, maxWaitTime: 120 }, { TableName: 'TournamentMatches' });
    console.log("Table created\n");
    
    // Step 2: Insert tournament matches
    console.log("2. Inserting tournament matches...");
    const matches = [
        { matchId: 'match-001', tournamentId: 'WINTER2024', region: 'NA-EAST', round: 'FINALS', bracket: 'CHAMPIONSHIP', player1Id: '101', player2Id: '103', matchDate: '2024-01-20', winner: '101', score: '3-1' },
        { matchId: 'match-002', tournamentId: 'WINTER2024', region: 'NA-EAST', round: 'SEMIFINALS', bracket: 'UPPER', player1Id: '101', player2Id: '105', matchDate: '2024-01-18', winner: '101', score: '3-2' },
        { matchId: 'match-003', tournamentId: 'WINTER2024', region: 'NA-WEST', round: 'FINALS', bracket: 'CHAMPIONSHIP', player1Id: '102', player2Id: '104', matchDate: '2024-01-20', winner: '102', score: '3-2' },
        { matchId: 'match-004', tournamentId: 'SPRING2024', region: 'NA-EAST', round: 'QUARTERFINALS', bracket: 'UPPER', player1Id: '101', player2Id: '108', matchDate: '2024-03-15', winner: '101', score: '3-0' }
    ];
    
    for (const match of matches) {
        await docClient.send(new PutCommand({ TableName: 'TournamentMatches', Item: match }));
    }
    console.log(`Inserted ${matches.length} tournament matches\n`);
    
    // Step 3: Query GSI with multi-attribute partition key
    console.log("3. Query TournamentRegionIndex GSI: WINTER2024/NA-EAST matches");
    const gsiQuery1 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'TournamentRegionIndex',
        KeyConditionExpression: 'tournamentId = :tournament AND #region = :region',
        ExpressionAttributeNames: { '#region': 'region' },
        ExpressionAttributeValues: { ':tournament': 'WINTER2024', ':region': 'NA-EAST' }
    }));
    
    console.log(`  Found ${gsiQuery1.Items.length} matches:`);
    gsiQuery1.Items.forEach(match => {
        console.log(`    ${match.round} - ${match.bracket} - ${match.winner} won`);
    });
    
    // Step 4: Query GSI with multi-attribute sort key
    console.log("\n4. Query PlayerMatchHistoryIndex GSI: All matches for Player 101");
    const gsiQuery2 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'PlayerMatchHistoryIndex',
        KeyConditionExpression: 'player1Id = :player',
        ExpressionAttributeValues: { ':player': '101' }
    }));
    
    console.log(`  Found ${gsiQuery2.Items.length} matches for Player 101:`);
    gsiQuery2.Items.forEach(match => {
        console.log(`    ${match.tournamentId}/${match.region} - ${match.matchDate} - ${match.round}`);
    });
    
    console.log("\nDemo complete");
    console.log("No synthetic keys needed - GSIs use native attributes automatically");
}

async function cleanup() {
    console.log("Deleting table...");
    await client.send(new DeleteTableCommand({ TableName: 'TournamentMatches' }));
    console.log("Table deleted");
}

// Run demo
multiAttributeKeysDemo().catch(console.error);

// Uncomment to cleanup:
// cleanup().catch(console.error);
```

**最小コードの雛形**

### コード例
<a name="w2aac19c13c45c23b9c15b9b1"></a>

```
// 1. Create table with GSI using multi-attribute keys
await client.send(new CreateTableCommand({
    TableName: 'MyTable',
    KeySchema: [
        { AttributeName: 'id', KeyType: 'HASH' }        // Simple base table PK
    ],
    AttributeDefinitions: [
        { AttributeName: 'id', AttributeType: 'S' },
        { AttributeName: 'attr1', AttributeType: 'S' },
        { AttributeName: 'attr2', AttributeType: 'S' },
        { AttributeName: 'attr3', AttributeType: 'S' },
        { AttributeName: 'attr4', AttributeType: 'S' }
    ],
    GlobalSecondaryIndexes: [{
        IndexName: 'MyGSI',
        KeySchema: [
            { AttributeName: 'attr1', KeyType: 'HASH' },    // GSI PK attribute 1
            { AttributeName: 'attr2', KeyType: 'HASH' },    // GSI PK attribute 2
            { AttributeName: 'attr3', KeyType: 'RANGE' },   // GSI SK attribute 1
            { AttributeName: 'attr4', KeyType: 'RANGE' }    // GSI SK attribute 2
        ],
        Projection: { ProjectionType: 'ALL' }
    }],
    BillingMode: 'PAY_PER_REQUEST'
}));

// 2. Insert items with native attributes (no concatenation needed for GSI)
await docClient.send(new PutCommand({
    TableName: 'MyTable',
    Item: {
        id: 'item-001',
        attr1: 'value1',
        attr2: 'value2',
        attr3: 'value3',
        attr4: 'value4',
        // ... other attributes
    }
}));

// 3. Query GSI with all partition key attributes
await docClient.send(new QueryCommand({
    TableName: 'MyTable',
    IndexName: 'MyGSI',
    KeyConditionExpression: 'attr1 = :v1 AND attr2 = :v2',
    ExpressionAttributeValues: {
        ':v1': 'value1',
        ':v2': 'value2'
    }
}));

// 4. Query GSI with sort key attributes (left-to-right)
await docClient.send(new QueryCommand({
    TableName: 'MyTable',
    IndexName: 'MyGSI',
    KeyConditionExpression: 'attr1 = :v1 AND attr2 = :v2 AND attr3 = :v3',
    ExpressionAttributeValues: {
        ':v1': 'value1',
        ':v2': 'value2',
        ':v3': 'value3'
    }
}));

// Note: If any attribute name is a DynamoDB reserved keyword, use ExpressionAttributeNames:
// KeyConditionExpression: 'attr1 = :v1 AND #attr2 = :v2'
// ExpressionAttributeNames: { '#attr2': 'attr2' }
```

## その他のリソース
<a name="GSI.DesignPattern.MultiAttributeKeys.AdditionalResources"></a>
+ [DynamoDB のベストプラクティス](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/best-practices.html)
+ [テーブルとデータの操作](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.html)
+ [グローバルセカンダリインデックス](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html): 
+ [クエリおよびスキャンオペレーション](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html)

# DynamoDB のグローバルセカンダリインデックスの管理
<a name="GSI.OnlineOps"></a>

このセクションでは、Amazon DynamoDB でグローバルセカンダリインデックスを作成、変更、削除する方法について説明します。

**Topics**
+ [グローバルセカンダリインデックスを持つテーブルの作成](#GSI.Creating)
+ [テーブルのグローバルセカンダリインデックスの説明](#GSI.Describing)
+ [グローバルセカンダリインデックスの既存テーブルへの追加](#GSI.OnlineOps.Creating)
+ [グローバルセカンダリインデックスの削除](#GSI.OnlineOps.Deleting)
+ [作成時のグローバルセカンダリインデックスの変更](#GSI.OnlineOps.Creating.Modify)

## グローバルセカンダリインデックスを持つテーブルの作成
<a name="GSI.Creating"></a>

1 つ以上のグローバルセカンダリインデックスを持つテーブルを作成するには、`CreateTable` オペレーションと `GlobalSecondaryIndexes` パラメータを使用します。最大限のクエリの柔軟性を得るために、テーブルごとに最大 20 個のグローバルセカンダリインデックス (デフォルトのクォータ) を作成できます。

インデックスパーティションキーとして機能する属性を 1 つ指定する必要があります。必要に応じて、インデックスソートキーに別の属性を指定できます。これらのキー属性のいずれも、テーブルのキー属性と同じである必要はありません。例えば、*GameScores* テーブル (「[DynamoDB のグローバルセカンダリインデックスの使用](GSI.md)」参照) では、`TopScore` も `TopScoreDateTime` もキー属性ではありません。パーティションキーとして `TopScore`、ソートキーとして `TopScoreDateTime` を使用して、グローバルセカンダリインデックスを作成できます。このようなインデックスを使用して、ハイスコアとゲームのプレイ時刻との間に相関があるかどうかを判定できる可能性があります。

各インデックスキー属性は、`String`、`Number`、または `Binary` タイプのスカラーである必要があります。(ドキュメントまたはセットは指定できません)。グローバルセカンダリインデックスには、任意のデータ型の属性を射影できます。これには、スカラー、ドキュメント、およびセットが含まれます。データ型の詳細なリストについては、「[データ型](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes)」を参照してください。

プロビジョニング済みモードを使用する場合は、インデックスに `ReadCapacityUnits` および `WriteCapacityUnits` で構成される `ProvisionedThroughput` 設定をする必要があります。これらのプロビジョニングされたスループット設定は、テーブルの設定とは別ですが、同様の動作をします。詳細については、「」を参照してください[グローバルセカンダリインデックスに対するプロビジョニングされたスループットに関する考慮事項](GSI.md#GSI.ThroughputConsiderations)

 グローバルセカンダリーインデックスは、基本テーブルから読み込み/書き込みキャパシティーモードを継承します。詳細については、「[DynamoDB でキャパシティモードを切り替える際の考慮事項](bp-switching-capacity-modes.md)」を参照してください。

**注記**  
 新しい GSI を作成する際、選択したパーティションキーが、新しいインデックスのパーティションキー値全体でデータやトラフィックの分散が不均一だったり、狭くなっているかを確認することが重要になります。この場合、バックフィル操作と書き込み操作が同時に発生し、ベーステーブルへの書き込みをスロットリングしている可能性があります。このサービスでは、このシナリオの可能性を最小限に抑えるための対策を講じていますが、インデックスパーティションキー、選択した射影、またはインデックスプライマリキーのスパース性について、カスタマーデータのインサイトを見える形で取得していません。  
新しいグローバルセカンダリインデックスで、パーティションキー値全体でデータやトラフィックの分散が狭められたり、歪められたりしている可能性がある場合は、運用上重要なテーブルに新しいインデックスを追加する前に、次の点を考慮してください。  
アプリケーションのトラフィック量が最も少ない時間帯にインデックスを追加するのが、最も安全な方法と言えます。
ベーステーブルとインデックスで CloudWatch Contributor Insights 有効にすることを検討してください。これにより、トラフィックの分散に関する貴重なインサイトが得られます。
 プロセス全体を通して、CloudWatch メトリクス `WriteThrottleEvents`、`ThrottledRequests`、および `OnlineIndexPercentageProgress` を表示します。必要に応じて、プロビジョニングされた書き込みキャパシティを調整し、進行中の操作に対してスロットリングによる大きな影響を与えることなく、適切な時間内にバックフィルを完了します。`OnlineIndexConsumedWriteCapacity` と `OnlineThrottleEvents` は、インデックスバックフィル中は 0 と表示されると予想されます。
書き込みスロットリングによる運用上の影響が発生した場合は、インデックスの作成をキャンセルする準備をしてください。

## テーブルのグローバルセカンダリインデックスの説明
<a name="GSI.Describing"></a>

テーブルのすべてのグローバルセカンダリインデックスのステータスを表示するには、`DescribeTable` オペレーションを使用します。レスポンスの `GlobalSecondaryIndexes` 部分は、テーブル上のすべてのインデックスと、それぞれの現在のステータス (`IndexStatus`) を示しています。

グローバルセカンダリインデックスの `IndexStatus` は、次のいずれかになります。
+ `CREATING` – インデックスは現在作成されていますが、まだ使用することはできません。
+ `ACTIVE` – インデックスは使用可能になり、アプリケーションはインデックスに対して `Query` オペレーションを実行できます。
+ `UPDATING` – インデックスのプロビジョニングされたスループット設定の変更中です。
+ `DELETING` – インデックスは現在削除されているため、使用できなくなっています。

DynamoDB がグローバルセカンダリインデックスの作成を完了すると、インデックスのステータスが `CREATING` から `ACTIVE` に変わります。

## グローバルセカンダリインデックスの既存テーブルへの追加
<a name="GSI.OnlineOps.Creating"></a>

既存のテーブルにグローバルセカンダリインデックスを追加するには、`UpdateTable` オペレーションと `GlobalSecondaryIndexUpdates` パラメータを使用します。以下の情報が必要です。
+ インデックス名。名前は、テーブル内のすべてのインデックスにおいて一意である必要があります。
+ インデックスのキースキーマ。インデックスパーティションキーには 1 つの属性を指定する必要があります。必要に応じて、インデックスソートキーに別の属性を指定できます。これらのキー属性のいずれも、テーブルのキー属性と同じである必要はありません。各スキーマ属性のデータ型は、`String`、`Number`、または `Binary` のスカラーである必要があります。
+ テーブルからインデックスに射影される属性は以下の通りです。
  + `KEYS_ONLY` – インデックス内の各項目は、テーブルパーティションキーとソートキーの値、およびインデックスキーの値のみで構成されます。
  + `INCLUDE` – `KEYS_ONLY` の属性に加えて、セカンダリインデックスにその他の非キー属性が含まれるように指定できます。
  + `ALL` – インデックスには、ソーステーブルのすべての属性が含まれます。
+ インデックスのプロビジョニングされたスループット設定で、`ReadCapacityUnits` および `WriteCapacityUnits` で構成されます。これは、テーブルのプロビジョニングされたスループット設定とは異なります。

`UpdateTable` オペレーションについて作成できるグローバルセカンダリインデックスは 1 つだけです。

### インデックス作成のフェーズ
<a name="GSI.OnlineOps.Creating.Phases"></a>

新しいグローバルセカンダリインデックスを既存のテーブルに追加すると、インデックスの構築中もテーブルが使用可能になります。ただし、新しいインデックスは、そのステータスが `CREATING` から `ACTIVE` に変わるまでクエリオペレーションに使用できません。

**注記**  
グローバルセカンダリインデックスの作成では、Application Auto Scaling を使用しません。`MIN` Application Auto Scaling の容量を増やしても、グローバルセカンダリインデックスの作成時間は短縮されません。

その裏では、DynamoDB は 2 つのフェーズでインデックスを構築しています。

**リソース割り当て**  
DynamoDB は、インデックスの構築に必要なコンピューティングリソースとストレージリソースを割り当てます。  
リソース割り当てフェーズでは、`IndexStatus` 属性は `CREATING`、`Backfilling` 属性は false です。`DescribeTable` オペレーションを使用して、テーブルとそのセカンダリインデックスすべてのステータスを取得します。  
インデックスがリソース割り当てフェーズにある間は、インデックスやその親テーブルを削除することはできません。インデックスまたはテーブルのプロビジョニングされたスループットを変更することもできません。テーブル上の他のインデックスを追加したり削除したりすることはできません。ただし、これらの他のインデックスのプロビジョニングされたスループットは変更できます。

**バックフィル**  
テーブルの各項目について、DynamoDB は、その射影 (`KEYS_ONLY`、`INCLUDE`、または `ALL`) に基づいて、インデックスに書き込む属性のセットを決定します。次に、これらの属性をインデックスに書き込みます。バックフィルフェーズでは、DynamoDB はテーブルで追加、削除、または更新される項目を追跡します。これらの項目の属性も、必要に応じてインデックスで追加、削除、または更新されます。  
バックフィルフェーズでは、`IndexStatus` 属性は `CREATING` に設定されていて、`Backfilling` 属性は true です。`DescribeTable` オペレーションを使用して、テーブルとそのセカンダリインデックスすべてのステータスを取得します。  
インデックスがバックフィルされている間は、親テーブルを削除することはできません。ただし、インデックスを削除したり、テーブルとそのグローバルセカンダリインデックスのプロビジョニングされたスループットを変更したりすることはできます。  
バックフィルフェーズでは、違反する項目の書き込みの一部が成功し、他の項目が拒否されることがあります。バックフィル後、新しいインデックスのキースキーマに違反する項目への書き込みはすべて拒否されます。バックフィルフェーズの終了後に Violation Detector ツールを実行して、発生した可能性のあるキー違反を検出して解決することをお勧めします。詳細については、「」を参照してください[DynamoDB でのインデックスキー違反の検出と修正](GSI.OnlineOps.ViolationDetection.md)

リソース割り当てフェーズとバックフィルフェーズの進行中、インデックスは `CREATING` 状態になっています。この間、DynamoDB はテーブルに対して読み込みオペレーションを実行します。グローバルセカンダリインデックスを設定するためのベーステーブルからの読み込みオペレーションに対しては課金されません。

インデックス構築が完了すると、ステータスは `ACTIVE` に変わります。インデックスが `ACTIVE` になるまで、`Query` や `Scan` はできません。

**注記**  
場合によっては、DynamoDB は、インデックスキーの違反のため、テーブルからインデックスにデータを書き込めない場合があります。これは、以下の場合に発生します。  
属性値のデータ型が、インデックスキーのスキーマデータ型のデータ型と一致しません。
属性のサイズが、インデックスキー属性の最大長を超えています。
インデックスキー属性には、空の文字列または空のバイナリ属性値があります。
インデックスキー違反があっても、グローバルセカンダリインデックスの作成はできます。ただし、インデックスが `ACTIVE` になると、違反しているキーはインデックスに存在しなくなります。  
DynamoDB には、これらの問題を検出して解決するためのスタンドアロンツールが用意されています。詳細については、「[DynamoDB でのインデックスキー違反の検出と修正](GSI.OnlineOps.ViolationDetection.md)」を参照してください。

### 大きなテーブルへのグローバルセカンダリインデックスの追加
<a name="GSI.OnlineOps.Creating.LargeTable"></a>

グローバルセカンダリインデックスの構築に必要な時間は、次のようないくつかの要因によって異なります。
+ テーブルのサイズ
+ インデックスに含めることができるテーブル内の項目の数
+ インデックスに射影される属性の数。
+ インデックス構築中のメインテーブルへの書き込みアクティビティ

非常に大きなテーブルにグローバルセカンダリインデックスを追加する場合、作成プロセスが完了するまで時間がかかることがあります。進行状況をモニタリングし、インデックスに十分な書き込み容量があるかどうかを判断するには、次の Amazon CloudWatch メトリクスを参照してください。
+ `OnlineIndexPercentageProgress`

DynamoDB に関する CloudWatch メトリクスの詳細については、「[DynamoDB のメトリクス](metrics-dimensions.md#dynamodb-metrics)」を参照してください。

**重要**  
グローバルセカンダリインデックスを作成または更新する前に、非常に大きなテーブルを許可リストする必要がある場合があります。AWS サポートに連絡して、テーブルの許可リストを作成してください。

インデックスがバックフィルされている間、DynamoDB は内部システム容量を使用してテーブルから読み込みます。これは、インデックスの作成による影響を最小限に抑え、テーブルの読み込みキャパシティーが不足しないようにするためです。

## グローバルセカンダリインデックスの削除
<a name="GSI.OnlineOps.Deleting"></a>

グローバルセカンダリインデックスが不要になった場合には、`UpdateTable` オペレーションを使用して削除することができます。

グローバルセカンダリインデックスは、1 回の `UpdateTable` オペレーションで 1 つだけ削除できます。

グローバルセカンダリインデックスが削除されている間、親テーブルの読み込みまたは書き込みアクティビティに影響はありません。削除の進行中でも、他のインデックスでプロビジョニングされたスループットを変更できます。

**注記**  
`DeleteTable` アクションを使用してテーブルを削除すると、そのテーブルのすべてのグローバルセカンダリインデックスも削除されます。
アカウントではグローバルセカンダリインデックスの削除操作に対して課金されません。

## 作成時のグローバルセカンダリインデックスの変更
<a name="GSI.OnlineOps.Creating.Modify"></a>

インデックスが作成されている間は、`DescribeTable` オペレーションを使用して、どのフェーズにあるかを判断します。インデックスの説明に含まれているブール属性 `Backfilling` は、DynamoDB がテーブルから項目を含むインデックスを現在ロードしているかどうかを示します。`Backfilling` が true の場合、リソース割り当てフェーズは完了していて、インデックスがバックフィルされています。

バックフィルフェーズでは、作成中のインデックスを削除できます。このフェーズでは、テーブルの他のインデックスを追加または削除することはできません。

**注記**  
`CreateTable` オペレーションの一部として作成されたインデックスの場合、`Backfilling` 属性は `DescribeTable` 出力に現れません。詳細については、「[インデックス作成のフェーズ](#GSI.OnlineOps.Creating.Phases)」を参照してください。

# DynamoDB でのインデックスキー違反の検出と修正
<a name="GSI.OnlineOps.ViolationDetection"></a>

グローバルセカンダリインデックスの作成のバックフィルフェーズでは、Amazon DynamoDB はテーブルの各項目を調べて、インデックスに含める適格性があるかどうかを判断します。一部の項目は、インデックスキー違反の原因となるため、適格でない場合があります。このような場合、項目はテーブルに残りますが、インデックスにはその項目に対応するエントリがなくなります。

インデックスキー違反は次の場合に発生します。
+ 属性値とインデックスキーのスキーマのデータ型が一致しない場合。たとえば、`GameScores` テーブルの項目の 1 つが、`String` 型の `TopScore` 値を持っていたとします。`Number` 型の `TopScore` のパーティションキーを持つグローバルセカンダリインデックスを追加した場合、テーブルの項目はインデックスキーに違反します。
+ テーブルの属性値が、インデックスキー属性の最大長を超えている場合。パーティションキーの最大長は 2048 バイトで、ソートキーの最大長は 1024 バイトです。テーブル内の対応する属性値のいずれかがこれらの制限を超えると、テーブルの項目はインデックスキーに違反します。

**注記**  
インデックスキーとして使用される属性に String 属性または Binary 属性値が設定されている場合、属性値の長さは 0 より大きくする必要があります。そうしないと、テーブルの項目がインデックスキーに違反します。  
現時点では、このツールはこのインデックスキー違反にフラグを設定しません。

インデックスキー違反が発生した場合でも、バックフィルフェーズは中断することなく続行されます。ただし、違反している項目は、インデックスに含まれません。バックフィルフェーズ完了後は、新しいインデックスのキースキーマに違反する項目へのすべての書き込みが拒否されます。

インデックスキーに違反するテーブル内の属性値を識別して修正するには、Violation Detector ツールを使用します。Violation Detector を実行するには、スキャンするテーブルの名前、グローバルセカンダリインデックスパーティションキーとソートキーの名前とデータ型、インデックスキー違反が見つかった場合に実行するアクションを指定する設定ファイルを作成します。Violation Detector は、次の 2 つのモードのいずれかで実行できます。
+ **検出モード** – インデックスキー違反を検出します。検出モードを使用して、グローバルセカンダリインデックスでキー違反の原因となるテーブル内の項目を報告します。(必要に応じて、違反しているテーブル項目が見つかるとすぐに削除するように要求できます)。検出モードからの出力はファイルに書き込まれるので、これを使用してさらに分析できます。
+ **修正モード** – インデックスキー違反を修正します。修正モードでは、Violation Detector は検出モードからの出力ファイルと同じ形式の入力ファイルを読み込みます。修正モードでは、入力ファイルからレコードを読み込み、各レコードについて、テーブル内の対応する項目を削除または更新します。(項目の更新を選択した場合は、入力ファイルを編集して、これらの更新に適切な値を設定する必要があります)。

## Violation Detector のダウンロードおよび実行
<a name="GSI.OnlineOps.ViolationDetection.Running"></a>

Violation Detector は、実行可能な Java Archive (`.jar` ファイル) で、Windows、macOS、または Linux コンピュータ上で実行されます。Violation Detector には Java 1.7 (以降) と Apache Maven が必要です。
+ [GitHub から Violation Detector をダウンロードします](https://github.com/awslabs/dynamodb-online-index-violation-detector)

`README.md` ファイルの指示に従って、Maven を使用して Violation Detector をダウンロードしてインストールします。

Violation Detector を起動するには、`ViolationDetector.java` を構築したディレクトリで、次のコマンドを入力します。

```
java -jar ViolationDetector.jar [options]
```

Violation Detector ツールのコマンドラインでは、以下のオプションが使用できます。
+ `-h | --help` – Violation Detector の使用方法の概要とオプションを出力します。
+ `-p | --configFilePath` `value` – Violation Detector 設定ファイルの完全修飾名。詳細については、「」を参照してください[Violation Detector 設定ファイル](#GSI.OnlineOps.ViolationDetection.ConfigFile)
+ `-t | --detect` `value` – テーブル内のインデックスキー違反を検出し、Violation Detector の出力ファイルに書き込みます。このパラメータの値が `keep` に設定されている場合、キー違反のある項目は変更されません。値が `delete` に設定されている場合、キー違反のある項目はテーブルから削除されます。
+ `-c | --correct` `value` – 入力ファイルからインデックスキー違反を読み込み、テーブル内の項目に対して修正アクションを実行します。このパラメータの値が `update` に設定されている場合、キー違反のある項目は、違反していない新しい値で更新されます。値が `delete` に設定されている場合、キー違反のある項目はテーブルから削除されます。

## Violation Detector 設定ファイル
<a name="GSI.OnlineOps.ViolationDetection.ConfigFile"></a>

実行時に、Violation Detector ツールには設定ファイルが必要です。このファイルのパラメータによって、Violation Detector がアクセスできる DynamoDB リソースと、プロビジョニングされたスループットを使用できる量が決まります。以下の表は、これらのパラメータの説明です。


****  

| パラメータ名 | 説明 | 必須? | 
| --- | --- | --- | 
|  `awsCredentialsFile`  |  AWS 認証情報を含むファイルの完全修飾名。認証情報ファイルの形式は、次のようになります。 <pre>accessKey = access_key_id_goes_here<br />secretKey = secret_key_goes_here </pre>  |  はい  | 
|  `dynamoDBRegion`  |  テーブルが存在する AWS リージョン。例: `us-west-2`。  |  はい  | 
|  `tableName`  | スキャンされる DynamoDB テーブルの名前。 |  はい  | 
|  `gsiHashKeyName`  |  インデックスパーティションキーの名前。  |  はい  | 
|  `gsiHashKeyType`  |  インデックスパーティションキーのデータ型 – `String`、`Number`、または `Binary`: `S \| N \| B`  |  はい  | 
|  `gsiRangeKeyName`  |  インデックスソートキーの名前。インデックスにシンプルなプライマリキー (パーティションキー) のみがある場合は、このパラメータを指定しないでください。  |  いいえ  | 
|  `gsiRangeKeyType`  |  インデックスソートキーのデータ型 – `String`、`Number`、または `Binary`: `S \| N \| B`  インデックスにシンプルなプライマリキー (パーティションキー) のみがある場合は、このパラメータを指定しないでください。  |  いいえ  | 
|  `recordDetails`  |  インデックスキー違反の詳細を出力ファイルに書き込むかどうか。設定が `true` の場合 (デフォルト)、違反項目に関する完全な情報が報告されます。設定が `false` の場合、違反の数のみが報告されます。  |  いいえ  | 
|  `recordGsiValueInViolationRecord`  |  違反しているインデックスキーの値を出力ファイルに書き込むかどうか。設定が `true` の場合 (デフォルト)、キー値が報告されます。設定が `false` の場合、キー値は報告されません。  |  いいえ  | 
|  `detectionOutputPath`  |  Violation Detector の出力ファイルのフルパス。このパラメータは、ローカルディレクトリまたはAmazon Simple Storage Service (Amazon S3) への書き込みをサポートしています。次に例を示します。 `detectionOutputPath = ``//local/path/filename.csv` `detectionOutputPath = ``s3://bucket/filename.csv` 出力ファイル内の情報は、CSV (Comma-Separated Values) 形式で表示されます。`detectionOutputPath` を設定しない場合、出力ファイル名は `violation_detection.csv` になり、現在の作業ディレクトリに書き込まれます。  |  いいえ  | 
|  `numOfSegments`  | Violation Detector がテーブルをスキャンするときに使用される並列スキャンセグメントの数。デフォルト値は 1 です。これは、テーブルがシーケンシャルにスキャンされることを意味します。値が 2 以上の場合、Violation Detector はテーブルをその数の論理セグメントに分割し、同じ数のスキャンスレッドにします。`numOfSegments` の設定は最大 4,096 です。大きなテーブルの場合、並列スキャンはシーケンシャルスキャンよりも一般的に高速です。さらに、テーブルが複数のパーティションにまたがるほど大きい場合、並列スキャンによって読み込みアクティビティが複数のパーティションに均等に分散されます。DynamoDB の並列スキャンの詳細については、「[並列スキャン](Scan.md#Scan.ParallelScan)」を参照してください。 |  いいえ  | 
|  `numOfViolations`  |  出力ファイルに書き込むインデックスキー違反の上限値。設定が `-1` の場合 (デフォルト)、テーブル全体がスキャンされます。正の整数に設定すると、Violation Detector は設定した数の違反を検出した後に停止します。  |  いいえ  | 
|  `numOfRecords`  |  スキャンされるテーブル内の項目数。設定が -1 の場合(デフォルト)、テーブル全体がスキャンされます。正の整数に設定すると、Violation Detector はテーブル内の設定した数の項目をスキャンした後に停止します。  |  いいえ  | 
|  `readWriteIOPSPercent`  |  テーブルスキャン中に使用される、プロビジョニングされた読み込みキャパシティーユニットの割合を調整します。有効な値の範囲は `1`～`100` です。デフォルト値 (`25`) は、Violation Detector がテーブルのプロビジョニングされた読み込みスループットの 25% 以下を使用することを意味します。  |  いいえ  | 
|  `correctionInputPath`  |  Violation Detector 修正入力ファイルのフルパス。Violation Detector を修正モードで実行すると、このファイルの内容を使用して、グローバルセカンダリインデックスに違反するテーブル内のデータ項目を変更または削除します。 `correctionInputPath` ファイルの形式は、`detectionOutputPath` ファイルの形式と同じです。これにより、検出モードからの出力を修正モードの入力として処理できます。  |  いいえ  | 
|  `correctionOutputPath`  |  Violation Detector 修正出力ファイルのフルパス。このファイルは、更新エラーがある場合にのみ作成されます。 このパラメータは、ローカルディレクトリまたは Amazon S3 への書き込みをサポートします。次に例を示します。 `correctionOutputPath = ``//local/path/filename.csv` `correctionOutputPath = ``s3://bucket/filename.csv` 出力ファイルの情報は CSV 形式で表示されます。`correctionOutputPath` を設定しない場合、出力ファイル名は `violation_update_errors.csv` になり、現在の作業ディレクトリに書き込まれます。  |  いいえ  | 

## 検出
<a name="GSI.OnlineOps.ViolationDetection.Detection"></a>

インデックスキー違反を検出するには、Violation Detector を `--detect` コマンドラインオプションで使用します。このオプションがどのように機能するかを示すには、`ProductCatalog` テーブルを検討してください。以下は、テーブルの項目の一覧です。プライマリキー (`Id`) と `Price` 属性のみを表示しています。


****  

| ID (プライマリキー) | 価格 | 
| --- | --- | 
| 101 |  5  | 
| 102 |  20  | 
| 103 | 200  | 
| 201 |  100  | 
| 202 |  200  | 
| 203 |  300  | 
| 204 |  400  | 
| 205 |  500  | 

`Price` の値はすべて、`Number` 型です。ただし、DynamoDB はスキーマレスであるため、数値以外の `Price` の項目を追加することができます。たとえば、`ProductCatalog` テーブルに別の項目を追加するとします。


****  

| ID (プライマリキー) | 価格 | 
| --- | --- | 
| 999 | "Hello" | 

これで、テーブルには合計 9 つの項目が追加されました。

ここで、新しいグローバルセカンダリインデックスをテーブルに追加します: `PriceIndex`。このインデックスのプライマリキーは、パーティションキー `Price` で、`Number` 型です。インデックスが構築されると、8 つの項目が含まれますが、`ProductCatalog` テーブルには 9 つの項目があります。この不一致の理由は、値 `"Hello"` は `String` 型ですが、`PriceIndex` は `Number` 型のプライマリキーを持っているからです。`String` 値がグローバルセカンダリインデックスキーに違反するため、インデックスには存在しません。

この状況で Violation Detector を使用するには、まず次のような設定ファイルを作成します。

```
# Properties file for violation detection tool configuration.
# Parameters that are not specified will use default values.

awsCredentialsFile = /home/alice/credentials.txt
dynamoDBRegion = us-west-2
tableName = ProductCatalog
gsiHashKeyName = Price
gsiHashKeyType = N
recordDetails = true
recordGsiValueInViolationRecord = true
detectionOutputPath = ./gsi_violation_check.csv
correctionInputPath = ./gsi_violation_check.csv
numOfSegments = 1
readWriteIOPSPercent = 40
```

続いて、次の例のように、Violation Detector を実行します。

```
$  java -jar ViolationDetector.jar --configFilePath config.txt --detect keep

Violation detection started: sequential scan, Table name: ProductCatalog, GSI name: PriceIndex
Progress: Items scanned in total: 9,    Items scanned by this thread: 9,    Violations found by this thread: 1, Violations deleted by this thread: 0
Violation detection finished: Records scanned: 9, Violations found: 1, Violations deleted: 0, see results at: ./gsi_violation_check.csv
```

`recordDetails` 設定パラメータが `true` に設定されている場合、Violation Detector は、次の例のように、各違反の詳細を出力ファイルに書き込みます。

```
Table Hash Key,GSI Hash Key Value,GSI Hash Key Violation Type,GSI Hash Key Violation Description,GSI Hash Key Update Value(FOR USER),Delete Blank Attributes When Updating?(Y/N) 

999,"{""S"":""Hello""}",Type Violation,Expected: N Found: S,,
```

出力ファイルは CSV 形式です。ファイルの最初の行はヘッダーで、2 行目以降はインデックスキーに違反する項目ごとに 1 つのレコードが続きます。これらの違反レコードのフィールドは次のとおりです。
+ **テーブルハッシュキー** – テーブル内の項目のパーティションキー値です。
+ **テーブル範囲キー** – テーブル内の項目のソートキー値です。
+ **GSI ハッシュキー値** – グローバルセカンダリインデックスのパーティションキー値です。
+ **GSI ハッシュキー違反タイプ** – `Type Violation` または `Size Violation`。
+ **GSI ハッシュキー違反の説明** – 違反の原因です。
+ **GSI ハッシュキー更新値 (ユーザー用)** – 修正モードでは、ユーザーが指定した属性の新しい値。
+ **GSI 範囲キー値** – グローバルセカンダリインデックスのソートキー値です。
+ **GSI 範囲キー違反タイプ** – `Type Violation` または `Size Violation`。
+ **GSI 範囲キー違反の説明** – 違反の原因です。
+ **GSI 範囲キーの更新値 (ユーザー用)** – 修正モードでは、ユーザーが指定した属性の新しい値。
+ **更新時に空白の属性を削除 (Y/N)** – 修正モードでは、テーブル内の違反項目を削除する (Y) か、保持する (N) かを決定します。ただし、次のフィールドのいずれかが空白の場合のみ実行されます。
  + `GSI Hash Key Update Value(FOR USER)`
  + `GSI Range Key Update Value(FOR USER)`

  これらのフィールドのいずれかが空白でない場合、`Delete Blank Attribute When Updating(Y/N)` は何も実行しません。

**注記**  
出力形式は、設定ファイルとコマンドラインオプションによって異なることがあります。例えば、テーブルに単純なプライマリキー (ソートキーなし) がある場合、出力にはソートキーフィールドは表示されません。  
ファイル内の違反レコードがソート順でない可能性があります。

## 修正
<a name="GSI.OnlineOps.ViolationDetection.Correction"></a>

インデックスキーの違反を修正するには、Violation Detector を `--correct` コマンドラインオプションで使用します。修正モードでは、Violation Detector は `correctionInputPath` パラメータで指定された入力ファイルを読み込みます。このファイルの形式は `detectionOutputPath` ファイルの形式と同じなので、検出からの出力を修正のための入力として使用できます。

Violation Detector には、インデックスキー違反を修正する 2 種類の方法があります。
+ **違反の削除** – 属性値に違反しているテーブル項目を削除します。
+ **違反の更新** – テーブル項目を更新し、違反している属性を違反しない値に置き換えます。

いずれの場合も、検出モードからの出力ファイルを修正モードの入力として使用できます。

`ProductCatalog` の例に進みます。違反している項目をテーブルから削除するとします。これを行うには、次のコマンドラインを使用します。

```
$  java -jar ViolationDetector.jar --configFilePath config.txt --correct delete
```

この時点で、違反している項目を削除するかどうかを確認するメッセージが表示されます。

```
Are you sure to delete all violations on the table?y/n
y
Confirmed, will delete violations on the table...
Violation correction from file started: Reading records from file: ./gsi_violation_check.csv, will delete these records from table.
Violation correction from file finished: Violations delete: 1, Violations Update: 0
```

`ProductCatalog` と `PriceIndex` の項目の数が同じになりました。

# グローバルセカンダリインデックスの操作: Java
<a name="GSIJavaDocumentAPI"></a>

AWS SDK for Java ドキュメント API を使用して、1 つ以上のグローバルセカンダリインデックスを持つ Amazon DynamoDB テーブルを作成し、テーブルのインデックスを記述し、インデックスを使用してクエリを実行できます。

以下に、テーブルオペレーションの一般的な手順を示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. 対応するリクエストオブジェクトを作成して、オペレーションについて必要なパラメータとオプションパラメータを入力します。

1. 前のステップで作成したクライアントが提供する適切なメソッドを呼び出します。

**Topics**
+ [グローバルセカンダリインデックスを持つテーブルを作成します](#GSIJavaDocumentAPI.CreateTableWithIndex)
+ [グローバルセカンダリインデックスを持つテーブルの説明](#GSIJavaDocumentAPI.DescribeTableWithIndex)
+ [グローバルセカンダリインデックスのクエリ](#GSIJavaDocumentAPI.QueryAnIndex)
+ [例: AWS SDK for Java ドキュメント API を使用したグローバルセカンダリインデックス](GSIJavaDocumentAPI.Example.md)

## グローバルセカンダリインデックスを持つテーブルを作成します
<a name="GSIJavaDocumentAPI.CreateTableWithIndex"></a>

グローバルセカンダリインデックスは、テーブルの作成と同時に作成できます。これを行うには、`CreateTable` を使用し、1 つ以上のグローバルセカンダリインデックスの仕様を指定します。次の Java コード例では、気象データに関する情報を保持するテーブルを作成しています。パーティションキーは `Location` で、ソートキーは `Date` です。グローバルセカンダリインデックス `PrecipIndex` は、さまざまな場所の降水データへの高速アクセスを可能にします。

DynamoDB ドキュメント API を使用して、グローバルセカンダリインデックスを持つテーブルを作成する手順を次に示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. リクエスト情報を指定する `CreateTableRequest` クラスのインスタンスを作成します。

   テーブル名、プライマリキー、およびプロビジョニングされたスループット値を指定する必要があります。グローバルセカンダリインデックスの場合は、インデックス名、プロビジョニングされたスループット設定、インデックスソートキーの属性定義、インデックスのキースキーマ、および属性射影を指定する必要があります。

1. リクエストオブジェクトをパラメータとして指定して、`createTable` メソッドを呼び出します。

以下の Java コード例は、前述のステップの例です。このコードは、グローバルセカンダリインデックス (`PrecipIndex`) を持つテーブル (`WeatherData`) を作成します。インデックスパーティションキーは `Date` で、ソートキーは `Precipitation` です。すべてのテーブル属性がインデックスに射影されます。ユーザーは、このインデックスに対するクエリを実行して、特定の日付の気象データを取得できます。必要に応じて、降水量によってデータを並べ替えることもできます。

`Precipitation` はテーブルのキー属性ではないため、必須ではありません。ただし、`Precipitation` のない `WeatherData` 項目は `PrecipIndex` に表示されません。

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

// Attribute definitions
ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();

attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Location")
    .withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Date")
    .withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Precipitation")
    .withAttributeType("N"));

// Table key schema
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
tableKeySchema.add(new KeySchemaElement()
    .withAttributeName("Location")
    .withKeyType(KeyType.HASH));  //Partition key
tableKeySchema.add(new KeySchemaElement()
    .withAttributeName("Date")
    .withKeyType(KeyType.RANGE));  //Sort key

// PrecipIndex
GlobalSecondaryIndex precipIndex = new GlobalSecondaryIndex()
    .withIndexName("PrecipIndex")
    .withProvisionedThroughput(new ProvisionedThroughput()
        .withReadCapacityUnits((long) 10)
        .withWriteCapacityUnits((long) 1))
        .withProjection(new Projection().withProjectionType(ProjectionType.ALL));

ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>();

indexKeySchema.add(new KeySchemaElement()
    .withAttributeName("Date")
    .withKeyType(KeyType.HASH));  //Partition key
indexKeySchema.add(new KeySchemaElement()
    .withAttributeName("Precipitation")
    .withKeyType(KeyType.RANGE));  //Sort key

precipIndex.setKeySchema(indexKeySchema);

CreateTableRequest createTableRequest = new CreateTableRequest()
    .withTableName("WeatherData")
    .withProvisionedThroughput(new ProvisionedThroughput()
        .withReadCapacityUnits((long) 5)
        .withWriteCapacityUnits((long) 1))
    .withAttributeDefinitions(attributeDefinitions)
    .withKeySchema(tableKeySchema)
    .withGlobalSecondaryIndexes(precipIndex);

Table table = dynamoDB.createTable(createTableRequest);
System.out.println(table.getDescription());
```

DynamoDB がテーブルを作成し、テーブルのステータスを `ACTIVE` に設定するまで待機する必要があります。その後、テーブルへのデータ項目の入力を開始できます。

## グローバルセカンダリインデックスを持つテーブルの説明
<a name="GSIJavaDocumentAPI.DescribeTableWithIndex"></a>

テーブルでグローバルセカンダリインデックスに関する情報を取得するには、`DescribeTable` を使用します。インデックスごとに、名前、キースキーマ、および射影された属性にアクセスできます。

テーブルのグローバルセカンダリインデックス情報にアクセスする手順を次に示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. 操作対象のインデックスを表すために、`Table` クラスのインスタンスを作成します。

1. `Table` オブジェクトの `describe` メソッドを呼び出します。

以下の Java コード例は、前述のステップの例です。

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("WeatherData");
TableDescription tableDesc = table.describe();
    

Iterator<GlobalSecondaryIndexDescription> gsiIter = tableDesc.getGlobalSecondaryIndexes().iterator();
while (gsiIter.hasNext()) {
    GlobalSecondaryIndexDescription gsiDesc = gsiIter.next();
    System.out.println("Info for index "
         + gsiDesc.getIndexName() + ":");

    Iterator<KeySchemaElement> kseIter = gsiDesc.getKeySchema().iterator();
    while (kseIter.hasNext()) {
        KeySchemaElement kse = kseIter.next();
        System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType());
    }
    Projection projection = gsiDesc.getProjection();
    System.out.println("\tThe projection type is: "
        + projection.getProjectionType());
    if (projection.getProjectionType().toString().equals("INCLUDE")) {
        System.out.println("\t\tThe non-key projected attributes are: "
            + projection.getNonKeyAttributes());
    }
}
```

## グローバルセカンダリインデックスのクエリ
<a name="GSIJavaDocumentAPI.QueryAnIndex"></a>

テーブルに `Query` を実行するのとほぼ同じ方法で、グローバルセカンダリインデックスに対する `Query` を使用することができます。インデックス名、インデックスパーティションキーとソートキー (存在する場合) のクエリ条件、および返す属性を指定する必要があります。この例では、インデックスは `PrecipIndex` で、パーティションキーが `Date` で、ソートキーが `Precipitation` です。このインデックスクエリは、降水量がゼロより大きい特定の日付のすべての気象データを返します。

AWS SDK for Java ドキュメント API を使用してグローバルセカンダリインデックスをクエリする手順を次に示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. 操作対象のインデックスを表すために、`Table` クラスのインスタンスを作成します。

1. クエリするインデックスの `Index` クラスのインスタンスを作成します。

1. `Index` オブジェクトの `query` メソッドを呼び出します。

属性名 `Date` は DynamoDB の予約語です。したがって、式の属性名を `KeyConditionExpression` のプレースホルダーとして使用する必要があります。

以下の Java コード例は、前述のステップの例です。

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("WeatherData");
Index index = table.getIndex("PrecipIndex");

QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("#d = :v_date and Precipitation = :v_precip")
    .withNameMap(new NameMap()
        .with("#d", "Date"))
    .withValueMap(new ValueMap()
        .withString(":v_date","2013-08-10")
        .withNumber(":v_precip",0));

ItemCollection<QueryOutcome> items = index.query(spec);
Iterator<Item> iter = items.iterator(); 
while (iter.hasNext()) {
    System.out.println(iter.next().toJSONPretty());
}
```

# 例: AWS SDK for Java ドキュメント API を使用したグローバルセカンダリインデックス
<a name="GSIJavaDocumentAPI.Example"></a>

次の Java コード例は、グローバルセカンダリインデックスの操作方法を示しています。この例では、`Issues` というテーブルを作成しています。これは、ソフトウェア開発のためのシンプルなバグ追跡システムで使用できる可能性があります。パーティションキーは `IssueId` で、ソートキーは `Title` です。このテーブルには、次の 3 つのグローバルセカンダリインデックスがあります。
+ `CreateDateIndex` – パーティションキーは `CreateDate` で、ソートキーは `IssueId` です。テーブルキーに加えて、属性 `Description` および `Status` はインデックスに射影されます。
+ `TitleIndex` – パーティションキーは `Title` で、ソートキーは `IssueId` です。テーブルキー以外の属性はインデックスに射影されません。
+ `DueDateIndex` – パーティションキーは `DueDate` で、ソートキーはありません。すべてのテーブル属性がインデックスに射影されます。

`Issues` テーブルが作成されると、プログラムはソフトウェアのバグレポートを表すデータをテーブルにロードします。次に、グローバルセカンダリインデックスを使用してデータのクエリを実行します。最後に、プログラムは `Issues` テーブルを削除します。

以下の例をテストするための詳細な手順については、「[Java コードの例](CodeSamples.Java.md)」を参照してください。

**Example**  

```
package com.amazonaws.codesamples.document;

import java.util.ArrayList;
import java.util.Iterator;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Index;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.ItemCollection;
import com.amazonaws.services.dynamodbv2.document.QueryOutcome;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.spec.QuerySpec;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
import com.amazonaws.services.dynamodbv2.model.GlobalSecondaryIndex;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.Projection;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;

public class DocumentAPIGlobalSecondaryIndexExample {

    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    public static String tableName = "Issues";

    public static void main(String[] args) throws Exception {

        createTable();
        loadData();

        queryIndex("CreateDateIndex");
        queryIndex("TitleIndex");
        queryIndex("DueDateIndex");

        deleteTable(tableName);

    }

    public static void createTable() {

        // Attribute definitions
        ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();

        attributeDefinitions.add(new AttributeDefinition().withAttributeName("IssueId").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("Title").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("CreateDate").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("DueDate").withAttributeType("S"));

        // Key schema for table
        ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
        tableKeySchema.add(new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.HASH)); // Partition
                                                                                                           // key
        tableKeySchema.add(new KeySchemaElement().withAttributeName("Title").withKeyType(KeyType.RANGE)); // Sort
                                                                                                          // key

        // Initial provisioned throughput settings for the indexes
        ProvisionedThroughput ptIndex = new ProvisionedThroughput().withReadCapacityUnits(1L)
                .withWriteCapacityUnits(1L);

        // CreateDateIndex
        GlobalSecondaryIndex createDateIndex = new GlobalSecondaryIndex().withIndexName("CreateDateIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("CreateDate").withKeyType(KeyType.HASH), // Partition
                                                                                                                 // key
                        new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.RANGE)) // Sort
                                                                                                        // key
                .withProjection(
                        new Projection().withProjectionType("INCLUDE").withNonKeyAttributes("Description", "Status"));

        // TitleIndex
        GlobalSecondaryIndex titleIndex = new GlobalSecondaryIndex().withIndexName("TitleIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("Title").withKeyType(KeyType.HASH), // Partition
                                                                                                            // key
                        new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.RANGE)) // Sort
                                                                                                        // key
                .withProjection(new Projection().withProjectionType("KEYS_ONLY"));

        // DueDateIndex
        GlobalSecondaryIndex dueDateIndex = new GlobalSecondaryIndex().withIndexName("DueDateIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("DueDate").withKeyType(KeyType.HASH)) // Partition
                                                                                                              // key
                .withProjection(new Projection().withProjectionType("ALL"));

        CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName)
                .withProvisionedThroughput(
                        new ProvisionedThroughput().withReadCapacityUnits((long) 1).withWriteCapacityUnits((long) 1))
                .withAttributeDefinitions(attributeDefinitions).withKeySchema(tableKeySchema)
                .withGlobalSecondaryIndexes(createDateIndex, titleIndex, dueDateIndex);

        System.out.println("Creating table " + tableName + "...");
        dynamoDB.createTable(createTableRequest);

        // Wait for table to become active
        System.out.println("Waiting for " + tableName + " to become ACTIVE...");
        try {
            Table table = dynamoDB.getTable(tableName);
            table.waitForActive();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void queryIndex(String indexName) {

        Table table = dynamoDB.getTable(tableName);

        System.out.println("\n***********************************************************\n");
        System.out.print("Querying index " + indexName + "...");

        Index index = table.getIndex(indexName);

        ItemCollection<QueryOutcome> items = null;

        QuerySpec querySpec = new QuerySpec();

        if (indexName == "CreateDateIndex") {
            System.out.println("Issues filed on 2013-11-01");
            querySpec.withKeyConditionExpression("CreateDate = :v_date and begins_with(IssueId, :v_issue)")
                    .withValueMap(new ValueMap().withString(":v_date", "2013-11-01").withString(":v_issue", "A-"));
            items = index.query(querySpec);
        } else if (indexName == "TitleIndex") {
            System.out.println("Compilation errors");
            querySpec.withKeyConditionExpression("Title = :v_title and begins_with(IssueId, :v_issue)")
                    .withValueMap(
                            new ValueMap().withString(":v_title", "Compilation error").withString(":v_issue", "A-"));
            items = index.query(querySpec);
        } else if (indexName == "DueDateIndex") {
            System.out.println("Items that are due on 2013-11-30");
            querySpec.withKeyConditionExpression("DueDate = :v_date")
                    .withValueMap(new ValueMap().withString(":v_date", "2013-11-30"));
            items = index.query(querySpec);
        } else {
            System.out.println("\nNo valid index name provided");
            return;
        }

        Iterator<Item> iterator = items.iterator();

        System.out.println("Query: printing results...");

        while (iterator.hasNext()) {
            System.out.println(iterator.next().toJSONPretty());
        }

    }

    public static void deleteTable(String tableName) {

        System.out.println("Deleting table " + tableName + "...");

        Table table = dynamoDB.getTable(tableName);
        table.delete();

        // Wait for table to be deleted
        System.out.println("Waiting for " + tableName + " to be deleted...");
        try {
            table.waitForDelete();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void loadData() {

        System.out.println("Loading data into table " + tableName + "...");

        // IssueId, Title,
        // Description,
        // CreateDate, LastUpdateDate, DueDate,
        // Priority, Status

        putItem("A-101", "Compilation error", "Can't compile Project X - bad version number. What does this mean?",
                "2013-11-01", "2013-11-02", "2013-11-10", 1, "Assigned");

        putItem("A-102", "Can't read data file", "The main data file is missing, or the permissions are incorrect",
                "2013-11-01", "2013-11-04", "2013-11-30", 2, "In progress");

        putItem("A-103", "Test failure", "Functional test of Project X produces errors", "2013-11-01", "2013-11-02",
                "2013-11-10", 1, "In progress");

        putItem("A-104", "Compilation error", "Variable 'messageCount' was not initialized.", "2013-11-15",
                "2013-11-16", "2013-11-30", 3, "Assigned");

        putItem("A-105", "Network issue", "Can't ping IP address 127.0.0.1. Please fix this.", "2013-11-15",
                "2013-11-16", "2013-11-19", 5, "Assigned");

    }

    public static void putItem(

            String issueId, String title, String description, String createDate, String lastUpdateDate, String dueDate,
            Integer priority, String status) {

        Table table = dynamoDB.getTable(tableName);

        Item item = new Item().withPrimaryKey("IssueId", issueId).withString("Title", title)
                .withString("Description", description).withString("CreateDate", createDate)
                .withString("LastUpdateDate", lastUpdateDate).withString("DueDate", dueDate)
                .withNumber("Priority", priority).withString("Status", status);

        table.putItem(item);
    }

}
```

# グローバルセカンダリインデックスの操作: .NET
<a name="GSILowLevelDotNet"></a>

AWS SDK for .NET 低レベル API を使用して、1 つ以上のグローバルセカンダリインデックスを含む Amazon DynamoDB テーブルを作成し、テーブルのインデックスを記述し、インデックスを使用してクエリを実行できます。これらのオペレーションは、対応する DynamoDB オペレーションにマッピングされます。詳細については、「[Amazon DynamoDB API リファレンス](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/)」を参照してください。

以下に、.NET 低レベル API を使用したテーブルオペレーションの一般的な手順を示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. 対応するリクエストオブジェクトを作成して、オペレーションについて必要なパラメータとオプションパラメータを入力します。

   たとえば、テーブルを作成するには `CreateTableRequest` オブジェクトを作成し、テーブルやインデックスにクエリを実行するには `QueryRequest` オブジェクトを作成します。

1. 前述のステップで作成したクライアントから提供された適切なメソッドを実行します。

**Topics**
+ [グローバルセカンダリインデックスを持つテーブルを作成します](#GSILowLevelDotNet.CreateTableWithIndex)
+ [グローバルセカンダリインデックスを持つテーブルの説明](#GSILowLevelDotNet.DescribeTableWithIndex)
+ [グローバルセカンダリインデックスのクエリ](#GSILowLevelDotNet.QueryAnIndex)
+ [例: AWS SDK for .NET 低レベル API を使用したグローバルセカンダリインデックス](GSILowLevelDotNet.Example.md)

## グローバルセカンダリインデックスを持つテーブルを作成します
<a name="GSILowLevelDotNet.CreateTableWithIndex"></a>

グローバルセカンダリインデックスは、テーブルの作成と同時に作成できます。これを行うには、`CreateTable` を使用し、1 つ以上のグローバルセカンダリインデックスの仕様を指定します。次の C\$1 コード例では、気象データに関する情報を保持するテーブルを作成しています。パーティションキーは `Location` で、ソートキーは `Date` です。グローバルセカンダリインデックス `PrecipIndex` は、さまざまな場所の降水データへの高速アクセスを可能にします。

.NET 低レベル API を使用して、グローバルセカンダリインデックスを含むテーブルを作成する手順を次に示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. リクエスト情報を指定する `CreateTableRequest` クラスのインスタンスを作成します。

   テーブル名、プライマリキー、およびプロビジョニングされたスループット値を指定する必要があります。グローバルセカンダリインデックスの場合は、インデックス名、プロビジョニングされたスループット設定、インデックスソートキーの属性定義、インデックスのキースキーマ、および属性射影を指定する必要があります。

1. リクエストオブジェクトをパラメータとして指定して、`CreateTable` メソッドを実行します。

以下の C\$1 サンプルコードは、前述のステップの例です。このコードは、グローバルセカンダリインデックス (`PrecipIndex`) を持つテーブル (`WeatherData`) を作成します。インデックスパーティションキーは `Date` で、ソートキーは `Precipitation` です。すべてのテーブル属性がインデックスに射影されます。ユーザーは、このインデックスに対するクエリを実行して、特定の日付の気象データを取得できます。必要に応じて、降水量によってデータを並べ替えることもできます。

`Precipitation` はテーブルのキー属性ではないため、必須ではありません。ただし、`Precipitation` のない `WeatherData` 項目は `PrecipIndex` に表示されません。

```
client = new AmazonDynamoDBClient();
string tableName = "WeatherData";

// Attribute definitions
var attributeDefinitions = new List<AttributeDefinition>()
{
    {new AttributeDefinition{
        AttributeName = "Location",
        AttributeType = "S"}},
    {new AttributeDefinition{
        AttributeName = "Date",
        AttributeType = "S"}},
    {new AttributeDefinition(){
        AttributeName = "Precipitation",
        AttributeType = "N"}
    }
};

// Table key schema
var tableKeySchema = new List<KeySchemaElement>()
{
    {new KeySchemaElement {
        AttributeName = "Location",
        KeyType = "HASH"}},  //Partition key
    {new KeySchemaElement {
        AttributeName = "Date",
        KeyType = "RANGE"}  //Sort key
    }
};

// PrecipIndex
var precipIndex = new GlobalSecondaryIndex
{
    IndexName = "PrecipIndex",
    ProvisionedThroughput = new ProvisionedThroughput
    {
        ReadCapacityUnits = (long)10,
        WriteCapacityUnits = (long)1
    },
    Projection = new Projection { ProjectionType = "ALL" }
};

var indexKeySchema = new List<KeySchemaElement> {
    {new KeySchemaElement { AttributeName = "Date", KeyType = "HASH"}},  //Partition key
    {new KeySchemaElement{AttributeName = "Precipitation",KeyType = "RANGE"}}  //Sort key
};

precipIndex.KeySchema = indexKeySchema;

CreateTableRequest createTableRequest = new CreateTableRequest
{
    TableName = tableName,
    ProvisionedThroughput = new ProvisionedThroughput
    {
        ReadCapacityUnits = (long)5,
        WriteCapacityUnits = (long)1
    },
    AttributeDefinitions = attributeDefinitions,
    KeySchema = tableKeySchema,
    GlobalSecondaryIndexes = { precipIndex }
};

CreateTableResponse response = client.CreateTable(createTableRequest);
Console.WriteLine(response.CreateTableResult.TableDescription.TableName);
Console.WriteLine(response.CreateTableResult.TableDescription.TableStatus);
```

DynamoDB がテーブルを作成し、テーブルのステータスを `ACTIVE` に設定するまで待機する必要があります。その後、テーブルへのデータ項目の入力を開始できます。

## グローバルセカンダリインデックスを持つテーブルの説明
<a name="GSILowLevelDotNet.DescribeTableWithIndex"></a>

テーブルでグローバルセカンダリインデックスに関する情報を取得するには、`DescribeTable` を使用します。インデックスごとに、名前、キースキーマ、および射影された属性にアクセスできます。

.NET 低レベル API を使用してテーブルのグローバルセカンダリインデックス情報にアクセスするには次の手順に従います。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. リクエストオブジェクトをパラメータとして指定して、`describeTable` メソッドを実行します。

   リクエスト情報を指定する `DescribeTableRequest` クラスのインスタンスを作成します。テーブル名を入力する必要があります。

以下の C\$1 サンプルコードは、前述のステップの例です。

**Example**  

```
client = new AmazonDynamoDBClient();
string tableName = "WeatherData";

DescribeTableResponse response = client.DescribeTable(new DescribeTableRequest { TableName = tableName});

List<GlobalSecondaryIndexDescription> globalSecondaryIndexes =
response.DescribeTableResult.Table.GlobalSecondaryIndexes;

// This code snippet will work for multiple indexes, even though
// there is only one index in this example.

foreach (GlobalSecondaryIndexDescription gsiDescription in globalSecondaryIndexes) {
     Console.WriteLine("Info for index " + gsiDescription.IndexName + ":");

     foreach (KeySchemaElement kse in gsiDescription.KeySchema) {
          Console.WriteLine("\t" + kse.AttributeName + ": key type is " + kse.KeyType);
     }

      Projection projection = gsiDescription.Projection;
      Console.WriteLine("\tThe projection type is: " + projection.ProjectionType);

      if (projection.ProjectionType.ToString().Equals("INCLUDE")) {
           Console.WriteLine("\t\tThe non-key projected attributes are: "
                + projection.NonKeyAttributes);
      }
}
```

## グローバルセカンダリインデックスのクエリ
<a name="GSILowLevelDotNet.QueryAnIndex"></a>

テーブルに `Query` を実行するのとほぼ同じ方法で、グローバルセカンダリインデックスに対する `Query` を使用することができます。インデックス名、インデックスパーティションキーとソートキー (存在する場合) のクエリ条件、および返す属性を指定する必要があります。この例では、インデックスは `PrecipIndex` で、パーティションキーが `Date` で、ソートキーが `Precipitation` です。このインデックスクエリは、降水量がゼロより大きい特定の日付のすべての気象データを返します。

.NET 低レベル API を使用して、グローバルセカンダリインデックスを含むテーブルにクエリを実行する手順を次に示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. リクエスト情報を指定する `QueryRequest` クラスのインスタンスを作成します。

1. リクエストオブジェクトをパラメータとして指定して、`query` メソッドを実行します。

属性名 `Date` は DynamoDB の予約語です。したがって、式の属性名を `KeyConditionExpression` のプレースホルダーとして使用する必要があります。

以下の C\$1 サンプルコードは、前述のステップの例です。

**Example**  

```
client = new AmazonDynamoDBClient();

QueryRequest queryRequest = new QueryRequest
{
    TableName = "WeatherData",
    IndexName = "PrecipIndex",
    KeyConditionExpression = "#dt = :v_date and Precipitation > :v_precip",
    ExpressionAttributeNames = new Dictionary<String, String> {
        {"#dt", "Date"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue> {
        {":v_date", new AttributeValue { S =  "2013-08-01" }},
        {":v_precip", new AttributeValue { N =  "0" }}
    },
    ScanIndexForward = true
};

var result = client.Query(queryRequest);

var items = result.Items;
foreach (var currentItem in items)
{
    foreach (string attr in currentItem.Keys)
    {
        Console.Write(attr + "---> ");
        if (attr == "Precipitation")
        {
            Console.WriteLine(currentItem[attr].N);
    }
    else
    {
        Console.WriteLine(currentItem[attr].S);
    }

         }
     Console.WriteLine();
}
```

# 例: AWS SDK for .NET 低レベル API を使用したグローバルセカンダリインデックス
<a name="GSILowLevelDotNet.Example"></a>

次の C\$1 コード例は、グローバルセカンダリインデックスの操作方法を示しています。この例では、`Issues` というテーブルを作成しています。これは、ソフトウェア開発のためのシンプルなバグ追跡システムで使用できる可能性があります。パーティションキーは `IssueId` で、ソートキーは `Title` です。このテーブルには、次の 3 つのグローバルセカンダリインデックスがあります。
+ `CreateDateIndex` – パーティションキーは `CreateDate` で、ソートキーは `IssueId` です。テーブルキーに加えて、属性 `Description` および `Status` はインデックスに射影されます。
+ `TitleIndex` – パーティションキーは `Title` で、ソートキーは `IssueId` です。テーブルキー以外の属性はインデックスに射影されません。
+ `DueDateIndex` – パーティションキーは `DueDate` で、ソートキーはありません。すべてのテーブル属性がインデックスに射影されます。

`Issues` テーブルが作成されると、プログラムはソフトウェアのバグレポートを表すデータをテーブルにロードします。次に、グローバルセカンダリインデックスを使用してデータのクエリを実行します。最後に、プログラムは `Issues` テーブルを削除します。

以下の例をテストするための詳細な手順については、「[.NET コード例](CodeSamples.DotNet.md)」を参照してください。

**Example**  

```
using System;
using System.Collections.Generic;
using System.Linq;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;
using Amazon.SecurityToken;

namespace com.amazonaws.codesamples
{
    class LowLevelGlobalSecondaryIndexExample
    {
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();
        public static String tableName = "Issues";

        public static void Main(string[] args)
        {
            CreateTable();
            LoadData();

            QueryIndex("CreateDateIndex");
            QueryIndex("TitleIndex");
            QueryIndex("DueDateIndex");

            DeleteTable(tableName);

            Console.WriteLine("To continue, press enter");
            Console.Read();
        }

        private static void CreateTable()
        {
            // Attribute definitions
            var attributeDefinitions = new List<AttributeDefinition>()
        {
            {new AttributeDefinition {
                 AttributeName = "IssueId", AttributeType = "S"
             }},
            {new AttributeDefinition {
                 AttributeName = "Title", AttributeType = "S"
             }},
            {new AttributeDefinition {
                 AttributeName = "CreateDate", AttributeType = "S"
             }},
            {new AttributeDefinition {
                 AttributeName = "DueDate", AttributeType = "S"
             }}
        };

            // Key schema for table
            var tableKeySchema = new List<KeySchemaElement>() {
            {
                new KeySchemaElement {
                    AttributeName= "IssueId",
                    KeyType = "HASH" //Partition key
                }
            },
            {
                new KeySchemaElement {
                    AttributeName = "Title",
                    KeyType = "RANGE" //Sort key
                }
            }
        };

            // Initial provisioned throughput settings for the indexes
            var ptIndex = new ProvisionedThroughput
            {
                ReadCapacityUnits = 1L,
                WriteCapacityUnits = 1L
            };

            // CreateDateIndex
            var createDateIndex = new GlobalSecondaryIndex()
            {
                IndexName = "CreateDateIndex",
                ProvisionedThroughput = ptIndex,
                KeySchema = {
                new KeySchemaElement {
                    AttributeName = "CreateDate", KeyType = "HASH" //Partition key
                },
                new KeySchemaElement {
                    AttributeName = "IssueId", KeyType = "RANGE" //Sort key
                }
            },
                Projection = new Projection
                {
                    ProjectionType = "INCLUDE",
                    NonKeyAttributes = {
                    "Description", "Status"
                }
                }
            };

            // TitleIndex
            var titleIndex = new GlobalSecondaryIndex()
            {
                IndexName = "TitleIndex",
                ProvisionedThroughput = ptIndex,
                KeySchema = {
                new KeySchemaElement {
                    AttributeName = "Title", KeyType = "HASH" //Partition key
                },
                new KeySchemaElement {
                    AttributeName = "IssueId", KeyType = "RANGE" //Sort key
                }
            },
                Projection = new Projection
                {
                    ProjectionType = "KEYS_ONLY"
                }
            };

            // DueDateIndex
            var dueDateIndex = new GlobalSecondaryIndex()
            {
                IndexName = "DueDateIndex",
                ProvisionedThroughput = ptIndex,
                KeySchema = {
                new KeySchemaElement {
                    AttributeName = "DueDate",
                    KeyType = "HASH" //Partition key
                }
            },
                Projection = new Projection
                {
                    ProjectionType = "ALL"
                }
            };



            var createTableRequest = new CreateTableRequest
            {
                TableName = tableName,
                ProvisionedThroughput = new ProvisionedThroughput
                {
                    ReadCapacityUnits = (long)1,
                    WriteCapacityUnits = (long)1
                },
                AttributeDefinitions = attributeDefinitions,
                KeySchema = tableKeySchema,
                GlobalSecondaryIndexes = {
                createDateIndex, titleIndex, dueDateIndex
            }
            };

            Console.WriteLine("Creating table " + tableName + "...");
            client.CreateTable(createTableRequest);

            WaitUntilTableReady(tableName);
        }

        private static void LoadData()
        {
            Console.WriteLine("Loading data into table " + tableName + "...");

            // IssueId, Title,
            // Description,
            // CreateDate, LastUpdateDate, DueDate,
            // Priority, Status

            putItem("A-101", "Compilation error",
                "Can't compile Project X - bad version number. What does this mean?",
                "2013-11-01", "2013-11-02", "2013-11-10",
                1, "Assigned");

            putItem("A-102", "Can't read data file",
                "The main data file is missing, or the permissions are incorrect",
                "2013-11-01", "2013-11-04", "2013-11-30",
                2, "In progress");

            putItem("A-103", "Test failure",
                "Functional test of Project X produces errors",
                "2013-11-01", "2013-11-02", "2013-11-10",
                1, "In progress");

            putItem("A-104", "Compilation error",
                "Variable 'messageCount' was not initialized.",
                "2013-11-15", "2013-11-16", "2013-11-30",
                3, "Assigned");

            putItem("A-105", "Network issue",
                "Can't ping IP address 127.0.0.1. Please fix this.",
                "2013-11-15", "2013-11-16", "2013-11-19",
                5, "Assigned");
        }

        private static void putItem(
            String issueId, String title,
            String description,
            String createDate, String lastUpdateDate, String dueDate,
            Int32 priority, String status)
        {
            Dictionary<String, AttributeValue> item = new Dictionary<string, AttributeValue>();

            item.Add("IssueId", new AttributeValue
            {
                S = issueId
            });
            item.Add("Title", new AttributeValue
            {
                S = title
            });
            item.Add("Description", new AttributeValue
            {
                S = description
            });
            item.Add("CreateDate", new AttributeValue
            {
                S = createDate
            });
            item.Add("LastUpdateDate", new AttributeValue
            {
                S = lastUpdateDate
            });
            item.Add("DueDate", new AttributeValue
            {
                S = dueDate
            });
            item.Add("Priority", new AttributeValue
            {
                N = priority.ToString()
            });
            item.Add("Status", new AttributeValue
            {
                S = status
            });

            try
            {
                client.PutItem(new PutItemRequest
                {
                    TableName = tableName,
                    Item = item
                });
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void QueryIndex(string indexName)
        {
            Console.WriteLine
                ("\n***********************************************************\n");
            Console.WriteLine("Querying index " + indexName + "...");

            QueryRequest queryRequest = new QueryRequest
            {
                TableName = tableName,
                IndexName = indexName,
                ScanIndexForward = true
            };


            String keyConditionExpression;
            Dictionary<string, AttributeValue> expressionAttributeValues = new Dictionary<string, AttributeValue>();

            if (indexName == "CreateDateIndex")
            {
                Console.WriteLine("Issues filed on 2013-11-01\n");

                keyConditionExpression = "CreateDate = :v_date and begins_with(IssueId, :v_issue)";
                expressionAttributeValues.Add(":v_date", new AttributeValue
                {
                    S = "2013-11-01"
                });
                expressionAttributeValues.Add(":v_issue", new AttributeValue
                {
                    S = "A-"
                });
            }
            else if (indexName == "TitleIndex")
            {
                Console.WriteLine("Compilation errors\n");

                keyConditionExpression = "Title = :v_title and begins_with(IssueId, :v_issue)";
                expressionAttributeValues.Add(":v_title", new AttributeValue
                {
                    S = "Compilation error"
                });
                expressionAttributeValues.Add(":v_issue", new AttributeValue
                {
                    S = "A-"
                });

                // Select
                queryRequest.Select = "ALL_PROJECTED_ATTRIBUTES";
            }
            else if (indexName == "DueDateIndex")
            {
                Console.WriteLine("Items that are due on 2013-11-30\n");

                keyConditionExpression = "DueDate = :v_date";
                expressionAttributeValues.Add(":v_date", new AttributeValue
                {
                    S = "2013-11-30"
                });

                // Select
                queryRequest.Select = "ALL_PROJECTED_ATTRIBUTES";
            }
            else
            {
                Console.WriteLine("\nNo valid index name provided");
                return;
            }

            queryRequest.KeyConditionExpression = keyConditionExpression;
            queryRequest.ExpressionAttributeValues = expressionAttributeValues;

            var result = client.Query(queryRequest);
            var items = result.Items;
            foreach (var currentItem in items)
            {
                foreach (string attr in currentItem.Keys)
                {
                    if (attr == "Priority")
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].N);
                    }
                    else
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].S);
                    }
                }
                Console.WriteLine();
            }
        }

        private static void DeleteTable(string tableName)
        {
            Console.WriteLine("Deleting table " + tableName + "...");
            client.DeleteTable(new DeleteTableRequest
            {
                TableName = tableName
            });
            WaitForTableToBeDeleted(tableName);
        }

        private static void WaitUntilTableReady(string tableName)
        {
            string status = null;
            // Let us wait until table is created. Call DescribeTable.
            do
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                    status = res.Table.TableStatus;
                }
                catch (ResourceNotFoundException)
                {
                    // DescribeTable is eventually consistent. So you might
                    // get resource not found. So we handle the potential exception.
                }
            } while (status != "ACTIVE");
        }

        private static void WaitForTableToBeDeleted(string tableName)
        {
            bool tablePresent = true;

            while (tablePresent)
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                }
                catch (ResourceNotFoundException)
                {
                    tablePresent = false;
                }
            }
        }
    }
}
```

# AWS CLI を使用した DynamoDBでのグローバルセカンダリインデックスの操作
<a name="GCICli"></a>

AWS CLI を使用して、1 つ以上のグローバルセカンダリインデックスを含む Amazon DynamoDB テーブルを作成し、テーブルのインデックスを記述し、インデックスを使用してクエリを実行できます。

**Topics**
+ [グローバルセカンダリインデックスを持つテーブルを作成します](#GCICli.CreateTableWithIndex)
+ [グローバルセカンダリインデックスを既存のテーブルに追加します](#GCICli.CreateIndexAfterTable)
+ [グローバルセカンダリインデックスを持つテーブルの説明](#GCICli.DescribeTableWithIndex)
+ [グローバルセカンダリインデックスのクエリ](#GCICli.QueryAnIndex)

## グローバルセカンダリインデックスを持つテーブルを作成します
<a name="GCICli.CreateTableWithIndex"></a>

グローバルセカンダリインデックスは、テーブルの作成と同時に作成できます。　 これを行うには、`create-table` パラメータを使用し、1 つ以上のグローバルセカンダリインデックスの仕様を指定します。次の例では、`GameTitleIndex` と呼ばれるグローバルセカンダリインデックスを含む `GameScores` という名前のテーブルを作成します。ベーステーブルには、パーティションキー `UserId` とソートキー `GameTitle` があり、特定のゲームの個々のユーザーのベストスコアを効率的に見つけることができます。一方、GSI にはパーティションキー `GameTitle` とソートキー `TopScore` があり、特定のゲームの全体的な最高スコアをすばやく見つけることができます。

```
aws dynamodb create-table \
    --table-name GameScores \
    --attribute-definitions AttributeName=UserId,AttributeType=S \
                            AttributeName=GameTitle,AttributeType=S \
                            AttributeName=TopScore,AttributeType=N  \
    --key-schema AttributeName=UserId,KeyType=HASH \
                 AttributeName=GameTitle,KeyType=RANGE \
    --provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --global-secondary-indexes \
        "[
            {
                \"IndexName\": \"GameTitleIndex\",
                \"KeySchema\": [{\"AttributeName\":\"GameTitle\",\"KeyType\":\"HASH\"},
                                {\"AttributeName\":\"TopScore\",\"KeyType\":\"RANGE\"}],
                \"Projection\":{
                    \"ProjectionType\":\"INCLUDE\",
                    \"NonKeyAttributes\":[\"UserId\"]
                },
                \"ProvisionedThroughput\": {
                    \"ReadCapacityUnits\": 10,
                    \"WriteCapacityUnits\": 5
                }
            }
        ]"
```

DynamoDB がテーブルを作成し、テーブルのステータスを `ACTIVE` に設定するまで待機する必要があります。その後、テーブルへのデータ項目の入力を開始できます。[describe-table](https://docs.aws.amazon.com/cli/latest/reference/dynamodb/describe-table.html) を使用して、テーブル作成のステータスを判断できます。

## グローバルセカンダリインデックスを既存のテーブルに追加します
<a name="GCICli.CreateIndexAfterTable"></a>

グローバルセカンダリインデックスは、テーブルの作成後に追加したり変更したりすることもできます。これを行うには、`update-table` パラメータを使用し、1 つ以上のグローバルセカンダリインデックスの仕様を指定します。次の例では、前の例と同じスキーマを使用していますが、テーブルが既に作成されており、後で GSI を追加する場合を想定しています。

```
aws dynamodb update-table \
    --table-name GameScores \
    --attribute-definitions AttributeName=TopScore,AttributeType=N  \
    --global-secondary-index-updates \
        "[
            {
                \"Create\": {
                    \"IndexName\": \"GameTitleIndex\",
                    \"KeySchema\": [{\"AttributeName\":\"GameTitle\",\"KeyType\":\"HASH\"},
                                    {\"AttributeName\":\"TopScore\",\"KeyType\":\"RANGE\"}],
                    \"Projection\":{
                        \"ProjectionType\":\"INCLUDE\",
                        \"NonKeyAttributes\":[\"UserId\"]
                    }
                }
            }
        ]"
```

## グローバルセカンダリインデックスを持つテーブルの説明
<a name="GCICli.DescribeTableWithIndex"></a>

テーブルのグローバルセカンダリインデックスに関する情報を取得するには、`describe-table` パラメータを使用します。インデックスごとに、名前、キースキーマ、および射影された属性にアクセスできます。

```
aws dynamodb describe-table --table-name GameScores
```

## グローバルセカンダリインデックスのクエリ
<a name="GCICli.QueryAnIndex"></a>

テーブルに `query` を実行するのとほぼ同じ方法で、グローバルセカンダリインデックスに対する `query` オペレーションを使用することができます。インデックス名、インデックスソートキーのクエリ条件、および返す属性を指定する必要があります。この例では、インデックスは `GameTitleIndex`、インデックスソートキーは `GameTitle` です。

返される属性は、インデックスに射影された属性だけです。このクエリを変更して非キー属性を選択することもできますが、これには比較的コストのかかるテーブルフェッチアクティビティが必要です。テーブルのフェッチの詳細については、「[属性の射影](GSI.md#GSI.Projections)」を参照してください。

```
aws dynamodb query --table-name GameScores\
    --index-name GameTitleIndex \
    --key-condition-expression "GameTitle = :v_game" \
    --expression-attribute-values '{":v_game":{"S":"Alien Adventure"} }'
```

# ローカルセカンダリインデックス
<a name="LSI"></a>

ベーステーブルのプライマリキーを使用してデータのクエリを実行する必要があるのは、一部のアプリケーションだけです。ただし、代替のソートキーが役に立つ場合があります。アプリケーションでソートキーを選択できるようにするには、Amazon DynamoDB テーブルに 1 つ以上のローカルセカンダリインデックスを作成し、これらのインデックスに対して `Query` または `Scan` のリクエストを発行できます。

**Topics**
+ [シナリオ: ローカルセカンダリインデックスの使用](#LSI.Scenario)
+ [属性の射影](#LSI.Projections)
+ [ローカルセカンダリインデックスの作成](#LSI.Creating)
+ [ローカルセカンダリインデックスからのデータの読み込み](#LSI.Reading)
+ [項目の書き込みとローカルセカンダリインデックス](#LSI.Writes)
+ [ローカルセカンダリインデックスに対するプロビジョニングされたスループットに関する考慮事項](#LSI.ThroughputConsiderations)
+ [ローカルセカンダリインデックスのストレージに関する考慮事項](#LSI.StorageConsiderations)
+ [ローカルセカンダリインデックス内の項目コレクション](#LSI.ItemCollections)
+ [ローカルセカンダリインデックスの操作: Java](LSIJavaDocumentAPI.md)
+ [ローカルセカンダリインデックスの操作: .NET](LSILowLevelDotNet.md)
+ [DynamoDB AWS CLI でのローカルセカンダリインデックスの操作](LCICli.md)

## シナリオ: ローカルセカンダリインデックスの使用
<a name="LSI.Scenario"></a>

例として、`Thread` テーブルを検討します。このテーブルは、[AWS ディスカッションフォーラム](https://forums.aws.amazon.com/)のようなアプリケーションで役立ちます。次の図は、テーブル内の項目の構成を示しています。一部表示されていない属性もあります。

![\[フォーラム名、件名、最後の投稿時刻、および返信数のリストを含む Thread テーブル。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/LSI_01.png)


DynamoDB には、同じパーティションキー値を持つすべての項目が継続的に保存されます。この例では、特定の `ForumName` を指定すると、`Query` オペレーションはそのフォーラムのすべてのスレッドをすばやく見つけることができます。同じパーティションキー値を持つ項目のグループでは、項目がソートキーの値によって並べ替えられます。ソートキー (`Subject`) がクエリでも提供される場合、DynamoDB は返される結果を絞り込むことができます。例えば、「S3」フォーラムで、文字「a」で始まる `Subject` を持つすべてのスレッドを返すことができます。

リクエストによっては、より複雑なデータアクセスパターンが要求される場合があります。以下に例を示します。
+ ビューと返信の数が最も多いのはどのフォーラムか?
+ 特定のフォーラムでメッセージ数が最も多いのはどのスレッドか?
+ 特定の期間中に特定のフォーラムに投稿されたスレッド数は?

これらの質問に答えるには、`Query` アクションでは十分ではありません。代わりに、テーブル全体の `Scan` を実行する必要があります。膨大な数の項目があるテーブルでは、これにより、プロビジョニングされた読み込みスループットが大量に消費されることになり、完了するまでに長時間かかります。

ただし、`Replies` や `LastPostDateTime` などの非キー属性に 1 つ以上のローカルセカンダリインデックスを指定することができます。

*ローカルセカンダリインデックス*は特定のパーティションキー値の代替ソートキーを維持します。またローカルセカンダリインデックスには、ベーステーブルの一部またはすべての属性のコピーが含まれます。テーブルを作成する際に、ローカルセカンダリインデックスに射影する属性を指定します。ローカルセカンダリインデックスのデータは、ベーステーブルと同じパーティションキーと、異なるソートキーで構成されます。これにより、この異なるディメンションにわたってデータ項目に効率的にアクセスできます。クエリまたはスキャンの柔軟性を向上させるために、テーブルごとに最大 5 つのローカルセカンダリインデックスを作成できます。

たとえば、あるアプリケーションで、特定のフォーラムに過去 3 か月以内に投稿されたすべてのスレッドを検索する必要があるとします。ローカルセカンダリインデックスがない場合、アプリケーションは `Scan` テーブル全体の `Thread` を実行して、指定された時間枠から外れた投稿を破棄する必要があります。ローカルセカンダリインデックスがあれば、`Query` オペレーションでは `LastPostDateTime` をソートキーとして使用し、データをすばやく見つけることができます。

次の図は、`LastPostIndex` という名前のローカルセカンダリインデックスを示しています。パーティションキーは `Thread` テーブルのパーティションキーと同じですが、ソートキーは `LastPostDateTime` です。

![\[フォーラム名、件名、および最後の投稿時刻のリストを含む LastPostIndex テーブル。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/LSI_02.png)


ローカルセカンダリインデックスは、すべて次の条件を満たす必要があります。
+ パーティションキーはそのベーステーブルのパーティションキーと同じである。
+ ソートキーは完全に 1 つのスカラー属性で構成されている。
+ ベーステーブルのソートキーがインデックスに射影され、非キー属性として機能する。

この例で、パーティションキーは `ForumName`、ローカルセカンダリインデックスのソートキーは `LastPostDateTime` です。さらに、ベーステーブルのソートキーの値 (この例では `Subject`) がインデックスに射影されますが、その値はインデックスキーの一部ではありません。アプリケーションが `ForumName` と `LastPostDateTime` に基づくリストを必要とする場合は、`Query` に対して `LastPostIndex` リクエストを実行できます。クエリ結果は `LastPostDateTime` によってソートされ、昇順または降順で返すことができます。このクエリは、特定の時間枠内に `LastPostDateTime` がある項目だけを返すなどのキー条件を適用することもできます。

すべてのローカルセカンダリインデックスには、そのベーステーブルからのパーティションキーとソートキーが自動的に格納されます。必要に応じて、非キー属性をインデックスに射影できます。インデックスのクエリを行うと、DynamoDB ではこれらの射影された属性を効率的に取り出すことができます。ローカルセカンダリインデックスにクエリを実行すると、インデックスに射影されて*いない*属性もクエリで取得できます。DynamoDB では、これらの属性をベーステーブルから自動的にフェッチしますが、レイテンシーが大きくなり、プロビジョニングされたスループットコストが高くなります。

任意のローカルセカンダリインデックスについて、異なるパーティションキーの値ごとに最大 10 GB のデータを保存できます。この数字には、ベーステーブル内のすべての項目に加えて、同じパーティションキーの値を持つインデックス内のすべての項目も含まれています。詳細については、「[ローカルセカンダリインデックス内の項目コレクション](#LSI.ItemCollections)」を参照してください。

## 属性の射影
<a name="LSI.Projections"></a>

`LastPostIndex` では、アプリケーションはクエリの基準として `ForumName` と `LastPostDateTime` を使用できます。ただし、追加の属性を取得する場合、DynamoDB は `Thread` テーブルに対して追加の読み取りオペレーションを実行する必要があります。このような追加の読み込みを*フェッチ*と言い、それによってクエリにプロビジョニングするスループットの合計量が増加することがあります。

ウェブページに「S3」内のすべてのスレッドと各スレッドの返信のリストを表示し、最新の返信の最後の返信日時で並べ替えるとします。このリストに入力するには、次の属性が必要になります。
+ `Subject`
+ `Replies`
+ `LastPostDateTime`

このデータのクエリを最も効率的に行い、フェッチオペレーションを回避するには、次の図に示すように、ローカルセカンダリインデックスにテーブルからの `Replies` 属性を射影します。

![\[フォーラム名、最後の投稿時刻、件名、および返信のリストを含む LastPostIndex テーブル。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/LSI_03.png)




*射影*とは、テーブルからセカンダリインデックスにコピーされる属性のセットです。テーブルのパーティションキーとソートキーは常にインデックスに射影されます。アプリケーションのクエリ要件をサポートするために、他の属性を射影できます。インデックスをクエリすると、Amazon DynamoDB は、その属性が自身のテーブル内にあるかのように、プロジェクション内の任意の属性にアクセスできます。

セカンダリインデックスを作成するときは、インデックスに射影される属性を指定する必要があります。DynamoDB には、そのために次の 3 つのオプションが用意されています。
+ *KEYS\$1ONLY* – インデックス内の各項目は、テーブルパーティションキーとソートキーの値、およびインデックスキーの値のみで構成されます。`KEYS_ONLY` オプションを指定すると、セカンダリインデックスが最小になります。
+ *INCLUDE* – `KEYS_ONLY` の属性に加えて、セカンダリインデックスにその他の非キー属性が含まれるように指定できます。
+ *ALL* – セカンダリインデックスには、ソーステーブルのすべての属性が含まれます。すべてのテーブルデータがインデックスに複製されるため、`ALL` の射影にすると、セカンダリインデックスが最大になります。

前の図では、非キー属性の `Replies` が `LastPostIndex` に射影されています。アプリケーションでは `LastPostIndex` テーブル全体ではなく `Thread` に対するクエリを実行し、ウェブページに `Subject`、`Replies`、`LastPostDateTime` を表示することができます。他の非キー属性がリクエストされた場合、DynamoDB はそれらの属性を `Thread` テーブルからフェッチする必要があります。

アプリケーションの観点から見ると、ベーステーブルから追加の属性をフェッチする処理は、自動的で透過的なので、アプリケーションロジックを書き直す必要はありません。ただし、このようなフェッチによって、ローカルセカンダリインデックスを使用することで得られるパフォーマンスの利点が大幅に小さくなる可能性があります。

ローカルセカンダリインデックスに射影する属性を選択する場合には、プロビジョニングされるスループットコストとストレージコストのトレードオフを考慮する必要があります。
+ ごく一部の属性だけに最小のレイテンシーでアクセスする必要がある場合は、それらの属性だけをローカルセカンダリインデックスに射影することを検討してください。インデックスが小さいほど少ないコストで格納でき、書き込みコストも低くなります。まれにしかフェッチされない属性がある場合には、それらの属性を格納するコストよりも、プロビジョニングされるスループットのコストのほうが長期的に大きくなる可能性があります。
+ アプリケーションが非キー属性に頻繁にアクセスする場合には、それらの属性をローカルセカンダリインデックスに射影することを検討してください。ローカルセカンダリインデックスのストレージコストの増加は、頻繁にテーブルスキャンを実行するコストの減少で相殺されます。
+ ほとんどの非キー属性に頻繁にアクセスする場合は、それらの属性を (場合によっては、ベーステーブル全体を) ローカルセカンダリインデックスに射影することができます。それによってフェッチが不要になるため、柔軟性が最大になり、プロビジョニングされるスループットが最小限になります。ただし、ストレージコストが増加し、すべての属性を射影する場合には 2 倍にまで増大します。
+ アプリケーションでテーブルのクエリを頻繁に行う必要がなく、テーブル内のデータに対する書き込みや更新が多数になる場合は、*KEYS\$1ONLY* を射影することを検討してください。ローカルセカンダリインデックスは、最小サイズになりますが、それでもクエリのアクティビティに必要な場合は利用可能です。

## ローカルセカンダリインデックスの作成
<a name="LSI.Creating"></a>

テーブルで 1 つ以上のローカルセカンダリインデックスを作成するには、`CreateTable` オペレーションの `LocalSecondaryIndexes` パラメータを使用します。テーブルのローカルセカンダリインデックスは、テーブルが作成された時点で作成されます。テーブルを削除すると、そのテーブルにあるローカルセカンダリインデックスも削除されます。

ローカルセカンダリインデックスのソートキーにとなる 1 つの非キー属性を指定する必要があります。選択する属性はスカラー `String`、`Number`、または `Binary` である必要があります。その他のスカラー型、ドキュメント型、およびセット型は許可されません。データ型の詳細なリストについては、「[データ型](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes)」を参照してください。

**重要**  
ローカルセカンダリインデックスがあるテーブルには、パーティションキーの値ごとに 10 GB のサイズ制限があります。ローカルセカンダリインデックスがあるテーブルには、1 つのパーティションキー値の合計サイズが 10 GB を超えない限り、任意の数の項目を格納できます。詳細については、「」を参照してください[項目コレクションのサイズ制限](#LSI.ItemCollections.SizeLimit)

ローカルセカンダリインデックスには、任意のデータ型の属性を射影できます。これには、スカラー、ドキュメント、およびセットが含まれます。データ型の詳細なリストについては、「[データ型](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes)」を参照してください。

## ローカルセカンダリインデックスからのデータの読み込み
<a name="LSI.Reading"></a>

`Query` および `Scan` オペレーションを使用して、ローカルセカンダリインデックスから項目を取得できます。`GetItem` および `BatchGetItem` オペレーションは、ローカルセカンダリインデックスでは使用できません。

### ローカルセカンダリインデックスのクエリ
<a name="LSI.Querying"></a>

DynamoDB テーブルでは、各項目のパーティションキー値とソートキー値の組み合わせは、一意である必要があります。ただし、ローカルセカンダリインデックスでは、ソートキーの値は、特定のパーティションキーの値に対して一意である必要がありません。ローカルセカンダリインデックスに同じソートキーを持つ複数の項目がある場合、`Query` オペレーションは、同じパーティションキーの値を持つすべての項目を返します。レスポンスでは、一致する項目は特定の順序で返されません。

結果整合性の読み込みまたは完全整合性の読み込みを使用して、ローカルセカンダリインデックスのクエリを行うことができます。必要な整合性のタイプを指定するには、`ConsistentRead` オペレーションの `Query` パラメータを使用します。ローカルセカンダリインデックスからの完全整合性の読み込みでは、常に最新の更新された値が返されます。クエリがベーステーブルからさらに属性をフェッチする必要がある場合、それらの属性はインデックスについて整合性があることになります。

**Example**  
特定のフォーラムのディスカッションスレッドにあるデータをリクエストする `Query` から返される次のデータを考えてみます。  

```
{
    "TableName": "Thread",
    "IndexName": "LastPostIndex",
    "ConsistentRead": false,
    "ProjectionExpression": "Subject, LastPostDateTime, Replies, Tags",
    "KeyConditionExpression": 
        "ForumName = :v_forum and LastPostDateTime between :v_start and :v_end",
    "ExpressionAttributeValues": {
        ":v_start": {"S": "2015-08-31T00:00:00.000Z"},
        ":v_end": {"S": "2015-11-31T00:00:00.000Z"},
        ":v_forum": {"S": "EC2"}
    }
}
```
このクエリでは次のようになっています。  
+ DynamoDB は `ForumName` パーティションキーを使用して `LastPostIndex` にアクセスし、「EC2」のインデックス項目を特定します。このキーを持つすべてのインデックス項目が、すばやく取り出せるように隣り合わせに格納されます。
+ このフォーラム内で、DynamoDB はインデックスを使用して、指定された `LastPostDateTime` 条件に一致するキーを検索します。
+ `Replies` 属性はインデックスに射影されているため、DynamoDB は追加でプロビジョニングされたスループットを使用することなく、この属性を取得できます。
+ `Tags` 属性はインデックスに射影されていないため、DynamoDB は `Thread` テーブルにアクセスしてこの属性をフェッチする必要があります。
+ 結果が、`LastPostDateTime` によってソートされ、返されます。インデックスのエントリはまずパーティションキーの値によって、次にソートキーの値によって並べ替えられ、保存される順序で `Query` から返されます （`ScanIndexForward` パラメータを使用すると、結果が降順で返されます）。
ローカルセカンダリインデックスには `Tags` 属性が射影されていないため、DynamoDB は、読み取りキャパシティユニットを追加で使用して、ベーステーブルからこの属性をフェッチする必要があります。このクエリを頻繁に実行する必要がある場合は、`Tags` 属性を `LastPostIndex` に射影して、ベーステーブルからフェッチされないようにする必要があります。ただし、`Tags` 属性をまれにしか使用しない場合は、`Tags` 属性をインデックスに射影するためにストレージを追加することが有効でない可能性があります。

### ローカルセカンダリインデックスのスキャン
<a name="LSI.Scanning"></a>

`Scan` を使用して、ローカルセカンダリインデックスからすべてのデータを取得できます。リクエストにはベーステーブル名とインデックス名を指定する必要があります。`Scan` では、DynamoDB はインデックスのすべてのデータを読み込み、それをアプリケーションに返します。また、データの一部のみを返し、残りのデータを破棄するようにリクエストすることもできます。これを行うには、`FilterExpression` API の `Scan` パラメータを使用します。詳細については、「[Scan のフィルタ式](Scan.md#Scan.FilterExpression)」を参照してください。

## 項目の書き込みとローカルセカンダリインデックス
<a name="LSI.Writes"></a>

DynamoDB によって、すべてのローカルセカンダリインデックスがそれぞれのベーステーブルと自動的に同期されます。アプリケーションがインデックスに直接書き込むことはありません。ただし、DynamoDB でこれらのインデックスがどのように維持されるかを理解することは重要です。

ローカルセカンダリインデックスを作成する場合は、インデックスのソートキーになる属性を指定します。その属性のデータ型も指定します。つまり、ベーステーブルに項目を書き込むとき、その項目にインデックスキー属性が定義されている場合は、その型がインデックスキースキーマのデータ型に一致する必要があります。`LastPostIndex` の場合、インデックス内の `LastPostDateTime` ソートキーは、`String` データ型として定義されています。`Thread` テーブルに項目を追加し、`LastPostDateTime` に対して別のデータ型を指定する場合 (`Number` など)、データ型の不一致により DynamoDB によって `ValidationException` が返されます。

ベーステーブル内の項目とローカルセカンダリインデックス内の項目を 1 対 1 の関係にする必要はありません。この動作は、多くのアプリケーションにとってメリットになります。

多数のローカルセカンダリインデックスがあるテーブルは、インデックス数が少ないテーブルに比べて書き込みアクティビティに多くのコストを要します。詳細については、「[ローカルセカンダリインデックスに対するプロビジョニングされたスループットに関する考慮事項](#LSI.ThroughputConsiderations)」を参照してください。

**重要**  
ローカルセカンダリインデックスがあるテーブルには、パーティションキーの値ごとに 10 GB のサイズ制限があります。ローカルセカンダリインデックスがあるテーブルには、1 つのパーティションキー値の合計サイズが 10 GB を超えない限り、任意の数の項目を格納できます。詳細については、「[項目コレクションのサイズ制限](#LSI.ItemCollections.SizeLimit)」を参照してください。

## ローカルセカンダリインデックスに対するプロビジョニングされたスループットに関する考慮事項
<a name="LSI.ThroughputConsiderations"></a>

DynamoDB にテーブルを作成する場合には、テーブルで予想されるワークロードに応じた読み込み/書き込みキャパシティーユニットをプロビジョニングします。このワークロードには、テーブルのローカルセカンダリインデックスの読み込み/書き込みアクティビティが含まれます。

プロビジョニングされたスループット容量の現在の料金を確認するには、「[Amazon DynamoDB 料金](https://aws.amazon.com/dynamodb/pricing)」を参照してください。

### 読み込みキャパシティユニット
<a name="LSI.ThroughputConsiderations.Reads"></a>

ローカルセカンダリインデックスに対してクエリを実行する場合、使用される読み込みキャパシティーユニットの数は、データのアクセス方法によって異なります。

テーブルに対するクエリと同様に、インデックスクエリでは `ConsistentRead` の値に応じて、結果整合性のある読み込みまたは強力な整合性のある読み込みを実行できます。1 回の強力な整合性のある読み込みでは、1 つの読み込みキャパシティーユニットが消費され、結果整合性のある読み込みではその半分が消費されます。したがって結果整合性のある読み込みを選択することで、読み込みキャパシティーユニットのコストを削減できます。

インデックスキーと射影された属性だけをリクエストするインデックスクエリの場合、DynamoDB はテーブルに対するクエリの場合と同じ方法で、プロビジョニングされた読み込みアクティビティを計算します。唯一の違いは、ベーステーブル内の項目のサイズではなくインデックスエントリのサイズに基づいて計算が行われることです。読み込みキャパシティーユニットの数は、返されたすべての項目について射影されたすべての属性のサイズの合計です。結果は、次の 4 KB 境界まで切り上げられます。DynamoDB がプロビジョニングされたスループットの利用率を計算する方法の詳細については、「[DynamoDB プロビジョンドキャパシティモード](provisioned-capacity-mode.md)」を参照してください。

ローカルセカンダリインデックスに射影されていない属性を読み込むインデックスクエリの場合、DynamoDB は射影された属性をインデックスから読み込むのに加えて、それらの属性をベーステーブルからフェッチする必要があります。これらのフェッチは、`Select` オペレーションの `ProjectionExpression` または `Query` パラメータに、射影されていない属性を含めた場合に実行されます。フェッチによってクエリ応答のレイテンシーが増加し、プロビジョニングされるスループットのコストも増加します。前述のローカルセカンダリインデックスからの読み込みに加えて、フェッチされるベーステーブル項目それぞれについて、読み込みキャパシティーユニットの料金がかかります。この料金は、リクエストした属性だけでなく、項目全体をテーブルから読み込むために発生するものです。

`Query` オペレーションによって返される結果の最大サイズは 1 MB です。これには、返されるすべての項目にわたる、すべての属性の名前と値のサイズが含まれます。ただし、ローカルセカンダリインデックスに対するクエリによって、DynamoDB がベーステーブルから項目の属性をフェッチする場合には、結果で示されるデータの最大サイズが小さくなる可能性があります。この場合、結果のサイズは次の合計になります。
+ インデックス内で一致する項目のサイズ (次の 4 KB に切り上げ)
+ ベーステーブル内で一致する各項目のサイズ (項目ごとに次の 4 KB に切り上げ)

この式を使用すると、クエリオペレーションによって返される結果の最大サイズは依然として 1 MB になります。

たとえば、各項目のサイズが 300 バイトであるテーブルがあるとします。そのテーブルにはローカルセカンダリインデックスがありますが、各項目のうち 200 バイトだけがインデックスに射影されます。このインデックスに対して `Query` を行うときに各項目についてテーブルのフェッチが必要で、クエリによって 4 つの項目が返されるとします。DynamoDB では、次のように合計されます。
+ インデックス内で一致する項目のサイズは 200 バイト × 4 項目 = 800 バイトになり、それが 4 KB に切り上げられます。
+ ベーステーブル内で一致する項目のサイズ: (300 バイト、4 KB に切り上げ) × 4 項目 = 16 KB。

それによって、結果データの合計サイズは 20 KB になります。

### 書き込みキャパシティユニット
<a name="LSI.ThroughputConsiderations.Writes"></a>

テーブル内の項目が追加、更新、または削除された場合にローカルセカンダリインデックスを更新すると、テーブルにプロビジョニングされた書き込みキャパシティーユニットが消費されます。書き込み用にプロビジョニングされたスループットの合計コストは、テーブルに対する書き込みと、ローカルセカンダリインデックスの更新で消費された書き込みキャパシティーユニットの合計になります。

ローカルセカンダリインデックスに項目を書き込むコストは、いくつかの要因に左右されます。
+ インデックス付き属性が定義されたテーブルに新しい項目を書き込む場合、または既存の項目を更新して未定義のインデックス付き属性を定義する場合には、インデックスへの項目の挿入に 1 回の書き込みオペレーションが必要です。
+ テーブルに対する更新によってインデックス付きキー属性の値が（A から B に）変更された場合には、インデックスから既存の項目を削除し、インデックスに新しい項目を挿入するために、2 回の書き込みが必要です。  
+ インデックス内に既存の項目があって、テーブルに対する書き込みによってインデックス付き属性が削除された場合は、インデックスから古い項目の射影を削除するために、1 回の書き込みが必要です。
+ 項目の更新の前後にインデックス内に項目が存在しない場合は、インデックスで追加の書き込みコストは発生しません。

これらすべての要因は、インデックス内の各項目のサイズが 1 KB 以下であるという前提で書き込みキャパシティーユニット数を算出します。インデックスエントリがそれよりも大きい場合は、書き込みキャパシティーユニットを追加する必要があります。クエリが返す必要がある属性を特定し、それらの属性だけをインデックスに射影することで、書き込みコストは最小になります。

## ローカルセカンダリインデックスのストレージに関する考慮事項
<a name="LSI.StorageConsiderations"></a>

アプリケーションがテーブルに項目を書き込むと、DynamoDB では正しい属性のサブセットが、それらの属性が表示されるローカルセカンダリインデックスに自動的にコピーされます。AWS アカウントでは、テーブル内の項目のストレージと、そのベーステーブルのローカルセカンダリインデックスにある属性のストレージに対して課金されます。

インデックス項目が使用するスペースの量は、次の量の合計になります。
+ ベーステーブルのプライマリキー (パーティションキーとソートキー) のサイズのバイト数
+ インデックスキー属性のサイズのバイト数
+ 射影された属性（存在する場合）のサイズのバイト数
+ インデックス項目あたり 100 バイトのオーバーヘッド

ローカルセカンダリインデックスに必要なストレージは、インデックス内の 1 項目の平均サイズの見積もり値に、インデックス内の項目数を掛けて見積もることができます。

特定の属性が定義されていない項目がテーブルに含まれており、その属性がインデックスソートキーとして定義されている場合、DynamoDB はその項目に関連するデータをインデックスに書き込みません。

## ローカルセカンダリインデックス内の項目コレクション
<a name="LSI.ItemCollections"></a>

**注記**  
このセクションは、ローカルセカンダリインデックスがあるテーブルだけに関係します。

DynamoDB において、*項目コレクション*とは、テーブルおよびそのローカルセカンダリインデックス全体で同じパーティションキーの値を持つ項目のグループです。このセクションで使用する例では、`Thread` テーブルのパーティションキーは `ForumName` で、`LastPostIndex` のパーティションキーも `ForumName` です。同じ `ForumName` を持つテーブルとインデックス項目は、すべて同じ項目コレクションの一部です。例えば、`Thread` テーブルと `LastPostIndex` ローカルセカンダリインデックスには、フォーラム `EC2` 用の項目コレクションと、フォーラム `RDS` 用の別の項目コレクションがあります。

次の図は、フォーラム `S3` の項目コレクションを示しています。

![\[テーブル、および同じパーティションキー値 S3 を持つローカルセカンダリインデックスを含む DynamoDB 項目のコレクション。　\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/LSI_04.png)


この図では、項目コレクションは、`Thread` および `LastPostIndex` 内のすべての項目で構成されていて、`ForumName` パーティションキーの値は「S3」です。テーブルにその他のローカルセカンダリインデックスがあった場合には、`ForumName` が「S3」であるそれらのインデックス内の項目も、項目コレクションの一部になります。

DynamoDB では次のオペレーションのいずれかを使用して、項目コレクションに関する情報を返すことができます。
+ `BatchWriteItem`
+ `DeleteItem`
+ `PutItem`
+ `UpdateItem`
+ `TransactWriteItems`

これらのオペレーションでは、それぞれ `ReturnItemCollectionMetrics` パラメータがサポートされています。このパラメータを `SIZE` に設定すると、インデックス内の各項目コレクションのサイズに関する情報が表示されます。

**Example**  
以下に、`UpdateItem` が `Thread` に設定されている、`ReturnItemCollectionMetrics` テーブルでの `SIZE` オペレーションの出力の例を示します。更新された項目には `ForumName` 値「EC2」があったため、出力にはその項目コレクションに関する情報が含まれています。  

```
{
    ItemCollectionMetrics: {
        ItemCollectionKey: {
            ForumName: "EC2"
        },
        SizeEstimateRangeGB: [0.0, 1.0]
    }
}
```
`SizeEstimateRangeGB` オブジェクトは、この項目コレクションのサイズが 0 GB と 1 GB の間であることを示します。DynamoDB では、このサイズ見積りが定期的に更新されるため、項目を変更した時には次回の数字が異なる場合があります。

### 項目コレクションのサイズ制限
<a name="LSI.ItemCollections.SizeLimit"></a>

1 つ以上のローカルセカンダリインデックスを含む項目コレクションのサイズは、すべて最大 10 GB です。これは、ローカルセカンダリインデックスのないテーブルの項目コレクションには適用されません。また、グローバルセカンダリインデックスの項目コレクションにも適用されません。1 つ以上のローカルセカンダリインデックスを含むテーブルのみに影響します。

項目コレクションが 10 GB の制限を超えると、DynamoDB は `ItemCollectionSizeLimitExceededException` を返す可能性があります。この場合、その項目コレクションに項目を追加したり、項目サイズを大きくしたりすることができなくなる場合があります。（項目コレクションのサイズを小さくする読み込み/書き込みオペレーションは、引き続き実行できます)。その他の項目コレクションには項目を追加することができます。

項目コレクションのサイズを小さくするには、次のいずれかを実行します。
+ 問題になっているパーティションキーの値を持つ不要な項目を削除します。ベーステーブルからこれらの項目を削除すると、DynamoDB では同じパーティションキーの値を持つインデックスエントリも削除されます。
+ 属性を削除するか属性のサイズを小さくすることで、項目を更新します。これらの属性がローカルセカンダリインデックスに射影されている場合には、DynamoDB では対応するインデックスエントリのサイズも小さくなります。
+ 同じパーティションキーおよびソートキーを使用して新しいテーブルを作成し、古いテーブルから新しいテーブルに項目を移動します。これは、頻繁にアクセスされない履歴データがテーブルに含まれている場合に適した方法です。また、この履歴データを Amazon Simple Storage Service (Amazon S3) にアーカイブすることを検討することもできます。

項目コレクションの合計サイズが 10 GB 未満になると、再び同じパーティションキーの値を使用して項目を追加できます。

項目コレクションのサイズを監視するようにアプリケーションを設定することをお勧めします。1 つの方法としては、`ReturnItemCollectionMetrics`、`SIZE`、`BatchWriteItem`、または `DeleteItem` を使用する場合に、`PutItem` パラメータを `UpdateItem` に設定するというものがあります。アプリケーションで出力内の `ReturnItemCollectionMetrics` オブジェクトを調査し、項目コレクションがユーザー定義の制限（たとえば 8 GB）を超えた場合にエラーメッセージを記録するようにします。制限を 10 GB より低く設定すれば早期警告システムになり、項目コレクションが上限に達する前に余裕をもって何らかの対処をとることができます。

### 項目コレクションおよびパーティション
<a name="LSI.ItemCollections.OnePartition"></a>

1 つ以上のローカルセカンダリインデックスを含むテーブルでは、各項目コレクションは 1 つのパーティションに保存されます。このような項目コレクションの合計サイズは、そのパーティションのキャパシティー (10 GB) に制限されます。データモデルにサイズに制限のない項目コレクションが含まれている場合や、一部の項目コレクションが将来に 10 GB を超えると合理的に予期されるアプリケーションでは、代わりにグローバルセカンダリインデックスの使用を検討します。

アプリケーションを設計する場合は、テーブルデータが異なるパーティションキー値に均一に分配されるようにする必要があります。ローカルセカンダリインデックス を持つテーブルについては、単一のパーティション内の単一の項目コレクションに読み込み/書き込みアクティビティの「ホットスポット」が作られないように、アプリケーションを設計します。

# ローカルセカンダリインデックスの操作: Java
<a name="LSIJavaDocumentAPI"></a>

AWS SDK for Java ドキュメント API を使用して、1 つ以上のローカルセカンダリインデックスを持つ Amazon DynamoDB テーブルを作成し、テーブルのインデックスを記述し、インデックスを使用してクエリを実行できます。

以下に、AWS SDK for Java ドキュメント API を使用したテーブルオペレーションの一般的な手順を示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. 対応するリクエストオブジェクトを作成して、オペレーションについて必要なパラメータとオプションパラメータを入力します。

1. 前のステップで作成したクライアントが提供する適切なメソッドを呼び出します。

**Topics**
+ [ローカルセカンダリインデックスを持つテーブルを作成する](#LSIJavaDocumentAPI.CreateTableWithIndex)
+ [ローカルセカンダリインデックスを持つテーブルの説明](#LSIJavaDocumentAPI.DescribeTableWithIndex)
+ [ローカルセカンダリインデックスのクエリ](#LSIJavaDocumentAPI.QueryAnIndex)
+ [例: Java ドキュメント API を使用したローカルセカンダリインデックス](LSIJavaDocumentAPI.Example.md)

## ローカルセカンダリインデックスを持つテーブルを作成する
<a name="LSIJavaDocumentAPI.CreateTableWithIndex"></a>

ローカルセカンダリインデックスは、テーブルの作成と同時に作成する必要があります。これを行うには、`createTable` メソッドを使用し、1 つ以上のローカルセカンダリインデックスの仕様を指定します。次の Java コード例では、ミュージックコレクション内の曲に関する情報を保持するためのテーブルを作成しています。パーティションキーは `Artist` で、ソートキーは `SongTitle` です。セカンダリインデックス `AlbumTitleIndex` は、アルバムタイトルによるクエリを容易にします。

DynamoDB ドキュメント API を使用して、ローカルセカンダリインデックスを持つテーブルを作成する手順を次に示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. リクエスト情報を指定する `CreateTableRequest` クラスのインスタンスを作成します。

   テーブル名、プライマリキー、およびプロビジョニングされたスループット値を指定する必要があります。ローカルセカンダリインデックスの場合は、インデックス名、インデックスソートキーの名前とデータ型、インデックスのキースキーマ、および属性射影を指定する必要があります。

1. リクエストオブジェクトをパラメータとして指定して、`createTable` メソッドを呼び出します。

以下の Java コード例は、前述のステップの例です。このコードは、`AlbumTitle` 属性に関するセカンダリインデックスを持つテーブル (`Music`) を作成します。テーブルパーティションキーとソートキー、およびインデックスソートキーのみが、インデックスに射影される属性です。

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

String tableName = "Music";

CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName);

//ProvisionedThroughput
createTableRequest.setProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits((long)5).withWriteCapacityUnits((long)5));

//AttributeDefinitions
ArrayList<AttributeDefinition> attributeDefinitions= new ArrayList<AttributeDefinition>();
attributeDefinitions.add(new AttributeDefinition().withAttributeName("Artist").withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition().withAttributeName("SongTitle").withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition().withAttributeName("AlbumTitle").withAttributeType("S"));

createTableRequest.setAttributeDefinitions(attributeDefinitions);

//KeySchema
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
tableKeySchema.add(new KeySchemaElement().withAttributeName("Artist").withKeyType(KeyType.HASH));  //Partition key
tableKeySchema.add(new KeySchemaElement().withAttributeName("SongTitle").withKeyType(KeyType.RANGE));  //Sort key

createTableRequest.setKeySchema(tableKeySchema);

ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>();
indexKeySchema.add(new KeySchemaElement().withAttributeName("Artist").withKeyType(KeyType.HASH));  //Partition key
indexKeySchema.add(new KeySchemaElement().withAttributeName("AlbumTitle").withKeyType(KeyType.RANGE));  //Sort key

Projection projection = new Projection().withProjectionType(ProjectionType.INCLUDE);
ArrayList<String> nonKeyAttributes = new ArrayList<String>();
nonKeyAttributes.add("Genre");
nonKeyAttributes.add("Year");
projection.setNonKeyAttributes(nonKeyAttributes);

LocalSecondaryIndex localSecondaryIndex = new LocalSecondaryIndex()
    .withIndexName("AlbumTitleIndex").withKeySchema(indexKeySchema).withProjection(projection);

ArrayList<LocalSecondaryIndex> localSecondaryIndexes = new ArrayList<LocalSecondaryIndex>();
localSecondaryIndexes.add(localSecondaryIndex);
createTableRequest.setLocalSecondaryIndexes(localSecondaryIndexes);

Table table = dynamoDB.createTable(createTableRequest);
System.out.println(table.getDescription());
```

DynamoDB がテーブルを作成し、テーブルのステータスを `ACTIVE` に設定するまで待機する必要があります。その後、テーブルへのデータ項目の入力を開始できます。

## ローカルセカンダリインデックスを持つテーブルの説明
<a name="LSIJavaDocumentAPI.DescribeTableWithIndex"></a>

テーブルのローカルセカンダリインデックスに関する情報を取得するには、`describeTable` メソッドを使用します。インデックスごとに、名前、キースキーマ、および射影された属性にアクセスできます。

AWS SDK for Java ドキュメント API を使用して、テーブルのローカルセカンダリインデックス情報にアクセスするためのステップを次に示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. `Table` クラスのインスタンスを作成します。テーブル名を入力する必要があります。

1. `Table` オブジェクトの `describeTable` メソッドを呼び出します。

以下の Java コード例は、前述のステップの例です。

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

String tableName = "Music";

Table table = dynamoDB.getTable(tableName);

TableDescription tableDescription = table.describe();

List<LocalSecondaryIndexDescription> localSecondaryIndexes 
    = tableDescription.getLocalSecondaryIndexes();

// This code snippet will work for multiple indexes, even though
// there is only one index in this example.

Iterator<LocalSecondaryIndexDescription> lsiIter = localSecondaryIndexes.iterator();
while (lsiIter.hasNext()) {

    LocalSecondaryIndexDescription lsiDescription = lsiIter.next();
    System.out.println("Info for index " + lsiDescription.getIndexName() + ":");
    Iterator<KeySchemaElement> kseIter = lsiDescription.getKeySchema().iterator();
    while (kseIter.hasNext()) {
        KeySchemaElement kse = kseIter.next();
        System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType());
    }
    Projection projection = lsiDescription.getProjection();
    System.out.println("\tThe projection type is: " + projection.getProjectionType());
    if (projection.getProjectionType().toString().equals("INCLUDE")) {
        System.out.println("\t\tThe non-key projected attributes are: " + projection.getNonKeyAttributes());
    }
}
```

## ローカルセカンダリインデックスのクエリ
<a name="LSIJavaDocumentAPI.QueryAnIndex"></a>

テーブルに `Query` を実行するのとほぼ同じ方法で、ローカルセカンダリインデックスに対する `Query` オペレーションを使用することができます。インデックス名、インデックスソートキーのクエリ条件、および返す属性を指定する必要があります。この例では、インデックスは `AlbumTitleIndex`、インデックスソートキーは `AlbumTitle` です。

返される属性は、インデックスに射影された属性だけです。このクエリを変更して非キー属性を選択することもできますが、これには比較的コストのかかるテーブルフェッチアクティビティが必要です。テーブルのフェッチの詳細については、「[属性の射影](LSI.md#LSI.Projections)」を参照してください。

AWS SDK for Java ドキュメント API を使用してローカルセカンダリインデックスをクエリする手順を次に示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. `Table` クラスのインスタンスを作成します。テーブル名を入力する必要があります。

1. `Index` クラスのインスタンスを作成します。インデックス名を入力する必要があります。

1. `Index` クラスの `query` メソッドを呼び出します。

以下の Java コード例は、前述のステップの例です。

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

String tableName = "Music";

Table table = dynamoDB.getTable(tableName);
Index index = table.getIndex("AlbumTitleIndex");

QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("Artist = :v_artist and AlbumTitle = :v_title")
    .withValueMap(new ValueMap()
        .withString(":v_artist", "Acme Band")
        .withString(":v_title", "Songs About Life"));

ItemCollection<QueryOutcome> items = index.query(spec);

Iterator<Item> itemsIter = items.iterator();

while (itemsIter.hasNext()) {
    Item item = itemsIter.next();
    System.out.println(item.toJSONPretty());
}
```

### ローカルセカンダリインデックスの整合性のある読み取り
<a name="LSIJavaDocumentAPI.ConsistentReads"></a>

結果整合性のある読み込みのみをサポートするグローバルセカンダリインデックスとは異なり、ローカルセカンダリインデックスは結果整合性のある読み込みと強力な整合性のある読み込みの両方をサポートします。ローカルセカンダリインデックスからの完全整合性の読み込みでは、常に最新の更新された値が返されます。クエリがベーステーブルからさらに属性をフェッチする必要がある場合、それらのフェッチされた属性もインデックスについて整合性があることになります。

デフォルトでは、`Query` は結果整合性のある読み込みを使用します。強力な整合性のある読み込みをリクエストするには、`QuerySpec` で `ConsistentRead` を `true` に設定します。次の例では、強力な整合性のある読み込みを使用して `AlbumTitleIndex` をクエリを実行します。

**Example**  

```
QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("Artist = :v_artist and AlbumTitle = :v_title")
    .withValueMap(new ValueMap()
        .withString(":v_artist", "Acme Band")
        .withString(":v_title", "Songs About Life"))
    .withConsistentRead(true);
```

**注記**  
強力な整合性のある読み込みでは、返されるデータ 4 KB ごとに 1 つの読み込みキャパシティーユニット (切り上げ) が消費されますが、結果整合性のある読み込みではその半分が消費されます。例えば、9 KB のデータを返す強力な整合性のある読み込みでは、3 つの読み込みキャパシティーユニット (9 KB / 4 KB = 2.25、3 に切り上げ) が消費されますが、結果整合性のある読み込みを使用した同じクエリでは、1.5 の読み込みキャパシティーユニットが消費されます。アプリケーションがわずかに古くなる可能性のあるデータの読み取りを許容できる場合は、結果整合性のある読み込みを使用して読み込みキャパシティーの使用量を減らします。詳細については、「[読み込みキャパシティユニット](LSI.md#LSI.ThroughputConsiderations.Reads)」を参照してください。

# 例: Java ドキュメント API を使用したローカルセカンダリインデックス
<a name="LSIJavaDocumentAPI.Example"></a>

次の Java コード例は、Amazon DynamoDB でローカルセカンダリインデックスを操作する方法を示しています。この例では、パーティションキーを `CustomerId` として 、ソートキーを `OrderId` として `CustomerOrders` という名前のテーブルを作成しています。このテーブルには、次の 2 つのローカルセカンダリインデックスがあります。
+ `OrderCreationDateIndex` – ソートキーは `OrderCreationDate` です。次の属性がインデックスに射影されます。
  + `ProductCategory`
  + `ProductName`
  + `OrderStatus`
  + `ShipmentTrackingId`
+ `IsOpenIndex` – ソートキーは `IsOpen` です。すべてのテーブル属性がインデックスに射影されます。

`CustomerOrders` テーブルが作成されると、プログラムは顧客の注文を表すデータをテーブルにロードします。次に、ローカルセカンダリインデックスを使用してデータにクエリを実行します。最後に、プログラムは `CustomerOrders` テーブルを削除します。

以下の例をテストするための詳細な手順については、「[Java コードの例](CodeSamples.Java.md)」を参照してください。

**Example**  

```
package com.example.dynamodb;

import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.waiters.DynamoDbWaiter;

import java.util.HashMap;
import java.util.Map;

public class DocumentAPILocalSecondaryIndexExample {

    static DynamoDbClient client = DynamoDbClient.create();
    public static String tableName = "CustomerOrders";

    public static void main(String[] args) {
        createTable();
        loadData();
        query(null);
        query("IsOpenIndex");
        query("OrderCreationDateIndex");
        deleteTable(tableName);
    }

    public static void createTable() {
        CreateTableRequest request = CreateTableRequest.builder()
            .tableName(tableName)
            .provisionedThroughput(ProvisionedThroughput.builder()
                .readCapacityUnits(1L)
                .writeCapacityUnits(1L)
                .build())
            .attributeDefinitions(
                AttributeDefinition.builder().attributeName("CustomerId").attributeType(ScalarAttributeType.S).build(),
                AttributeDefinition.builder().attributeName("OrderId").attributeType(ScalarAttributeType.N).build(),
                AttributeDefinition.builder().attributeName("OrderCreationDate").attributeType(ScalarAttributeType.N).build(),
                AttributeDefinition.builder().attributeName("IsOpen").attributeType(ScalarAttributeType.N).build())
            .keySchema(
                KeySchemaElement.builder().attributeName("CustomerId").keyType(KeyType.HASH).build(),
                KeySchemaElement.builder().attributeName("OrderId").keyType(KeyType.RANGE).build())
            .localSecondaryIndexes(
                LocalSecondaryIndex.builder()
                    .indexName("OrderCreationDateIndex")
                    .keySchema(
                        KeySchemaElement.builder().attributeName("CustomerId").keyType(KeyType.HASH).build(),
                        KeySchemaElement.builder().attributeName("OrderCreationDate").keyType(KeyType.RANGE).build())
                    .projection(Projection.builder()
                        .projectionType(ProjectionType.INCLUDE)
                        .nonKeyAttributes("ProductCategory", "ProductName")
                        .build())
                    .build(),
                LocalSecondaryIndex.builder()
                    .indexName("IsOpenIndex")
                    .keySchema(
                        KeySchemaElement.builder().attributeName("CustomerId").keyType(KeyType.HASH).build(),
                        KeySchemaElement.builder().attributeName("IsOpen").keyType(KeyType.RANGE).build())
                    .projection(Projection.builder()
                        .projectionType(ProjectionType.ALL)
                        .build())
                    .build())
            .build();

        System.out.println("Creating table " + tableName + "...");
        client.createTable(request);

        try (DynamoDbWaiter waiter = client.waiter()) {
            WaiterResponse<DescribeTableResponse> response = waiter.waitUntilTableExists(r -> r.tableName(tableName));
            response.matched().response().ifPresent(System.out::println);
        }
    }

    public static void query(String indexName) {
        System.out.println("\n***********************************************************\n");
        System.out.println("Querying table " + tableName + "...");

        if ("IsOpenIndex".equals(indexName)) {
            System.out.println("\nUsing index: '" + indexName + "': Bob's orders that are open.");
            System.out.println("Only a user-specified list of attributes are returned\n");

            Map<String, AttributeValue> values = new HashMap<>();
            values.put(":v_custid", AttributeValue.builder().s("bob@example.com").build());
            values.put(":v_isopen", AttributeValue.builder().n("1").build());

            QueryRequest request = QueryRequest.builder()
                .tableName(tableName)
                .indexName(indexName)
                .keyConditionExpression("CustomerId = :v_custid and IsOpen = :v_isopen")
                .expressionAttributeValues(values)
                .projectionExpression("OrderCreationDate, ProductCategory, ProductName, OrderStatus")
                .build();

            System.out.println("Query: printing results...");
            client.query(request).items().forEach(System.out::println);

        } else if ("OrderCreationDateIndex".equals(indexName)) {
            System.out.println("\nUsing index: '" + indexName + "': Bob's orders that were placed after 01/31/2015.");
            System.out.println("Only the projected attributes are returned\n");

            Map<String, AttributeValue> values = new HashMap<>();
            values.put(":v_custid", AttributeValue.builder().s("bob@example.com").build());
            values.put(":v_orddate", AttributeValue.builder().n("20150131").build());

            QueryRequest request = QueryRequest.builder()
                .tableName(tableName)
                .indexName(indexName)
                .keyConditionExpression("CustomerId = :v_custid and OrderCreationDate >= :v_orddate")
                .expressionAttributeValues(values)
                .select(Select.ALL_PROJECTED_ATTRIBUTES)
                .build();

            System.out.println("Query: printing results...");
            client.query(request).items().forEach(System.out::println);

        } else {
            System.out.println("\nNo index: All of Bob's orders, by OrderId:\n");

            Map<String, AttributeValue> values = new HashMap<>();
            values.put(":v_custid", AttributeValue.builder().s("bob@example.com").build());

            QueryRequest request = QueryRequest.builder()
                .tableName(tableName)
                .keyConditionExpression("CustomerId = :v_custid")
                .expressionAttributeValues(values)
                .build();

            System.out.println("Query: printing results...");
            client.query(request).items().forEach(System.out::println);
        }
    }

    public static void deleteTable(String tableName) {
        System.out.println("Deleting table " + tableName + "...");
        client.deleteTable(DeleteTableRequest.builder().tableName(tableName).build());

        try (DynamoDbWaiter waiter = client.waiter()) {
            waiter.waitUntilTableNotExists(r -> r.tableName(tableName));
        }
    }

    public static void loadData() {
        System.out.println("Loading data into table " + tableName + "...");

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("alice@example.com").build(),
            "OrderId", AttributeValue.builder().n("1").build(),
            "IsOpen", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150101").build(),
            "ProductCategory", AttributeValue.builder().s("Book").build(),
            "ProductName", AttributeValue.builder().s("The Great Outdoors").build(),
            "OrderStatus", AttributeValue.builder().s("PACKING ITEMS").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("alice@example.com").build(),
            "OrderId", AttributeValue.builder().n("2").build(),
            "IsOpen", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150221").build(),
            "ProductCategory", AttributeValue.builder().s("Bike").build(),
            "ProductName", AttributeValue.builder().s("Super Mountain").build(),
            "OrderStatus", AttributeValue.builder().s("ORDER RECEIVED").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("alice@example.com").build(),
            "OrderId", AttributeValue.builder().n("3").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150304").build(),
            "ProductCategory", AttributeValue.builder().s("Music").build(),
            "ProductName", AttributeValue.builder().s("A Quiet Interlude").build(),
            "OrderStatus", AttributeValue.builder().s("IN TRANSIT").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("176493").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150111").build(),
            "ProductCategory", AttributeValue.builder().s("Movie").build(),
            "ProductName", AttributeValue.builder().s("Calm Before The Storm").build(),
            "OrderStatus", AttributeValue.builder().s("SHIPPING DELAY").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("859323").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("2").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150124").build(),
            "ProductCategory", AttributeValue.builder().s("Music").build(),
            "ProductName", AttributeValue.builder().s("E-Z Listening").build(),
            "OrderStatus", AttributeValue.builder().s("DELIVERED").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("756943").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("3").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150221").build(),
            "ProductCategory", AttributeValue.builder().s("Music").build(),
            "ProductName", AttributeValue.builder().s("Symphony 9").build(),
            "OrderStatus", AttributeValue.builder().s("DELIVERED").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("645193").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("4").build(),
            "IsOpen", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150222").build(),
            "ProductCategory", AttributeValue.builder().s("Hardware").build(),
            "ProductName", AttributeValue.builder().s("Extra Heavy Hammer").build(),
            "OrderStatus", AttributeValue.builder().s("PACKING ITEMS").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("5").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150309").build(),
            "ProductCategory", AttributeValue.builder().s("Book").build(),
            "ProductName", AttributeValue.builder().s("How To Cook").build(),
            "OrderStatus", AttributeValue.builder().s("IN TRANSIT").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("440185").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("6").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150318").build(),
            "ProductCategory", AttributeValue.builder().s("Luggage").build(),
            "ProductName", AttributeValue.builder().s("Really Big Suitcase").build(),
            "OrderStatus", AttributeValue.builder().s("DELIVERED").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("893927").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("7").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150324").build(),
            "ProductCategory", AttributeValue.builder().s("Golf").build(),
            "ProductName", AttributeValue.builder().s("PGA Pro II").build(),
            "OrderStatus", AttributeValue.builder().s("OUT FOR DELIVERY").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("383283").build()));
    }

    private static void putItem(Map<String, AttributeValue> item) {
        client.putItem(PutItemRequest.builder().tableName(tableName).item(item).build());
    }
}
```

# ローカルセカンダリインデックスの操作: .NET
<a name="LSILowLevelDotNet"></a>

**Topics**
+ [ローカルセカンダリインデックスを持つテーブルを作成する](#LSILowLevelDotNet.CreateTableWithIndex)
+ [ローカルセカンダリインデックスを持つテーブルの説明](#LSILowLevelDotNet.DescribeTableWithIndex)
+ [ローカルセカンダリインデックスのクエリ](#LSILowLevelDotNet.QueryAnIndex)
+ [例: AWS SDK for .NET 低レベル API を使用したローカルセカンダリインデックス](LSILowLevelDotNet.Example.md)

AWS SDK for .NET 低レベル API を使用して、1 つ以上のローカルセカンダリインデックスを含む Amazon DynamoDB テーブルを作成し、テーブルのインデックスを記述し、インデックスを使用してクエリを実行できます。これらのオペレーションは、対応する低レベル DynamoDB API アクションにマッピングされます。詳細については、「[.NET コード例](CodeSamples.DotNet.md)」を参照してください。

以下に、.NET 低レベル API を使用したテーブルオペレーションの一般的な手順を示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. 対応するリクエストオブジェクトを作成して、オペレーションについて必要なパラメータとオプションパラメータを入力します。

   例えば、テーブルを作成するには `CreateTableRequest` オブジェクトを作成し、テーブルやインデックスにクエリを実行するには `QueryRequest` オブジェクトを作成します。

1. 前述のステップで作成したクライアントから提供された適切なメソッドを実行します。

## ローカルセカンダリインデックスを持つテーブルを作成する
<a name="LSILowLevelDotNet.CreateTableWithIndex"></a>

ローカルセカンダリインデックスは、テーブルの作成と同時に作成する必要があります。これを行うには、`CreateTable` を使用し、1 つ以上のローカルセカンダリインデックスの仕様を指定します。次の C\$1 コード例では、ミュージックコレクション内の曲に関する情報を保持するためのテーブルを作成しています。パーティションキーは `Artist` で、ソートキーは `SongTitle` です。セカンダリインデックス `AlbumTitleIndex` は、アルバムタイトルによるクエリを容易にします。

.NET 低レベル API を使用して、ローカルセカンダリインデックスを含むテーブルを作成する手順を次に示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. リクエスト情報を指定する `CreateTableRequest` クラスのインスタンスを作成します。

   テーブル名、プライマリキー、およびプロビジョニングされたスループット値を指定する必要があります。ローカルセカンダリインデックスの場合は、インデックス名、インデックスソートキーの名前とデータ型、インデックスのキースキーマ、および属性射影を指定する必要があります。

1. リクエストオブジェクトをパラメータとして指定して、`CreateTable` メソッドを実行します。

以下の C\$1 サンプルコードは、前述のステップの例です。このコードは、`AlbumTitle` 属性に関するセカンダリインデックスを持つテーブル (`Music`) を作成します。テーブルパーティションキーとソートキー、およびインデックスソートキーのみが、インデックスに射影される属性です。

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "Music";

CreateTableRequest createTableRequest = new CreateTableRequest()
{
    TableName = tableName
};

//ProvisionedThroughput
createTableRequest.ProvisionedThroughput = new ProvisionedThroughput()
{
    ReadCapacityUnits = (long)5,
    WriteCapacityUnits = (long)5
};

//AttributeDefinitions
List<AttributeDefinition> attributeDefinitions = new List<AttributeDefinition>();

attributeDefinitions.Add(new AttributeDefinition()
{
    AttributeName = "Artist",
    AttributeType = "S"
});

attributeDefinitions.Add(new AttributeDefinition()
 {
     AttributeName = "SongTitle",
     AttributeType = "S"
 });

attributeDefinitions.Add(new AttributeDefinition()
 {
     AttributeName = "AlbumTitle",
     AttributeType = "S"
 });

createTableRequest.AttributeDefinitions = attributeDefinitions;

//KeySchema
List<KeySchemaElement> tableKeySchema = new List<KeySchemaElement>();

tableKeySchema.Add(new KeySchemaElement() { AttributeName = "Artist", KeyType = "HASH" });  //Partition key
tableKeySchema.Add(new KeySchemaElement() { AttributeName = "SongTitle", KeyType = "RANGE" });  //Sort key

createTableRequest.KeySchema = tableKeySchema;

List<KeySchemaElement> indexKeySchema = new List<KeySchemaElement>();
indexKeySchema.Add(new KeySchemaElement() { AttributeName = "Artist", KeyType = "HASH" });  //Partition key
indexKeySchema.Add(new KeySchemaElement() { AttributeName = "AlbumTitle", KeyType = "RANGE" });  //Sort key

Projection projection = new Projection() { ProjectionType = "INCLUDE" };

List<string> nonKeyAttributes = new List<string>();
nonKeyAttributes.Add("Genre");
nonKeyAttributes.Add("Year");
projection.NonKeyAttributes = nonKeyAttributes;

LocalSecondaryIndex localSecondaryIndex = new LocalSecondaryIndex()
{
    IndexName = "AlbumTitleIndex",
    KeySchema = indexKeySchema,
    Projection = projection
};

List<LocalSecondaryIndex> localSecondaryIndexes = new List<LocalSecondaryIndex>();
localSecondaryIndexes.Add(localSecondaryIndex);
createTableRequest.LocalSecondaryIndexes = localSecondaryIndexes;

CreateTableResponse result = client.CreateTable(createTableRequest);
Console.WriteLine(result.CreateTableResult.TableDescription.TableName);
Console.WriteLine(result.CreateTableResult.TableDescription.TableStatus);
```

DynamoDB がテーブルを作成し、テーブルのステータスを `ACTIVE` に設定するまで待機する必要があります。その後、テーブルへのデータ項目の入力を開始できます。

## ローカルセカンダリインデックスを持つテーブルの説明
<a name="LSILowLevelDotNet.DescribeTableWithIndex"></a>

テーブルのローカルセカンダリインデックスに関する情報を取得するには、`DescribeTable` API を使用します。インデックスごとに、名前、キースキーマ、および射影された属性にアクセスできます。

.NET 低レベル API を使用してテーブルのローカルセカンダリインデックス情報にアクセスするには次の手順に従います。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. リクエスト情報を指定する `DescribeTableRequest` クラスのインスタンスを作成します。テーブル名を入力する必要があります。

1. リクエストオブジェクトをパラメータとして指定して、`describeTable` メソッドを実行します。

以下の C\$1 サンプルコードは、前述のステップの例です。

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "Music";

DescribeTableResponse response = client.DescribeTable(new DescribeTableRequest() { TableName = tableName });
List<LocalSecondaryIndexDescription> localSecondaryIndexes =
    response.DescribeTableResult.Table.LocalSecondaryIndexes;

// This code snippet will work for multiple indexes, even though
// there is only one index in this example.
foreach (LocalSecondaryIndexDescription lsiDescription in localSecondaryIndexes)
{
    Console.WriteLine("Info for index " + lsiDescription.IndexName + ":");

    foreach (KeySchemaElement kse in lsiDescription.KeySchema)
    {
        Console.WriteLine("\t" + kse.AttributeName + ": key type is " + kse.KeyType);
    }

    Projection projection = lsiDescription.Projection;

    Console.WriteLine("\tThe projection type is: " + projection.ProjectionType);

    if (projection.ProjectionType.ToString().Equals("INCLUDE"))
    {
        Console.WriteLine("\t\tThe non-key projected attributes are:");

        foreach (String s in projection.NonKeyAttributes)
        {
            Console.WriteLine("\t\t" + s);
        }

    }
}
```

## ローカルセカンダリインデックスのクエリ
<a name="LSILowLevelDotNet.QueryAnIndex"></a>

テーブルに `Query` を実行するのとほぼ同じ方法で、ローカルセカンダリインデックスに対する `Query` を使用することができます。インデックス名、インデックスソートキーのクエリ条件、および返す属性を指定する必要があります。この例では、インデックスは `AlbumTitleIndex`、インデックスソートキーは `AlbumTitle` です。

返される属性は、インデックスに射影された属性だけです。このクエリを変更して非キー属性を選択することもできますが、これには比較的コストのかかるテーブルフェッチアクティビティが必要です。テーブルのフェッチの詳細については、「[属性の射影](LSI.md#LSI.Projections)」を参照してください。

.NET 低レベル API を使用して、ローカルセカンダリインデックスを含むテーブルにクエリを実行する手順を次に示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. リクエスト情報を指定する `QueryRequest` クラスのインスタンスを作成します。

1. リクエストオブジェクトをパラメータとして指定して、`query` メソッドを実行します。

以下の C\$1 サンプルコードは、前述のステップの例です。

**Example**  

```
QueryRequest queryRequest = new QueryRequest
{
    TableName = "Music",
    IndexName = "AlbumTitleIndex",
    Select = "ALL_ATTRIBUTES",
    ScanIndexForward = true,
    KeyConditionExpression = "Artist = :v_artist and AlbumTitle = :v_title",
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":v_artist",new AttributeValue {S = "Acme Band"}},
        {":v_title",new AttributeValue {S = "Songs About Life"}}
    },
};

QueryResponse response = client.Query(queryRequest);

foreach (var attribs in response.Items)
{
    foreach (var attrib in attribs)
    {
        Console.WriteLine(attrib.Key + " ---> " + attrib.Value.S);
    }
    Console.WriteLine();
}
```

# 例: AWS SDK for .NET 低レベル API を使用したローカルセカンダリインデックス
<a name="LSILowLevelDotNet.Example"></a>

次の C\$1 コード例は、Amazon DynamoDB でローカルセカンダリインデックスを操作する方法を示しています。この例では、パーティションキーを `CustomerId` として 、ソートキーを `OrderId` として `CustomerOrders` という名前のテーブルを作成しています。このテーブルには、次の 2 つのローカルセカンダリインデックスがあります。
+ `OrderCreationDateIndex` – ソートキーは `OrderCreationDate` です。次の属性がインデックスに射影されます。
  + `ProductCategory`
  + `ProductName`
  + `OrderStatus`
  + `ShipmentTrackingId`
+ `IsOpenIndex` – ソートキーは `IsOpen` です。すべてのテーブル属性がインデックスに射影されます。

`CustomerOrders` テーブルが作成されると、プログラムは顧客の注文を表すデータをテーブルにロードします。次に、ローカルセカンダリインデックスを使用してデータにクエリを実行します。最後に、プログラムは `CustomerOrders` テーブルを削除します。

以下の例をテストするための詳細な手順については、「[.NET コード例](CodeSamples.DotNet.md)」を参照してください。

**Example**  

```
using System;
using System.Collections.Generic;
using System.Linq;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;
using Amazon.SecurityToken;

namespace com.amazonaws.codesamples
{
    class LowLevelLocalSecondaryIndexExample
    {
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();
        private static string tableName = "CustomerOrders";

        static void Main(string[] args)
        {
            try
            {
                CreateTable();
                LoadData();

                Query(null);
                Query("IsOpenIndex");
                Query("OrderCreationDateIndex");

                DeleteTable(tableName);

                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
            catch (AmazonDynamoDBException e) { Console.WriteLine(e.Message); }
            catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
            catch (Exception e) { Console.WriteLine(e.Message); }
        }

        private static void CreateTable()
        {
            var createTableRequest =
                new CreateTableRequest()
                {
                    TableName = tableName,
                    ProvisionedThroughput =
                    new ProvisionedThroughput()
                    {
                        ReadCapacityUnits = (long)1,
                        WriteCapacityUnits = (long)1
                    }
                };

            var attributeDefinitions = new List<AttributeDefinition>()
        {
            // Attribute definitions for table primary key
            { new AttributeDefinition() {
                  AttributeName = "CustomerId", AttributeType = "S"
              } },
            { new AttributeDefinition() {
                  AttributeName = "OrderId", AttributeType = "N"
              } },
            // Attribute definitions for index primary key
            { new AttributeDefinition() {
                  AttributeName = "OrderCreationDate", AttributeType = "N"
              } },
            { new AttributeDefinition() {
                  AttributeName = "IsOpen", AttributeType = "N"
              }}
        };

            createTableRequest.AttributeDefinitions = attributeDefinitions;

            // Key schema for table
            var tableKeySchema = new List<KeySchemaElement>()
        {
            { new KeySchemaElement() {
                  AttributeName = "CustomerId", KeyType = "HASH"
              } },                                                  //Partition key
            { new KeySchemaElement() {
                  AttributeName = "OrderId", KeyType = "RANGE"
              } }                                                //Sort key
        };

            createTableRequest.KeySchema = tableKeySchema;

            var localSecondaryIndexes = new List<LocalSecondaryIndex>();

            // OrderCreationDateIndex
            LocalSecondaryIndex orderCreationDateIndex = new LocalSecondaryIndex()
            {
                IndexName = "OrderCreationDateIndex"
            };

            // Key schema for OrderCreationDateIndex
            var indexKeySchema = new List<KeySchemaElement>()
        {
            { new KeySchemaElement() {
                  AttributeName = "CustomerId", KeyType = "HASH"
              } },                                                    //Partition key
            { new KeySchemaElement() {
                  AttributeName = "OrderCreationDate", KeyType = "RANGE"
              } }                                                            //Sort key
        };

            orderCreationDateIndex.KeySchema = indexKeySchema;

            // Projection (with list of projected attributes) for
            // OrderCreationDateIndex
            var projection = new Projection()
            {
                ProjectionType = "INCLUDE"
            };

            var nonKeyAttributes = new List<string>()
        {
            "ProductCategory",
            "ProductName"
        };
            projection.NonKeyAttributes = nonKeyAttributes;

            orderCreationDateIndex.Projection = projection;

            localSecondaryIndexes.Add(orderCreationDateIndex);

            // IsOpenIndex
            LocalSecondaryIndex isOpenIndex
                = new LocalSecondaryIndex()
                {
                    IndexName = "IsOpenIndex"
                };

            // Key schema for IsOpenIndex
            indexKeySchema = new List<KeySchemaElement>()
        {
            { new KeySchemaElement() {
                  AttributeName = "CustomerId", KeyType = "HASH"
              }},                                                     //Partition key
            { new KeySchemaElement() {
                  AttributeName = "IsOpen", KeyType = "RANGE"
              }}                                                  //Sort key
        };

            // Projection (all attributes) for IsOpenIndex
            projection = new Projection()
            {
                ProjectionType = "ALL"
            };

            isOpenIndex.KeySchema = indexKeySchema;
            isOpenIndex.Projection = projection;

            localSecondaryIndexes.Add(isOpenIndex);

            // Add index definitions to CreateTable request
            createTableRequest.LocalSecondaryIndexes = localSecondaryIndexes;

            Console.WriteLine("Creating table " + tableName + "...");
            client.CreateTable(createTableRequest);
            WaitUntilTableReady(tableName);
        }

        public static void Query(string indexName)
        {
            Console.WriteLine("\n***********************************************************\n");
            Console.WriteLine("Querying table " + tableName + "...");

            QueryRequest queryRequest = new QueryRequest()
            {
                TableName = tableName,
                ConsistentRead = true,
                ScanIndexForward = true,
                ReturnConsumedCapacity = "TOTAL"
            };


            String keyConditionExpression = "CustomerId = :v_customerId";
            Dictionary<string, AttributeValue> expressionAttributeValues = new Dictionary<string, AttributeValue> {
            {":v_customerId", new AttributeValue {
                 S = "bob@example.com"
             }}
        };


            if (indexName == "IsOpenIndex")
            {
                Console.WriteLine("\nUsing index: '" + indexName
                          + "': Bob's orders that are open.");
                Console.WriteLine("Only a user-specified list of attributes are returned\n");
                queryRequest.IndexName = indexName;

                keyConditionExpression += " and IsOpen = :v_isOpen";
                expressionAttributeValues.Add(":v_isOpen", new AttributeValue
                {
                    N = "1"
                });

                // ProjectionExpression
                queryRequest.ProjectionExpression = "OrderCreationDate, ProductCategory, ProductName, OrderStatus";
            }
            else if (indexName == "OrderCreationDateIndex")
            {
                Console.WriteLine("\nUsing index: '" + indexName
                          + "': Bob's orders that were placed after 01/31/2013.");
                Console.WriteLine("Only the projected attributes are returned\n");
                queryRequest.IndexName = indexName;

                keyConditionExpression += " and OrderCreationDate > :v_Date";
                expressionAttributeValues.Add(":v_Date", new AttributeValue
                {
                    N = "20130131"
                });

                // Select
                queryRequest.Select = "ALL_PROJECTED_ATTRIBUTES";
            }
            else
            {
                Console.WriteLine("\nNo index: All of Bob's orders, by OrderId:\n");
            }
            queryRequest.KeyConditionExpression = keyConditionExpression;
            queryRequest.ExpressionAttributeValues = expressionAttributeValues;

            var result = client.Query(queryRequest);
            var items = result.Items;
            foreach (var currentItem in items)
            {
                foreach (string attr in currentItem.Keys)
                {
                    if (attr == "OrderId" || attr == "IsOpen"
                        || attr == "OrderCreationDate")
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].N);
                    }
                    else
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].S);
                    }
                }
                Console.WriteLine();
            }
            Console.WriteLine("\nConsumed capacity: " + result.ConsumedCapacity.CapacityUnits + "\n");
        }

        private static void DeleteTable(string tableName)
        {
            Console.WriteLine("Deleting table " + tableName + "...");
            client.DeleteTable(new DeleteTableRequest()
            {
                TableName = tableName
            });
            WaitForTableToBeDeleted(tableName);
        }

        public static void LoadData()
        {
            Console.WriteLine("Loading data into table " + tableName + "...");

            Dictionary<string, AttributeValue> item = new Dictionary<string, AttributeValue>();

            item["CustomerId"] = new AttributeValue
            {
                S = "alice@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "1"
            };
            item["IsOpen"] = new AttributeValue
            {
                N = "1"
            };
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130101"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Book"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "The Great Outdoors"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "PACKING ITEMS"
            };
            /* no ShipmentTrackingId attribute */
            PutItemRequest putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "alice@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "2"
            };
            item["IsOpen"] = new AttributeValue
            {
                N = "1"
            };
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130221"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Bike"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Super Mountain"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "ORDER RECEIVED"
            };
            /* no ShipmentTrackingId attribute */
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "alice@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "3"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130304"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Music"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "A Quiet Interlude"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "IN TRANSIT"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "176493"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "1"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130111"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Movie"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Calm Before The Storm"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "SHIPPING DELAY"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "859323"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "2"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130124"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Music"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "E-Z Listening"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "DELIVERED"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "756943"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "3"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130221"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Music"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Symphony 9"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "DELIVERED"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "645193"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "4"
            };
            item["IsOpen"] = new AttributeValue
            {
                N = "1"
            };
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130222"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Hardware"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Extra Heavy Hammer"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "PACKING ITEMS"
            };
            /* no ShipmentTrackingId attribute */
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "5"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130309"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Book"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "How To Cook"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "IN TRANSIT"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "440185"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "6"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130318"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Luggage"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Really Big Suitcase"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "DELIVERED"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "893927"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "7"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130324"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Golf"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "PGA Pro II"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "OUT FOR DELIVERY"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "383283"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);
        }

        private static void WaitUntilTableReady(string tableName)
        {
            string status = null;
            // Let us wait until table is created. Call DescribeTable.
            do
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                    status = res.Table.TableStatus;
                }
                catch (ResourceNotFoundException)
                {
                    // DescribeTable is eventually consistent. So you might
                    // get resource not found. So we handle the potential exception.
                }
            } while (status != "ACTIVE");
        }

        private static void WaitForTableToBeDeleted(string tableName)
        {
            bool tablePresent = true;

            while (tablePresent)
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                }
                catch (ResourceNotFoundException)
                {
                    tablePresent = false;
                }
            }
        }
    }
}
```

# DynamoDB AWS CLI でのローカルセカンダリインデックスの操作
<a name="LCICli"></a>

AWS CLI を使用して、1 つ以上のローカルセカンダリインデックスを含む Amazon DynamoDB テーブルを作成し、テーブルのインデックスを記述し、インデックスを使用してクエリを実行できます。

**Topics**
+ [ローカルセカンダリインデックスを持つテーブルを作成する](#LCICli.CreateTableWithIndex)
+ [ローカルセカンダリインデックスを持つテーブルの説明](#LCICli.DescribeTableWithIndex)
+ [ローカルセカンダリインデックスのクエリ](#LCICli.QueryAnIndex)

## ローカルセカンダリインデックスを持つテーブルを作成する
<a name="LCICli.CreateTableWithIndex"></a>

ローカルセカンダリインデックスは、テーブルの作成と同時に作成する必要があります。これを行うには、`create-table` パラメータを使用し、1 つ以上のローカルセカンダリインデックスの仕様を指定します。次の例では、ミュージックコレクション内の曲に関する情報を保持するためのテーブル (`Music`) を作成しています。パーティションキーは `Artist` で、ソートキーは `SongTitle` です。`AlbumTitle` 属性に関するセカンダリインデックス `AlbumTitleIndex` は、アルバムタイトルによるクエリを容易にします。

```
aws dynamodb create-table \
    --table-name Music \
    --attribute-definitions AttributeName=Artist,AttributeType=S AttributeName=SongTitle,AttributeType=S \
        AttributeName=AlbumTitle,AttributeType=S  \
    --key-schema AttributeName=Artist,KeyType=HASH AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --local-secondary-indexes \
        "[{\"IndexName\": \"AlbumTitleIndex\",
        \"KeySchema\":[{\"AttributeName\":\"Artist\",\"KeyType\":\"HASH\"},
                      {\"AttributeName\":\"AlbumTitle\",\"KeyType\":\"RANGE\"}],
        \"Projection\":{\"ProjectionType\":\"INCLUDE\",  \"NonKeyAttributes\":[\"Genre\", \"Year\"]}}]"
```

DynamoDB がテーブルを作成し、テーブルのステータスを `ACTIVE` に設定するまで待機する必要があります。その後、テーブルへのデータ項目の入力を開始できます。[describe-table](https://docs.aws.amazon.com/cli/latest/reference/dynamodb/describe-table.html) を使用して、テーブル作成のステータスを判断できます。

## ローカルセカンダリインデックスを持つテーブルの説明
<a name="LCICli.DescribeTableWithIndex"></a>

テーブルのローカルセカンダリインデックスに関する情報を取得するには、`describe-table` パラメータを使用します。インデックスごとに、名前、キースキーマ、および射影された属性にアクセスできます。

```
aws dynamodb describe-table --table-name Music
```

## ローカルセカンダリインデックスのクエリ
<a name="LCICli.QueryAnIndex"></a>

テーブルに `query` を実行するのとほぼ同じ方法で、ローカルセカンダリインデックスに対する `query` オペレーションを使用することができます。インデックス名、インデックスソートキーのクエリ条件、および返す属性を指定する必要があります。この例では、インデックスは `AlbumTitleIndex`、インデックスソートキーは `AlbumTitle` です。

返される属性は、インデックスに射影された属性だけです。このクエリを変更して非キー属性を選択することもできますが、これには比較的コストのかかるテーブルフェッチアクティビティが必要です。テーブルのフェッチの詳細については、「[属性の射影](LSI.md#LSI.Projections)」を参照してください。

```
aws dynamodb query \
    --table-name Music \
    --index-name AlbumTitleIndex \
    --key-condition-expression "Artist = :v_artist and AlbumTitle = :v_title" \
    --expression-attribute-values  '{":v_artist":{"S":"Acme Band"},":v_title":{"S":"Songs About Life"} }'
```

# DynamoDB トランザクションで複雑なワークフローを管理する
<a name="transactions"></a>

Amazon DynamoDB Transactions は、テーブル内およびテーブル間の複数の項目を調整したり、変更しないといった、デベロッパーのエクスペリエンスを簡素化します。トランザクションによって DynamoDB に不可分性、一貫性、分離性、耐久性 (ACID) が実現されるため、アプリケーション内でのデータの精度を維持することができます。

DynamoDB トランザクション読み込み/書き込み API を使用し、1 つのオースオアナッシングオペレーションとして複数の項目の追加、更新、または削除が必要となる複雑なビジネスワークフローを管理できます。たとえば、ビデオゲームデベロッパーであれば、ゲーム内での項目交換やゲーム内購入の際に、プレイヤーのプロファイル更新の正確性を確保できます。

トランザクション書き込み API を使用して、複数の `Put`、`Update`、`Delete`、`ConditionCheck` の各アクションをグループ化できます。その後、アクションを単一の `TransactWriteItems` オペレーションとして送信できます。このオペレーションはユニットとして成功または失敗します。同じことが複数の `Get` アクションにも当てはまります。この場合、1 つの `TransactGetItems` オペレーションとしてグループ化し、送信できます。

DynamoDB テーブルのトランザクションを有効にするために、追加コストはかかりません。料金の支払いは、トランザクションの一部である読み込みまたは書き込みに対してのみ行われます。DynamoDB は、トランザクション内の各項目の基になっている 2 つの読み込みまたは書き込みを実行します。1 つはトランザクションの準備用で、もう 1 つはトランザクションのコミット用です。これらの基になっている 2 つの読み込み/書き込みオペレーションは、Amazon CloudWatch メトリクスに表示されます。

DynamoDB トランザクションを開始するには、最新の AWS SDK または AWS Command Line Interface (AWS CLI) をダウンロードします。その後、「[DynamoDB トランザクションの例](transaction-example.md)」に従います。

以下のセクションでは、トランザクション API の詳細についての概要とそれらを DynamoDB で使用する方法を示します。

**Topics**
+ [仕組み](transaction-apis.md)
+ [トランザクションでの IAM の使用](transaction-apis-iam.md)
+ [サンプルのコード](transaction-example.md)

# Amazon DynamoDB Transactions: 仕組み
<a name="transaction-apis"></a>

Amazon DynamoDB Transactions を使用すれば、複数のアクションをまとめてグループ化し、1 つのオールオアナッシングの `TransactWriteItems` または `TransactGetItems` オペレーションとして送信できます。以下のセクションでは、API オペレーション、容量管理、ベストプラクティス、DynamoDB でのトランザクション操作の使用に関する他の詳細について説明します。

**Topics**
+ [TransactWriteItems API](#transaction-apis-txwriteitems)
+ [TransactGetItems API](#transaction-apis-txgetitems)
+ [DynamoDB トランザクションの分離レベル](#transaction-isolation)
+ [DynamoDB でのトランザクション競合の処理](#transaction-conflict-handling)
+ [DynamoDB アクセラレーター (DAX) でのトランザクション API の使用](#transaction-apis-dax)
+ [トランザクションの容量管理](#transaction-capacity-handling)
+ [トランザクションのベストプラクティス](#transaction-best-practices)
+ [グローバルテーブルでのトランザクション API の使用](#transaction-integration)
+ [DynamoDB トランザクションと AWSLabs トランザクションクライアントライブラリ](#transaction-vs-library)

## TransactWriteItems API
<a name="transaction-apis-txwriteitems"></a>

`TransactWriteItems` は、最大 100 の書き込みアクションを 1 つのオールオアナッシングオペレーションにグループ化する、同期的でべき等な書き込みオペレーションです。これらのアクションは、同じ AWS アカウントおよび同じリージョン内の 1 つ以上の DynamoDB テーブルにある最大 100 個の異なる項目をターゲットにすることができます。トランザクション内のアイテムの合計サイズは 4 MB を超えることはできません。すべて成功するかどれも成功しないかのどちらとなるように、アトミックに実行されます。

**注記**  
 `TransactWriteItems` オペレーションは、含まれるすべてのアクションを正常に完了する必要があり、そうでない場合は変更がまったく行われないという点で `BatchWriteItem` オペレーションとは異なります。`BatchWriteItem` オペレーションでは、バッチ内の一部のアクションのみ成功し、他のアクションは成功しないことがあり得ます。
 インデックスを使用してトランザクションを実行することはできません。

同じトランザクション内の複数のオペレーションが同じ項目をターゲットとすることはできません。たとえば、同じトランザクション内で同じ項目に対して `ConditionCheck` を実行し、`Update` アクションも実行することはできません。

以下のタイプのアクションをトランザクションに追加できます。
+ `Put` — `PutItem` オペレーションを開始し、条件付きで、または条件をまったく指定せずに、新しい項目を作成するか、古い項目を新しい項目に置き換えます。
+ `Update` — `UpdateItem` オペレーションを開始し、既存の項目の属性を編集するか、まだ存在しない場合は新しい項目をテーブルに追加します。条件付きまたは条件なしで既存の項目で属性を追加、削除、更新するには、このアクションを使用します。
+ `Delete` — `DeleteItem` オペレーションを開始し、プライマリキーにより識別される 1 つの項目をテーブルで削除します。
+ `ConditionCheck` — 項目が存在することを確認するか、項目の特定の属性の条件を確認します。

DynamoDB でトランザクションが完了すると、変更のグローバルセカンダリインデックス (GSIs)、ストリーム、バックアップへの伝播が開始されます。この伝播は以下のとおり、徐々に行われます。同じトランザクションからのストリームレコードは異なるタイミングで表示されるため、他のトランザクションからのレコードとインターリーブする可能性があります。ストリームコンシューマーは、トランザクションの原子性や順番が保証されていると想定すべきではありません。

トランザクションで変更された項目のアトミックスナップショットを確保するには、TransactGetItems オペレーションを使用して、関連するすべての項目をまとめて読み取ります。このオペレーションを使用すると、データの一貫したビューが得られ、完了済みのトランザクションからのすべての変更が表示されるか、まったく表示されません。

伝播は即時に実行されるわけでないため、伝播中にテーブルがバックアップから復元された場合や([RestoreTableFromBackup](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_RestoreTableFromBackup.html))、特定時点のエクスポートが行われた場合 ([ExportTableToPointInTime](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ExportTableToPointInTime.html))、含まれるのが最近のトランザクション中に行われた変更の一部のみとなる場合もあります。

### べき等性
<a name="transaction-apis-txwriteitems-idempotency"></a>

`TransactWriteItems` 呼び出しを行ってリクエストが*冪等*であることを確認する場合、オプションでクライアントトークンを含めることができます。トランザクションを冪等にすると、接続のタイムアウトや他の接続問題に伴って同じオペレーションが複数回送信された場合に、アプリケーションエラーを防ぐことができます。

元の `TransactWriteItems` 呼び出しが成功した場合、同じクライアントトークンを持つ以降の `TransactWriteItems` 呼び出しは変更なしで正常に返ります。`ReturnConsumedCapacity` パラメータが設定されている場合、最初の `TransactWriteItems` 呼び出しは変更時に消費された書き込みキャパシティーユニットの数を返します。同じクライアントトークンを持つ以降の `TransactWriteItems` 呼び出しは、項目の読み取り時に消費された読み取りキャパシティユニットの数を返します。

**冪等性について重要な点**
+ クライアントトークンは、それを使用するリクエストが完了してから 10 分間有効です。10 分後、同じクライアントトークンを使用するリクエストは新しいリクエストとして扱われます。10 分が経過してから、同じリクエストに同じクライアントトークンを再利用しないでください。
+ 10 分間のべき等性期間内に同じクライアントトークンを使用してリクエストを繰り返すとき、他の一部のリクエストパラメータを変更した場合、DynamoDB は `IdempotentParameterMismatch` 例外を返します。

### 書き込みのエラー処理
<a name="transaction-apis-txwriteitems-errors"></a>

以下の条件下では、書き込みトランザクションが成功しません。
+ いずれかの条件式の条件が満たされていない場合。
+ 同じ `TransactWriteItems` オペレーション内の複数のアクションが同じ項目をターゲットとしているために、トランザクション検証エラーが発生した場合。
+ `TransactWriteItems` リクエストが、`TransactWriteItems` リクエスト内の 1 つ以上の項目に対する継続中の `TransactWriteItems` オペレーションと競合する場合。この場合、リクエストは `TransactionCanceledException` で失敗します。
+ トランザクションを完了するプロビジョンドキャパシティーが足りない場合。
+ 項目サイズが大きくなりすぎる (400 KB 超)、ローカルセカンダリインデックス (LSI) が大きくなりすぎる、またはトランザクションにより変更が加えられたために同様の検証エラーが発生した場合。
+ 無効なデータ形式などのユーザーエラーがある場合。

 `TransactWriteItems` オペレーションとの競合がどのように処理されるかについて詳しくは、「[DynamoDB でのトランザクション競合の処理](#transaction-conflict-handling)」を参照してください。

## TransactGetItems API
<a name="transaction-apis-txgetitems"></a>

`TransactGetItems` は、最大 100 個の `Get` アクションをまとめてグループ化する同期読み取りオペレーションです。これらのアクションは、同じ AWS アカウントおよびリージョン内の 1 つ以上の DynamoDB テーブルにある最大 100 個の異なる項目をターゲットにすることができます。トランザクション内の項目の合計サイズは 4 MB を超えることはできません。

`Get` アクションは、すべて成功するかすべて失敗するかのどちらとなるように、アトミックに実行されます。
+ `Get` — `GetItem` オペレーションを開始し、指定されたプライマリキーを持つ項目の属性のセットを取得します。一致する項目が見つからない場合、`Get` はデータを返しません。

### 読み込みのエラー処理
<a name="transaction-apis-txgetitems-errors"></a>

以下の条件下では、読み取りトランザクションが成功しません。
+ `TransactGetItems` リクエストが、`TransactWriteItems` リクエスト内の 1 つ以上の項目に対する継続中の `TransactGetItems` オペレーションと競合する場合。この場合、リクエストは `TransactionCanceledException` で失敗します。
+ トランザクションを完了するプロビジョンドキャパシティーが足りない場合。
+ 無効なデータ形式などのユーザーエラーがある場合。

 `TransactGetItems` オペレーションとの競合がどのように処理されるかについて詳しくは、「[DynamoDB でのトランザクション競合の処理](#transaction-conflict-handling)」を参照してください。

## DynamoDB トランザクションの分離レベル
<a name="transaction-isolation"></a>

トランザクションオペレーション (`TransactWriteItems` または `TransactGetItems`) と他のオペレーションの分離レベルは、次のとおりです。

### SERIALIZABLE
<a name="transaction-isolation-serializable"></a>

*直列化可能*分離レベルでは、複数の同時オペレーションの結果は、前のオペレーションが完了するまでオペレーションが開始されない場合と同じになります。

以下のタイプのオペレーション間には、直列化可能分離があります。
+ トランザクションオペレーションと標準書き込みオペレーション (`PutItem`、`UpdateItem`、または `DeleteItem`) の間。
+ トランザクションオペレーションと標準読み取りオペレーション (`GetItem`) の間。
+ `TransactWriteItems` オペレーションと `TransactGetItems` オペレーションの間。

トランザクションオペレーション間と `BatchWriteItem` オペレーション内の個々の標準書き込み間には直列化可能分離がありますが、トランザクションとユニットとしての `BatchWriteItem` オペレーションの間には直列化可能分離はありません。

同様に、トランザクションオペレーションと `GetItems` オペレーションの個別の `BatchGetItem` 間の分離レベルは直列化可能です。ただし、トランザクションとユニットとしての `BatchGetItem` オペレーション間の分離レベルは*コミット済み読み取り*です。

単一の `GetItem` リクエストは、`TransactWriteItems` リクエストの前または後に行う 2 つの方法のいずれかで、`TransactWriteItems` リクエストに関してシリアル化することができます。同時実行 `TransactWriteItems` リクエストのキーに対する複数の `GetItem` リクエストは、任意の順序で実行できるため、結果は*読み込みがコミット*されます。

たとえば、項目 A と項目 B の `GetItem` リクエストが、項目 A と項目 B の両方を変更する `TransactWriteItems` リクエストと同時に実行される場合、次の 4 つの可能性があります。
+ 両方の `GetItem` リクエストは、`TransactWriteItems` リクエストの前に実行されます。
+ 両方の `GetItem` リクエストは、`TransactWriteItems` リクエストの後に実行されます。
+ 項目 A の `GetItem` リクエストは、`TransactWriteItems` リクエストの前に実行されます。項目 B の場合、`GetItem` は `TransactWriteItems` の後に実行されます。
+ 項目 B の `GetItem` リクエストは、`TransactWriteItems` リクエストの前に実行されます。項目 A の場合、`GetItem` は `TransactWriteItems` の後に実行されます。

複数の `GetItem` リクエストにシリアル化可能な分離レベルが望ましい場合は、`TransactGetItems` を使用してください。

処理中に同じトランザクション書き込みリクエストの一部であった複数の項目に対して非トランザクション読み取りが行われた場合、一部の項目の新しい状態と他の項目の古い状態を読み取ることができる可能性があります。トランザクション書き込みリクエストに含まれていたすべての項目の新しい状態を読み取ることができるのは、トランザクションが完了したことを示すトランザクション書き込みの応答が成功した場合のみです。

トランザクションが正常に完了し、応答が受信されると、DynamoDB の結果整合性モデルにより、*結果整合性のある*読み込みオペレーションが短期間、古い状態を返す可能性があります。トランザクションの直後に最新のデータを確実に読み取るには、`ConsistentRead` を true に設定して、[*強力な整合性のある*](HowItWorks.ReadConsistency.md#HowItWorks.ReadConsistency.Strongly)読み込みを使用する必要があります。

### コミット済み読み取り
<a name="transaction-isolation-read-committed"></a>

*コミット済み読み取り*分離により、読み取りオペレーションは常に項目のコミット済み値を返します。つまり、読み取りによって、最終的に成功しなかったトランザクション書き込みの状態を表すビューが項目に表示されることはありません。コミット済み読み取り分離では、読み取りオペレーションの直後に項目の変更が防止されません。

分離レベルは、トランザクションオペレーションと、複数の標準読み取り (`BatchGetItem`、`Query`、または `Scan`) が関係する読み取りオペレーションの間ではコミット済み読み取りです。トランザクション書き込みにより `BatchGetItem`、`Query`、または `Scan` オペレーションの途中で項目が更新された場合、その後の読み取りオペレーションの部分では、新しくコミットされた値 (`ConsistentRead)` を使用) か、場合によってはそれ以前のコミット済み値 (結果整合性のある読み込み) を返します。

### オペレーションの概要
<a name="transaction-isolation-table"></a>

以下の表は、トランザクションオペレーション (`TransactWriteItems` または `TransactGetItems`) と他のオペレーションの間の分離レベルをまとめたものです。


| オペレーション | 分離レベル | 
| --- | --- | 
| `DeleteItem` | *直列化可能* | 
| `PutItem` | *直列化可能* | 
| `UpdateItem` | *直列化可能* | 
| `GetItem` | *直列化可能* | 
| `BatchGetItem` | *コミット済み読み取り*\$1 | 
| `BatchWriteItem` | *直列化不可*\$1 | 
| `Query` | *コミット済み読み取り* | 
| `Scan` | *コミット済み読み取り* | 
| 他のトランザクションオペレーション | *直列化可能* | 

アスタリスク (\$1) が付いたレベルは、ユニットとしてオペレーションに適用されます。ただし、これらのオペレーション内の個々のアクションの分離レベルは*直列化可能*です。

## DynamoDB でのトランザクション競合の処理
<a name="transaction-conflict-handling"></a>

トランザクション競合は、トランザクション内の項目に対する項目レベルの同時リクエスト中に発生する場合があります。トランザクション競合は、次のシナリオで発生する場合があります。
+ 項目に対する `PutItem`、`UpdateItem`、または `DeleteItem` リクエストが、同じ項目を含む継続中の `TransactWriteItems` リクエストと競合する。
+ `TransactWriteItems` リクエスト内の項目が、継続中の別の `TransactWriteItems` リクエストの一部である。
+ `TransactGetItems` リクエスト内の項目が、継続中の `TransactWriteItems`、`BatchWriteItem`、`PutItem`、`UpdateItem`、または `DeleteItem` リクエストの一部である。

**注記**  
`PutItem`、`UpdateItem`、または `DeleteItem` リクエストが拒否された場合、リクエストは `TransactionConflictException` で失敗します。
`TransactWriteItems` または `TransactGetItems` 内の項目レベルのリクエストが拒否された場合、リクエストは `TransactionCanceledException` で失敗します。そのリクエストが失敗した場合、AWS SDK はリクエストを再試行しません。  
AWS SDK for Java を使用している場合、例外には `TransactItems` リクエストパラメータの項目リスト通りに順序付けられた [CancellationReasons](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CancellationReason.html) のリストが含まれます。他の言語の場合、リストの文字列表現が例外のエラーメッセージに含まれます。
継続中の `TransactWriteItems` オペレーションまたは `TransactGetItems` オペレーションが同時 `GetItem` リクエストと競合している場合、両方のオペレーションが成功する可能性があります。

[TransactionConflict CloudWatch メトリクス](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/metrics-dimensions.html)は、項目レベルのリクエストが失敗するたびに増分されます。

## DynamoDB アクセラレーター (DAX) でのトランザクション API の使用
<a name="transaction-apis-dax"></a>

`TransactWriteItems` と `TransactGetItems` が、どちらも DynamoDB と同じ分離レベルで DynamoDB アクセラレーター (DAX) でサポートされています。

`TransactWriteItems` は DAX を介して書き込みます。DAX は DynamoDB に `TransactWriteItems` コールを渡し、応答を返します。書き込み後にキャッシュにデータを追加するために、DAX は、`TransactWriteItems` オペレーション内の各項目に対してバックグラウンドで `TransactGetItems` をコールします。これにより、追加の読み込み容量単位が消費されます。（詳しくは、[トランザクションの容量管理](#transaction-capacity-handling) を参照してください）。この機能により、アプリケーションロジックをシンプルに保ち、トランザクション処理と非トランザクション処理の両方に DAX を使用できます。

`TransactGetItems` コールは、項目がローカルにキャッシュされることなく DAX を通過します。これは、DAX の強い整合性のある読み込み API と同じです。

## トランザクションの容量管理
<a name="transaction-capacity-handling"></a>

DynamoDB テーブルのトランザクションを有効にするために、追加コストはかかりません。料金の支払いは、トランザクションの一部である読み込みまたは書き込みに対してのみ行われます。DynamoDB は、トランザクション内の各項目の基になっている 2 つの読み込みまたは書き込みを実行します。1 つはトランザクションの準備用で、もう 1 つはトランザクションのコミット用です。基になっている 2 つの読み込み/書き込みオペレーションは、Amazon CloudWatch メトリクスに表示されます。

容量をテーブルにプロビジョニングするとき、トランザクション API により要求される追加の読み取りと書き込みを計画してください。たとえば、アプリケーションが 1 秒あたり 1 件のトランザクションを実行し、各トランザクションは 500 バイトの項目を 3 個テーブルに書き込むとします。各項目には、2 つの書き込みキャパシティーユニット (WCU) が必要です。1 つはトランザクションの準備用で、もう 1 つはトランザクションのコミット用です。したがって、テーブルには WCU を 6 個プロビジョニングする必要があります。

前の例で DynamoDB アクセラレーター (DAX) を使用していた場合、`TransactWriteItems` のコールで項目ごとに 2 つの読み込み容量単位 (RCU) も使用します。したがって、テーブルには追加の RCU を 6 個プロビジョニングする必要があります。

同様に、アプリケーションが 1 秒あたり 1 件の読み込みトランザクションを実行し、各トランザクションは 500 バイトの項目を 3 個テーブルで読み取る場合、読み込み容量単位 (RCU) を 6 個テーブルにプロビジョンする必要があります。各項目を読み取るには、2 つの RCU が必要です。1 つはトランザクションの準備用で、もう 1 つはトランザクションのコミット用です。

さらに、SDK のデフォルトの動作は、`TransactionInProgressException` 例外が発生した場合にトランザクションを再試行します。これらの再試行で消費される追加の読み込みキャパシティーユニット (RCU) を計画してください。同じことは、`ClientRequestToken` を使用して独自のコードでトランザクションを再試行しようとする場合に当てはまります。

## トランザクションのベストプラクティス
<a name="transaction-best-practices"></a>

DynamoDB トランザクションの使用時に推奨される以下の手法を検討してください。
+ テーブルで自動スケーリングを有効にするか、トランザクションの項目ごとに 2 つの読み取りまたは書き込みオペレーションを実行するのに十分なスループット容量をプロビジョニングしたことを確認します。
+ AWS によって提供された SDK を使用していない場合、`TransactWriteItems` 呼び出しを行うときに `ClientRequestToken` 属性を含め、リクエストがべき等となるようにします。
+ 必要でない場合は、トランザクションにオペレーションをまとめてグループ化しないでください。たとえば、10 個のオペレーションを持つ 1 つのトランザクションを、アプリケーションの正確性を低下させずに複数のトランザクションに分割できる場合、トランザクションを分割することをお勧めします。トランザクションをシンプルにするとスループットが向上し、成功する可能性が高まります。
+ 同じ項目を同時に更新する複数のトランザクションによって、トランザクションをキャンセルする競合が発生する可能性があります。そのような競合を最小限に抑えるため、データモデリングには以下の DynamoDB のベストプラクティスをお勧めします。
+ 属性のセットが 1 つのトランザクションの一部として複数の項目間で頻繁に更新される場合、属性を 1 つの項目にグループ化し、トランザクションのスコープを減らすことを検討してください。
+ データを大量に取り込むためにトランザクションを使用しないでください。一括書き込みには、`BatchWriteItem` の使用をお勧めします。

## グローバルテーブルでのトランザクション API の使用
<a name="transaction-integration"></a>

トランザクションオペレーションは、書き込み API が最初に呼び出された AWS リージョン内でのみ、不可分性、一貫性、分離性、および耐久性 (ACID) を保証します。グローバルテーブルのリージョン間では、トランザクションはサポートされていません。たとえば、米国東部 (オハイオ) リージョンと米国西部 (オレゴン) リージョンにレプリカを含むグローバルテーブルがあり、米国東部 (バージニア北部) リージョンで `TransactWriteItems` オペレーションを実行するとします。変更がレプリケートされると、米国西部 (オレゴン) リージョンで部分的に完了したトランザクションを確認できます。変更は、ソースリージョンでコミットされた後でのみ、他のリージョンにレプリケートされます。

## DynamoDB トランザクションと AWSLabs トランザクションクライアントライブラリ
<a name="transaction-vs-library"></a>

DynamoDB トランザクションには、[AWSLabs](https://github.com/awslabs) トランザクションクライアントライブラリを置き換えるより費用対効率、堅牢性、パフォーマンスに優れた手段が用意されています。ネイティブのサーバー側トランザクション API を使用するようにアプリケーションを更新することをお勧めします。

# DynamoDB トランザクションでの IAM の使用
<a name="transaction-apis-iam"></a>

AWS Identity and Access Management (IAM) を使用すると、トランザクションオペレーションが Amazon DynamoDB で実行可能なアクションを制限できます。DynamoDB での IAM ポリシーの詳細な使用については、[DynamoDB のアイデンティティベースのポリシー](security_iam_service-with-iam.md#security_iam_service-with-iam-id-based-policies) を参照してください。

`Put`、`Update`、`Delete`、および `Get` アクションの権限は、基になる `PutItem`、`UpdateItem`、`DeleteItem`、および `GetItem` オペレーションに使用される権限により決定されます。`ConditionCheck` アクションの場合、IAM ポリシーで `dynamodb:ConditionCheckItem` 許可を使用できます。

以下に、DynamoDB トランザクションの設定に使用できる IAM ポリシーの例を示します。

## 例 1: トランザクションオペレーションを許可する
<a name="tx-policy-example-1"></a>

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:ConditionCheckItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:DeleteItem",
                "dynamodb:GetItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:*:*:table/table04"
            ]
        }
    ]
}
```

------

## 例 2: トランザクションオペレーションのみを許可する
<a name="tx-policy-example-2"></a>

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:ConditionCheckItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:DeleteItem",
                "dynamodb:GetItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:*:*:table/table04"
            ],
            "Condition": {
                "ForAnyValue:StringEquals": {
                    "dynamodb:EnclosingOperation": [
                        "TransactWriteItems",
                        "TransactGetItems"
                    ]
                }
            }
        }
    ]
}
```

------

## 例 3: トランザクションを使用しない読み込み/書き込みを許可し、トランザクションを使用する読み込み/書き込みをブロックする
<a name="tx-policy-example-3"></a>

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Deny",
            "Action": [
                "dynamodb:ConditionCheckItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:DeleteItem",
                "dynamodb:GetItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:*:*:table/table04"
            ],
            "Condition": {
                "ForAnyValue:StringEquals": {
                    "dynamodb:EnclosingOperation": [
                        "TransactWriteItems",
                        "TransactGetItems"
                    ]
                }
            }
        },
        {
            "Effect": "Allow",
             "Action": [
                 "dynamodb:PutItem",
                 "dynamodb:DeleteItem",
                 "dynamodb:GetItem",
                 "dynamodb:UpdateItem"
             ],
             "Resource": [
                 "arn:aws:dynamodb:*:*:table/table04"
             ]
         }
    ]
}
```

------

## 例 4: ConditionCheck の失敗時に情報が返されないようにする
<a name="tx-policy-example-4"></a>

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:ConditionCheckItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:DeleteItem",
                "dynamodb:GetItem"
            ],
            "Resource": "arn:aws:dynamodb:*:*:table/table01",
            "Condition": {
                "StringEqualsIfExists": {
                    "dynamodb:ReturnValues": "NONE"
                }
            }
        }
    ]
}
```

------

# DynamoDB トランザクションの例
<a name="transaction-example"></a>

Amazon DynamoDB Transactions が役に立つ状況の例として、このサンプルの Java アプリケーションをオンラインマーケットプレイスで検討してください。

アプリケーションには、バックエンドに 3 つの DynamoDB テーブルがあります。
+ `Customers` — このテーブルには、マーケットプレイスの顧客に関する詳細が保存されます。プライマリキーは `CustomerId` 一意の識別子です。
+ `ProductCatalog` — このテーブルには、マーケットプレイスで販売されている製品の価格や在庫状況などの詳細が保存されます。プライマリキーは `ProductId` 一意の識別子です。
+ `Orders` — このテーブルには、マーケットプレイスからの注文に関する詳細が保存されます。プライマリキーは `OrderId` 一意の識別子です。

## 注文を作成する
<a name="transaction-example-write-order"></a>

次のコードスニペットは、DynamoDB トランザクションを使用して、注文の作成と処理に必要な複数のステップを調整する方法を示しています。単一のオールオアナッシングオペレーションを使用すると、トランザクションのいずれかの部分が失敗しても、トランザクション内のアクションは実行されず、変更も行われません。

この例では、`customerId` が `09e8e9c8-ec48` である顧客からの注文を設定します。次に、次の単純な注文処理ワークフローを使用して、単一のトランザクションとして実行します。

1. 顧客 ID が有効であることを確認します。

1. 製品が `IN_STOCK` であることを確認し、製品のステータスを `SOLD` に更新します。

1. 注文がまだ存在していないことを確認し、注文を作成します。

### 顧客を検証する
<a name="transaction-example-order-part-a"></a>

まず、`customerId` が `09e8e9c8-ec48` に等しい顧客が顧客テーブルに存在することを確認するアクションを定義します。

```
final String CUSTOMER_TABLE_NAME = "Customers";
final String CUSTOMER_PARTITION_KEY = "CustomerId";
final String customerId = "09e8e9c8-ec48";
final HashMap<String, AttributeValue> customerItemKey = new HashMap<>();
customerItemKey.put(CUSTOMER_PARTITION_KEY, new AttributeValue(customerId));

ConditionCheck checkCustomerValid = new ConditionCheck()
    .withTableName(CUSTOMER_TABLE_NAME)
    .withKey(customerItemKey)
    .withConditionExpression("attribute_exists(" + CUSTOMER_PARTITION_KEY + ")");
```

### 製品のステータスを更新する
<a name="transaction-example-order-part-b"></a>

次に、製品ステータスが現在 `IN_STOCK` に設定されている条件が `true` の場合に、製品ステータスを `SOLD` に更新するアクションを定義します。項目の製品ステータス属性が `IN_STOCK` に等しくなかった場合、`ReturnValuesOnConditionCheckFailure` パラメータを設定すると項目が返されます。

```
final String PRODUCT_TABLE_NAME = "ProductCatalog";
final String PRODUCT_PARTITION_KEY = "ProductId";
HashMap<String, AttributeValue> productItemKey = new HashMap<>();
productItemKey.put(PRODUCT_PARTITION_KEY, new AttributeValue(productKey));

Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
expressionAttributeValues.put(":new_status", new AttributeValue("SOLD"));
expressionAttributeValues.put(":expected_status", new AttributeValue("IN_STOCK"));

Update markItemSold = new Update()
    .withTableName(PRODUCT_TABLE_NAME)
    .withKey(productItemKey)
    .withUpdateExpression("SET ProductStatus = :new_status")
    .withExpressionAttributeValues(expressionAttributeValues)
    .withConditionExpression("ProductStatus = :expected_status")
    .withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD);
```

### 注文を作成する
<a name="transaction-example-order-part-c"></a>

最後に、`OrderId` の注文がまだ存在しない場合に限って、注文を作成します。

```
final String ORDER_PARTITION_KEY = "OrderId";
final String ORDER_TABLE_NAME = "Orders";

HashMap<String, AttributeValue> orderItem = new HashMap<>();
orderItem.put(ORDER_PARTITION_KEY, new AttributeValue(orderId));
orderItem.put(PRODUCT_PARTITION_KEY, new AttributeValue(productKey));
orderItem.put(CUSTOMER_PARTITION_KEY, new AttributeValue(customerId));
orderItem.put("OrderStatus", new AttributeValue("CONFIRMED"));
orderItem.put("OrderTotal", new AttributeValue("100"));

Put createOrder = new Put()
    .withTableName(ORDER_TABLE_NAME)
    .withItem(orderItem)
    .withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
    .withConditionExpression("attribute_not_exists(" + ORDER_PARTITION_KEY + ")");
```

### トランザクションを実行する
<a name="transaction-example-order-part-d"></a>

次の例は、単一のオールオアナッシングオペレーションとして以前に定義されたアクションを実行する方法を示しています。

```
    Collection<TransactWriteItem> actions = Arrays.asList(
        new TransactWriteItem().withConditionCheck(checkCustomerValid),
        new TransactWriteItem().withUpdate(markItemSold),
        new TransactWriteItem().withPut(createOrder));

    TransactWriteItemsRequest placeOrderTransaction = new TransactWriteItemsRequest()
        .withTransactItems(actions)
        .withReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL);

    // Run the transaction and process the result.
    try {
        client.transactWriteItems(placeOrderTransaction);
        System.out.println("Transaction Successful");

    } catch (ResourceNotFoundException rnf) {
        System.err.println("One of the table involved in the transaction is not found" + rnf.getMessage());
    } catch (InternalServerErrorException ise) {
        System.err.println("Internal Server Error" + ise.getMessage());
    } catch (TransactionCanceledException tce) {
        System.out.println("Transaction Canceled " + tce.getMessage());
    }
```

## 注文詳細の読み込み
<a name="transaction-example-read-order"></a>

次の例は、完了した注文を `Orders` テーブルと `ProductCatalog` テーブルでのトランザクションから読み込む方法を示しています。

```
HashMap<String, AttributeValue> productItemKey = new HashMap<>();
productItemKey.put(PRODUCT_PARTITION_KEY, new AttributeValue(productKey));

HashMap<String, AttributeValue> orderKey = new HashMap<>();
orderKey.put(ORDER_PARTITION_KEY, new AttributeValue(orderId));

Get readProductSold = new Get()
    .withTableName(PRODUCT_TABLE_NAME)
    .withKey(productItemKey);
Get readCreatedOrder = new Get()
    .withTableName(ORDER_TABLE_NAME)
    .withKey(orderKey);

Collection<TransactGetItem> getActions = Arrays.asList(
    new TransactGetItem().withGet(readProductSold),
    new TransactGetItem().withGet(readCreatedOrder));

TransactGetItemsRequest readCompletedOrder = new TransactGetItemsRequest()
    .withTransactItems(getActions)
    .withReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL);

// Run the transaction and process the result.
try {
    TransactGetItemsResult result = client.transactGetItems(readCompletedOrder);
    System.out.println(result.getResponses());
} catch (ResourceNotFoundException rnf) {
    System.err.println("One of the table involved in the transaction is not found" + rnf.getMessage());
} catch (InternalServerErrorException ise) {
    System.err.println("Internal Server Error" + ise.getMessage());
} catch (TransactionCanceledException tce) {
    System.err.println("Transaction Canceled" + tce.getMessage());
}
```

# Amazon DynamoDB の変更データキャプチャ
<a name="streamsmain"></a>

多くのアプリケーションでは、DynamoDB テーブルに保存された項目の変更を、変更の発生時にキャプチャすることで利点を利用できます。以下に示しているのは、いくつかのユースケースの例です。
+ 人気のモバイルアプリケーションは、1 秒あたり数千件の更新速度で、DynamoDB テーブルのデータを変更します。別のアプリケーションは、これらの更新に関するデータをキャプチャして保存し、モバイルアプリの使用状況メトリクスをほぼリアルタイムで提供します。
+ 金融アプリケーションは、DynamoDB テーブル内の株式市場データを変更します。並行して実行されるさまざまなアプリケーションは、これらの変化をリアルタイムで追跡し、リスクのある価値を計算し、株価の動きに基づいてポートフォリオを自動的にリバランスします。
+ 輸送車両や産業機器のセンサーは、DynamoDB テーブルにデータを送信します。さまざまなアプリケーションがパフォーマンスをモニタリングし、問題が検出されたときにメッセージングアラートを送信し、機械学習アルゴリズムを適用して潜在的な欠陥を予測し、データを圧縮して Amazon Simple Storage Service (Amazon S3) にアーカイブします。
+ アプリケーションは、友人の 1 人が新しい画像をアップロードするとすぐに、グループ内のすべての友人のモバイルデバイスに通知を自動送信します。
+ 新しいお客様がデータを DynamoDB テーブルに追加します。このイベントにより、新しいお客様にようこそメールを送信する別のアプリケーションが起動されます。

DynamoDB は、項目レベルの変更データキャプチャレコードのストリーミングをほぼリアルタイムでサポートします。これらのストリーミングを使用し、内容に基づいてアクションを実行するアプリケーションを構築できます。

**注記**  
DynamoDB Streams へのタグの追加、DynamoDB Streams での[属性ベースのアクセス制御 (ABAC)](access-control-resource-based.md) の使用はサポートされていません。

次の動画では、データキャプチャ変更の概念を紹介します。

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/VVv_-mZ5Ge8/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/VVv_-mZ5Ge8)


**Topics**
+ [変更データキャプチャのストリーミングオプション](#streamsmain.choose)
+ [Kinesis Data Streams を使用して DynamoDB への変更をキャプチャする。](kds.md)
+ [DynamoDB Streams の変更データキャプチャ](Streams.md)

## 変更データキャプチャのストリーミングオプション
<a name="streamsmain.choose"></a>

DynamoDB には、変更データキャプチャ用の 2 つのストリーミングモデルがあります。DynamoDB 用 Kinesis Data Streams と DynamoDB Streams です。

アプリケーションに適したソリューションを選択しやすくするために、次の表に、各ストリーミングモデルの特徴をまとめてあります。

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/streamsmain.html)

同じ DynamoDB テーブルで両方のストリーミングモデルを有効にすることができます。

次の動画では、これら 2 つのオプションの違いを詳しく説明しています。

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/UgG17Wh2y0g/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/UgG17Wh2y0g)


# Kinesis Data Streams を使用して DynamoDB への変更をキャプチャする。
<a name="kds"></a>

Amazon Kinesis Data Streams を使用して Amazon DynamoDB への変更をキャプチャできます。

Kinesis Data Streams は、DynamoDB テーブルの項目レベルの変更をキャプチャーし、それらを [Kinesis Data Streams](https://docs.aws.amazon.com/streams/latest/dev/introduction.html) にレプリケートします。アプリケーションは このストリームにアクセスして、項目レベルの変更をほぼリアルタイムで表示できます。1 時間あたりテラバイトのデータを継続的に取り込み保存できます。より長いデータ保持時間を利用し、強化されたファンアウト機能により、2 つ以上のダウンストリームアプリケーションに同時にアクセスできます。その他のメリットには、追加の監査とセキュリティの透明性が含まれます。

Kinesis Data Streams を使用すると、[Amazon Data Firehose](https://docs.aws.amazon.com/firehose/latest/dev/what-is-this-service.html) および [Amazon Managed Service for Apache Flink](https://docs.aws.amazon.com/kinesisanalytics/latest/dev/what-is.html) にもアクセスできます。これらのサービスは、リアルタイムでのダッシュボードの強化、アラートの生成、動的な料金設定、広告の実装、高度なデータ分析および機械学習アルゴリズムを実装するアプリケーションの構築に役立ちます。

**注記**  
DynamoDB で Kinesis データストリームを使用すると、データストリームに対する [Kinesis Data Streams 料金](https://aws.amazon.com/kinesis/data-streams/pricing/)とソーステーブルに対する [DynamoDB 料金](https://aws.amazon.com/dynamodb/pricing/)の両方が適用されます。

コンソール、AWS CLI、または Java SDK を使用して DynamoDB テーブルで Kinesis ストリーミングを有効にするには、「[Amazon DynamoDB 用 Kinesis Data Streams の開始方法](kds_gettingstarted.md)」を参照してください。

**Topics**
+ [Kinesis Data Streams の DynamoDB との連携について](#kds_howitworks)
+ [Amazon DynamoDB 用 Kinesis Data Streams の開始方法](kds_gettingstarted.md)
+ [DynamoDB Streams および Kinesis Data Streams でのシャードとメトリクスの使用](kds_using-shards-and-metrics.md)
+ [Amazon Kinesis Data Streams および Amazon DynamoDB の IAM ポリシーを使用する](kds_iam.md)

## Kinesis Data Streams の DynamoDB との連携について
<a name="kds_howitworks"></a>

DynamoDB テーブルで Kinesis データストリームが有効になっている場合、そのテーブルは、テーブルのデータに対するすべての変更をキャプチャしたデータレコードを送信します。このデータレコードは以下を含みます。
+ 項目が最後に作成、更新、または削除された特定の時刻
+ その項目のプライマリキー
+ 変更前のレコードのスナップショット
+ 変更後のレコードのスナップショット 

これらのデータレコードは、ほぼリアルタイムに取り込まれ、公開されます。これらが Kinesis データストリームに書き込まれた後は、他のレコードと同じように読み込むことができます。Kinesis Client Library の使用、AWS Lambda の使用、Kinesis Data Streams API の呼び出し、およびその他接続サービスの利用が可能です。詳細については、Amazon Kinesis Data Streams デベロッパーガイドの[Amazon Kinesis Data Streams からのデータの読み込み](https://docs.aws.amazon.com/streams/latest/dev/building-consumers.html)を参照してください。

これらのデータへの変更は、非同期的にキャプチャーされます。Kinesis は、ストリーミング元のテーブルに対するパフォーマンスに影響を与えません。Kinesis データストリームに保存されているストリームレコードも、保存時に暗号化されます。詳細については、「[Amazon Kinesis Data Streams のデータ保護](https://docs.aws.amazon.com/streams/latest/dev/server-side-encryption.html)」を参照してください。

Kinesis データストリームレコードは、項目の変更が発生した順序とは異なる順序で表示される場合があります。同じ項目の通知がストリームに複数回表示される場合もあります。`ApproximateCreationDateTime` 属性をチェックして、項目の変更が発生した順序を特定したり、重複するレコードを特定したりできます。

Kinesis データストリームを DynamoDB テーブルのストリーミング先として有効にすると、`ApproximateCreationDateTime` 値の精度をミリ秒またはマイクロ秒単位で設定できます。デフォルトでは、`ApproximateCreationDateTime` は変更の時刻をミリ秒単位で示します。さらに、アクティブなストリーミング先でこの値を変更できます。このような変更後、Kinesis に書き込まれるストリームレコードは、希望する精度の `ApproximateCreationDateTime` 値を持つようになります。

DynamoDB に書き込むバイナリ値は、[base64 エンコード形式](HowItWorks.NamingRulesDataTypes.md)でエンコードする必要があります。ただし、データレコードを Kinesis データストリームに書き込む場合、これらのエンコードされたバイナリ値は、再度 base64 エンコーディングでエンコードされます。これらのレコードを Kinesis データストリームから読み取る場合、未加工のバイナリ値を取得するには、アプリケーションでこれらの値を 2 回デコードする必要があります。

DynamoDB では、Kinesis Data Streams の使用に対して変更データキャプチャ単位で課金されます。単一の項目あたり 1 KB の変更が、1 つの変更データキャプチャ単位としてカウントされます。各項目で変更したキロバイト数は、[書き込み操作のキャパシティーユニット消費量](read-write-operations.md#write-operation-consumption)と同じロジックを使用して、ストリームに書き込まれた項目の「前の」イメージと「後の」イメージの大きい方で計算されます。DynamoDB [オンデマンド](capacity-mode.md#capacity-mode-on-demand)モードの動作と同様に、変更データキャプチャ単位のキャパシティースループットをプロビジョニングする必要はありません。

### DynamoDB テーブルの Kinesis データストリームを有効にする
<a name="kds_howitworks.enabling"></a>

AWS マネジメントコンソール、AWS SDK、または AWS Command Line Interface (AWS CLI) を使用して、既存の DynamoDB テーブルから Kinesis へのストリーミングを有効または無効にすることができます。
+ テーブルと同じ AWS アカウントと AWS リージョンでのみ、DynamoDB から Kinesis Data Streams にデータをストリーミングできます。
+ DynamoDB テーブルからのデータを 1 つの Kinesis データストリームにだけストリーミングできます。

  

### DynamoDB テーブルの Kinesis Data Streams 送信先に変更を加える
<a name="kds_howitworks.makingchanges"></a>

デフォルトでは、すべての Kinesis データストリームレコードには `ApproximateCreationDateTime` 属性が含まれます。この属性は、各レコードが作成されたおおよその時刻のタイムスタンプをミリ秒単位で表します。この値の精度は、[https://console.aws.amazon.com/kinesis](https://console.aws.amazon.com/kinesis)、SDK、または AWS CLI を使用して変更できます。

# Amazon DynamoDB 用 Kinesis Data Streams の開始方法
<a name="kds_gettingstarted"></a>

このセクションでは、Amazon DynamoDB コンソール、AWS Command Line Interface (AWS CLI)、および API を活用して Amazon DynamoDB 用の Amazon Kinesis Data Streams テーブルを使用する方法について説明します。

## アクティブな Amazon Kinesis Data Streams の作成
<a name="kds_gettingstarted.making-changes"></a>

これらの例はすべて、[DynamoDB の使用開始](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStartedDynamoDB.html)チュートリアルの一部として作成された `Music` DynamoDB テーブルを使用しています。

コンシューマーを構築し、Kinesis データストリームを他の AWS のサービスに接続する詳細方法については、「*Amazon Kinesis Data Streams デベロッパーガイド*」の「[Kinesis Data Streams からのデータの読み込み](https://docs.aws.amazon.com/streams/latest/dev/building-consumers.html)」を参照してください。

**注記**  
 KDS シャードを初めて使用するときは、使用パターンに合わせてシャードをスケールアップまたはスケールダウンするように設定することをお勧めします。使用パターンに関するデータをさらに蓄積したら、それに合わせてストリーム内のシャードを調整できます。

------
#### [ Console ]

1. AWS マネジメントコンソール にサインインし、Kinesis コンソール ([https://console.aws.amazon.com/kinesis/](https://console.aws.amazon.com/kinesis/)) を開きます。

1. [**Create data stream (データストリーミングの作成)**] を選択し、指示に従って `samplestream` というストリーミングを作成します。

1. DynamoDB コンソール ([https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/)) を開きます。

1. コンソールの左側のナビゲーションペインで、[**テーブル**] を選択します。

1. **[Music]** テーブルを選択します。

1. [**エクスポートとストリーム**] タブを選択します。

1. (オプション) **[Amazon Kinesis データストリームの詳細]** で、レコードのタイムスタンプの精度をマイクロ秒 (デフォルト) からミリ秒に変更できます。

1. ドロップダウンリストから **samplestream** を選択します。

1. **[オンにする]** ボタンを選択します。

------
#### [ AWS CLI ]

1. [create-stream コマンド](https://docs.aws.amazon.com/cli/latest/reference/kinesis/create-stream.html)を使用して、`samplestream` という名前の Kinesis データストリームを作成します。

   ```
   aws kinesis create-stream --stream-name samplestream --shard-count 3 
   ```

   Kinesis データストリームのシャード数を設定する前に「[Kinesis Data Streams のシャード管理に関する考慮事項](kds_using-shards-and-metrics.md#kds_using-shards-and-metrics.shardmanagment)」を参照してください。

1. Kinesis ストリームがアクティブで、使用できる状態になっていることを確認するには、[describe-stream](https://docs.aws.amazon.com/cli/latest/reference/kinesis/describe-stream.html) コマンドを使用します。

   ```
   aws kinesis describe-stream --stream-name samplestream
   ```

1. DynamoDB `enable-kinesis-streaming-destination` コマンドを使用して、DynamoDB テーブルで Kinesis ストリーミングを有効にします。`stream-arn` の値を、前のステップの `describe-stream` によって返された値で置き換えます。オプションで、各レコードに返されるタイムスタンプ値の精度をより細かく (マイクロ秒) したストリーミングを有効にします。

   マイクロ秒のタイムスタンプ精度でのストリーミングを有効にします。

   ```
   aws dynamodb enable-kinesis-streaming-destination \
     --table-name Music \
     --stream-arn arn:aws:kinesis:us-west-2:12345678901:stream/samplestream
     --enable-kinesis-streaming-configuration ApproximateCreationDateTimePrecision=MICROSECOND
   ```

   または、デフォルトのタイムスタンプ精度 (ミリ秒) でのストリーミングを有効にします。

   ```
   aws dynamodb enable-kinesis-streaming-destination \
     --table-name Music \
     --stream-arn arn:aws:kinesis:us-west-2:12345678901:stream/samplestream
   ```

1. DynamoDB `describe-kinesis-streaming-destination` コマンドを使用して、テーブルで Kinesis ストリーミングがアクティブかどうかを確認します。

   ```
   aws dynamodb describe-kinesis-streaming-destination --table-name Music
   ```

1. 「[DynamoDB デベロッパーガイド](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/getting-started-step-2.html)」の説明通りに、`put-item` コマンドを使用して DynamoDB テーブルにデータを書き込みます。

   ```
   aws dynamodb put-item \
       --table-name Music  \
       --item \
           '{"Artist": {"S": "No One You Know"}, "SongTitle": {"S": "Call Me Today"}, "AlbumTitle": {"S": "Somewhat Famous"}, "Awards": {"N": "1"}}'
   
   aws dynamodb put-item \
       --table-name Music \
       --item \
           '{"Artist": {"S": "Acme Band"}, "SongTitle": {"S": "Happy Day"}, "AlbumTitle": {"S": "Songs About Life"}, "Awards": {"N": "10"} }'
   ```

1. Kinesis [get-records](https://docs.aws.amazon.com/cli/latest/reference/kinesis/get-records.html) CLI コマンドを使用して、Kinesis ストリームコンテンツを取得します。次に、以下のコードスニペットを使用して、ストリーミングコンテンツを逆シリアル化します。

   ```
   /**
    * Takes as input a Record fetched from Kinesis and does arbitrary processing as an example.
    */
   public void processRecord(Record kinesisRecord) throws IOException {
       ByteBuffer kdsRecordByteBuffer = kinesisRecord.getData();
       JsonNode rootNode = OBJECT_MAPPER.readTree(kdsRecordByteBuffer.array());
       JsonNode dynamoDBRecord = rootNode.get("dynamodb");
       JsonNode oldItemImage = dynamoDBRecord.get("OldImage");
       JsonNode newItemImage = dynamoDBRecord.get("NewImage");
       Instant recordTimestamp = fetchTimestamp(dynamoDBRecord);
   
       /**
        * Say for example our record contains a String attribute named "stringName" and we want to fetch the value
        * of this attribute from the new item image. The following code fetches this value.
        */
       JsonNode attributeNode = newItemImage.get("stringName");
       JsonNode attributeValueNode = attributeNode.get("S"); // Using DynamoDB "S" type attribute
       String attributeValue = attributeValueNode.textValue();
       System.out.println(attributeValue);
   }
   
   private Instant fetchTimestamp(JsonNode dynamoDBRecord) {
       JsonNode timestampJson = dynamoDBRecord.get("ApproximateCreationDateTime");
       JsonNode timestampPrecisionJson = dynamoDBRecord.get("ApproximateCreationDateTimePrecision");
       if (timestampPrecisionJson != null && timestampPrecisionJson.equals("MICROSECOND")) {
           return Instant.EPOCH.plus(timestampJson.longValue(), ChronoUnit.MICROS);
       }
       return Instant.ofEpochMilli(timestampJson.longValue());
   }
   ```

------
#### [ Java ]

1. 「Kinesis Data Streams デベロッパーガイド」の指示に従って、Java を使用し `samplestream` という名前の Kinesis データストリームを[作成](https://docs.aws.amazon.com/streams/latest/dev/kinesis-using-sdk-java-create-stream.html)します。

   Kinesis Data Streams のシャード数を設定する前に [Kinesis Data Streams のシャード管理に関する考慮事項](kds_using-shards-and-metrics.md#kds_using-shards-and-metrics.shardmanagment) を参照してください。

1. 次のコードスニペットを使用して、DynamoDB テーブルで Kinesis ストリーミングを有効にします。オプションで、各レコードに返されるタイムスタンプ値の精度をより細かく (マイクロ秒) したストリーミングを有効にします。

   マイクロ秒のタイムスタンプ精度でのストリーミングを有効にします。

   ```
   EnableKinesisStreamingConfiguration enableKdsConfig = EnableKinesisStreamingConfiguration.builder()
     .approximateCreationDateTimePrecision(ApproximateCreationDateTimePrecision.MICROSECOND)
     .build();
   
   EnableKinesisStreamingDestinationRequest enableKdsRequest = EnableKinesisStreamingDestinationRequest.builder()
     .tableName(tableName)
     .streamArn(kdsArn)
     .enableKinesisStreamingConfiguration(enableKdsConfig)
     .build();
   
   EnableKinesisStreamingDestinationResponse enableKdsResponse = ddbClient.enableKinesisStreamingDestination(enableKdsRequest);
   ```

   または、デフォルトのタイムスタンプ精度 (ミリ秒) でのストリーミングを有効にします。

   ```
   EnableKinesisStreamingDestinationRequest enableKdsRequest = EnableKinesisStreamingDestinationRequest.builder()
     .tableName(tableName)
     .streamArn(kdsArn)
     .build();
   
   EnableKinesisStreamingDestinationResponse enableKdsResponse = ddbClient.enableKinesisStreamingDestination(enableKdsRequest);
   ```

1. 「*Kinesis Data Streams デベロッパーガイド*」の指示に従って、作成したデータストリームから[読み込み](https://docs.aws.amazon.com/streams/latest/dev/building-consumers.html)ます。

1. 次のコードスニペットを使用して、ストリームコンテンツを逆シリアル化します。

   ```
   /**
    * Takes as input a Record fetched from Kinesis and does arbitrary processing as an example.
    */
   public void processRecord(Record kinesisRecord) throws IOException {
       ByteBuffer kdsRecordByteBuffer = kinesisRecord.getData();
       JsonNode rootNode = OBJECT_MAPPER.readTree(kdsRecordByteBuffer.array());
       JsonNode dynamoDBRecord = rootNode.get("dynamodb");
       JsonNode oldItemImage = dynamoDBRecord.get("OldImage");
       JsonNode newItemImage = dynamoDBRecord.get("NewImage");
       Instant recordTimestamp = fetchTimestamp(dynamoDBRecord);
   
       /**
        * Say for example our record contains a String attribute named "stringName" and we wanted to fetch the value
        * of this attribute from the new item image, the below code would fetch this.
        */
       JsonNode attributeNode = newItemImage.get("stringName");
       JsonNode attributeValueNode = attributeNode.get("S"); // Using DynamoDB "S" type attribute
       String attributeValue = attributeValueNode.textValue();
       System.out.println(attributeValue);
   }
   
   private Instant fetchTimestamp(JsonNode dynamoDBRecord) {
       JsonNode timestampJson = dynamoDBRecord.get("ApproximateCreationDateTime");
       JsonNode timestampPrecisionJson = dynamoDBRecord.get("ApproximateCreationDateTimePrecision");
       if (timestampPrecisionJson != null && timestampPrecisionJson.equals("MICROSECOND")) {
           return Instant.EPOCH.plus(timestampJson.longValue(), ChronoUnit.MICROS);
       }
       return Instant.ofEpochMilli(timestampJson.longValue());
   }
   ```

------

## アクティブな Amazon Kinesis データストリームに変更を加える
<a name="kds_gettingstarted.making-changes"></a>

このセクションでは、コンソール、AWS CLI、API を使用して DynamoDB 用 Kinesis Data Streams のセットアップを変更する方法について説明します。

**AWS マネジメントコンソール**

1. DynamoDB コンソール ([https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/)) を開きます。

1. テーブルに移動します。

1. **[エクスポートおよびストリーム]** タブを選択します。

**AWS CLI**

1. `describe-kinesis-streaming-destination` を呼び出して、ストリームが `ACTIVE` であることを確認します。

1. 次の例のように、`UpdateKinesisStreamingDestination` を呼び出します。

   ```
   aws dynamodb update-kinesis-streaming-destination --table-name enable_test_table --stream-arn arn:aws:kinesis:us-east-1:12345678901:stream/enable_test_stream --update-kinesis-streaming-configuration ApproximateCreationDateTimePrecision=MICROSECOND
   ```

1. `describe-kinesis-streaming-destination` を呼び出して、ストリームが `UPDATING` であることを確認します。

1. ストリーミングステータスが再び `ACTIVE` になるまで `describe-kinesis-streaming-destination` を定期的に呼び出します。タイムスタンプの精度の更新が有効になるまで、最大 5 分かかることがあります。このステータスが更新されると、更新が完了したことを表し、新しい精度値が今後のレコードに適用されます。

1. `putItem` を使用してテーブルに書き込みます。

1. Kinesis `get-records` コマンドを使用して、Kinesis ストリームコンテンツを取得します。

1. 書き込みの `ApproximateCreationDateTime` の精度が希望どおりであることを確認します。

**Java API**

1. `UpdateKinesisStreamingDestination` リクエストと `UpdateKinesisStreamingDestination` レスポンスを構成するコードスニペットを提供します。

1. `DescribeKinesisStreamingDestination` リクエストと `DescribeKinesisStreamingDestination response` を構成するコードスニペットを提供します。

1. ストリーミングのステータスが、更新が完了し、将来のレコードに新しい精度値が適用されることを示す `ACTIVE` に戻るまで、`describe-kinesis-streaming-destination` を定期的に呼び出します。

1. テーブルへの書き込みを実行します。

1.  ストリームから読み取り、ストリームコンテンツを逆シリアル化します。

1. 書き込みの `ApproximateCreationDateTime` の精度が希望どおりであることを確認します。

# DynamoDB Streams および Kinesis Data Streams でのシャードとメトリクスの使用
<a name="kds_using-shards-and-metrics"></a>

## Kinesis Data Streams のシャード管理に関する考慮事項
<a name="kds_using-shards-and-metrics.shardmanagment"></a>

Kinesis データストリームは、[シャード](https://docs.aws.amazon.com/streams/latest/dev/key-concepts.html)でスループットをカウントします。Amazon Kinesis Data Streams では、データストリームに**オンデマンド**モードと**プロビジョンド**モードのどちらかを選択できます。

DynamoDB の書き込みワークロードが大きく変動しやすく予測不可能な場合は、Kinesis データストリームにオンデマンドモードを使用することをお勧めします。オンデマンドモードでは、Kinesis Data Streams が必要なスループットを提供するためにシャードを自動的に管理するため、キャパシティプランニングは必要ありません。

予測可能なワークロードの場合は、Kinesis データストリームにプロビジョニングモードを使用できます。プロビジョンドモードでは、DynamoDB からの変更データ取得レコードを格納するためのデータストリームのシャード数を指定する必要があります。Kinesis データストリームが DynamoDB テーブルをサポートするために必要なシャードの数を決定するには、次の入力値が必要です。
+ DynamoDB テーブルのレコードの平均サイズ (バイト単位、`average_record_size_in_bytes`)。
+ DynamoDB テーブルで実行される 1 秒あたりの書き込み操作の最大数。これには、アプリケーションで実行される作成、削除、更新操作のほか、Time to Live で生成された削除操作 (`write_throughput`) などの自動生成された操作も含まれます。
+ テーブルに対して実行する作成操作や削除操作と比較した、更新操作と上書き操作の割合 (`percentage_of_updates`)。更新操作と上書き操作では、変更された項目の古いイメージと新しいイメージの両方がストリームにレプリケートされることに留意してください。これにより、DynamoDB 項目のサイズが 2 倍になります。

Kinesis データストリームに必要なシャードの数 (`number_of_shards`) を計算するには、入力値を以下の式にあてはめます。

```
number_of_shards = ceiling( max( ((write_throughput * (4+percentage_of_updates) * average_record_size_in_bytes) / 1024 / 1024), (write_throughput/1000)), 1)
```

例えば、書き込み操作の最大スループットが 1,040 回/秒で (`write_throughput`)、平均レコードサイズが 800 バイト (`average_record_size_in_bytes)` であるとします。 この書き込み操作の 25% が更新操作 (`percentage_of_updates`) である場合、DynamoDB ストリーミングスループットに対応するために 2 つのシャード (`number_of_shards`) が必要になります。

```
ceiling( max( ((1040 * (4+25/100) * 800)/ 1024 / 1024), (1040/1000)), 1).
```

式を使用して Kinesis データストリームのプロビジョニングモードで必要なシャード数を計算する前に、次の点を考慮してください。
+ この式では、DynamoDB 変更データレコードを格納するのに必要なシャードの数を見積もることができます。これは、追加の Kinesis データストリームのコンシューマーをサポートするために必要なシャードの数など、Kinesis データストリームで必要なシャードの総数を表すものではありません。
+ ピークスループットを処理するようにデータストリームを設定しないと、プロビジョンドモードで読み取りおよび書き込みのスループットの例外が発生する可能性があります。この場合、データトラフィックに対応するようにデータストリームを手動でスケーリングする必要があります。
+ この式では、変更ログデータレコードを Kinesis Data Stream にストリーミングする前に DynamoDB によって生成される追加の肥大化を考慮に入れています。

Kinesis データストリームのキャパシティモードの詳細については、「[データストリームのキャパシティモードの選択](https://docs.aws.amazon.com/streams/latest/dev/how-do-i-size-a-stream.html)」を参照してください。さまざまなキャパシティモード間の料金の違いについて詳しくは、「[Amazon Kinesis Data Streams の料金](https://aws.amazon.com/kinesis/data-streams/pricing/)」を参照してください。

## Kinesis Data Streams を使用した変更データキャプチャのモニタリング
<a name="kds_using-shards-and-metrics.monitoring"></a>

DynamoDB には、Kinesis への変更データキャプチャのレプリケーションをモニタリングするのに役立つ複数の Amazon CloudWatch メトリクスが用意されています。CloudWatch メトリクスの詳細な一覧については、「[DynamoDB のメトリクスとディメンション](metrics-dimensions.md)」を参照してください。

ストリームに十分な容量があるかどうかを判断する場合は、ストリームの有効化時と実稼働時の両方で次の項目をモニタリングすることをお勧めします。
+ `ThrottledPutRecordCount`: Kinesis データストリームのキャパシティが不足しているために、Kinesis データストリームによってスロットリングされたレコードの数。例外的な使用量のピーク時にスロットリングが発生する可能性がありますが、`ThrottledPutRecordCount` は可能な限り低く保つ必要があります。DynamoDB は、スロットリングされたレコードを Kinesis データストリームに再送信しますが、これによりレプリケーションのレイテンシーが高くなる可能性があります。

  過剰で定期的なスロットリングが発生した場合は、テーブルで観測された書き込みスループットに比例して Kinesis ストリーミングシャードの数を増やす必要があります。Kinesis Data Streams のサイズ決定の詳細については、「[Kinesis Data Streams の初期サイズの決定](https://docs.aws.amazon.com/streams/latest/dev/amazon-kinesis-streams.html#how-do-i-size-a-stream)」を参照してください。
+ `AgeOfOldestUnreplicatedRecord`: Kinesis Data Streams にまだレプリケートされていない最も古い項目レベルの変更からの経過時間が DynamoDB テーブルに表示されました。通常のオペレーションでは、`AgeOfOldestUnreplicatedRecord` はミリ秒単位で順序を指定しなければなりません。この数字は、カスタマー管理設定上の選択が原因で失敗したレプリケーションの試行回数に基づいて増加します。

   `AgeOfOldestUnreplicatedRecord` メトリクスが 168 時間を超えると、DynamoDB テーブルから Kinesis データストリームへの項目レベルの変更のレプリケーションは自動的に無効になります。

  カスタマー管理設定でレプリケーション試行の失敗の原因になる例として、Kinesis データストリームキャパシティーのプロビジョニングが不足していたために過剰なスロットリングにつながった場合や、Kinesis データストリームのアクセスポリシーを手動で更新したために DynamoDB がデータストリームにデータを追加できなくなった場合が挙げられます。このメトリクスを可能な限り低く保つために、Kinesis データストリームキャパシティーを十分にプロビジョニングし、DynamoDB のアクセス許可が変更されていないことを確認する必要があります。
+ `FailedToReplicateRecordCount`: DynamoDB が Kinesis データストリームにレプリケートできなかったレコードの数。34 KB を超える特定の項目はサイズが拡張されて、Kinesis Data Streams の項目サイズ制限 1MB を超えるデータレコードが変更される場合があります。このサイズの拡張は、34 KB を超えるこれらの項目に多数のブール値や空の属性値が含まれる場合に発生します。ブール値と空の属性値は、DynamoDB に 1 バイトで格納されますが、Kinesis Data Streams レプリケーションで標準 JSON を使用してシリアル化すると、最大 5 バイトまで拡張されます。DynamoDB は、このような変更レコードを Kinesis データストリームにレプリケートできません。DynamoDB は、これらの変更データレコードをスキップし、後続のレコードを自動的にレプリケートします。

   

前述のメトリクスのいずれかが特定のしきい値を超えた場合に通知するために、Amazon Simple Notification Service (Amazon SNS) メッセージを送信する Amazon CloudWatch アラームを作成できます。

# Amazon Kinesis Data Streams および Amazon DynamoDB の IAM ポリシーを使用する
<a name="kds_iam"></a>

Amazon DynamoDB 用 Amazon Kinesis Data Streams を初めて有効にすると、DynamoDB は AWS Identity and Access Management (IAM) サービスにリンクされたロールを自動的に作成します。このロール `AWSServiceRoleForDynamoDBKinesisDataStreamsReplication` を使用すると、DynamoDB はユーザーに代わって Kinesis Data Streams への項目レベルの変更のレプリケーションを管理できます。このサービスにリンクされたロールは削除しないでください。

サービスリンクロールの詳細については、「*IAM ユーザーガイド*」の「[サービスリンクロールの使用](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html)」を参照してください。

**注記**  
DynamoDB は、IAM ポリシーのタグベースの条件をサポートしていません。

Amazon DynamoDB 用 Amazon Kinesis Data Streams を有効にするには、テーブルに対する以下の許可が必要です。
+ `dynamodb:EnableKinesisStreamingDestination`
+ `kinesis:ListStreams`
+ `kinesis:PutRecords`
+ `kinesis:DescribeStream`

特定の DynamoDB テーブルに対する Amazon DynamoDB 用 Amazon Kinesis Data Streams を記述するには、テーブルに対する以下の許可が必要です。
+ `dynamodb:DescribeKinesisStreamingDestination`
+ `kinesis:DescribeStreamSummary`
+ `kinesis:DescribeStream`

Amazon DynamoDB 用 Amazon Kinesis Data Streams を無効にするには、テーブルに対する以下の許可が必要です。
+ `dynamodb:DisableKinesisStreamingDestination`

Amazon DynamoDB 用 Amazon Kinesis Data Streams を更新にするには、テーブルに対する以下の許可が必要です。
+ `dynamodb:UpdateKinesisStreamingDestination`

以下の例は、IAM ポリシーを使用して Amazon DynamoDB 用 Amazon Kinesis Data Streams の許可を付与する方法を示しています。

## 例: Amazon DynamoDB 用 Amazon Kinesis Data Streams を有効にする
<a name="access-policy-kds-example1"></a>

以下の IAM ポリシーは、`Music` テーブルに対して Amazon DynamoDB 用 Amazon Kinesis Data Streams を有効にする権限を付与します。`Music` テーブルに対して DynamoDB 用 Kinesis Data Streams を無効化、更新、記述する権限は付与されません。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iam:CreateServiceLinkedRole",
            "Resource": "arn:aws:iam::*:role/aws-service-role/kinesisreplication.dynamodb.amazonaws.com/AWSServiceRoleForDynamoDBKinesisDataStreamsReplication",
            "Condition": {
                "StringLike": {
                    "iam:AWSServiceName": "kinesisreplication.dynamodb.amazonaws.com"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:EnableKinesisStreamingDestination"
            ],
            "Resource": "arn:aws:dynamodb:us-west-2:111122223333:table/Music"
        }
    ]
}
```

------

## 例: Amazon DynamoDB 用 Amazon Kinesis Data Streams を更新する
<a name="access-policy-kds-example2"></a>

以下の IAM ポリシーは、`Music` テーブルに対して Amazon DynamoDB 用 Amazon Kinesis Data Streams を更新する権限を付与します。`Music` テーブルに対して DynamoDB 用 Kinesis Data Streams を有効化、無効化、記述する権限は付与されません。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:UpdateKinesisStreamingDestination"
            ],
            "Resource": "arn:aws:dynamodb:us-west-2:111122223333:table/Music"
        }
    ]
}
```

------

## 例: Amazon DynamoDB 用 Amazon Kinesis Data Streams を無効にする
<a name="access-policy-kds-example2"></a>

以下の IAM ポリシーは、`Music` テーブルに対して Amazon DynamoDB 用 Amazon Kinesis Data Streams を無効にする権限を付与します。`Music` テーブルに対して Amazon DynamoDB 用 Amazon Kinesis Data Streams を有効化、更新、記述する権限は付与されません。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:DisableKinesisStreamingDestination"
            ],
            "Resource": "arn:aws:dynamodb:us-west-2:111122223333:table/Music"
        }
    ]
}
```

------

## 例: リソースに基づいて、選択的に Amazon DynamoDB 用 Amazon Kinesis Data Streams に許可を適用する
<a name="access-policy-kds-example3"></a>

以下の IAM ポリシーは、`Music` テーブルの Amazon DynamoDB 用 Amazon Kinesis Data Streams を有効にするか、記述する許可を付与しますが、`Orders` テーブルの Amazon DynamoDB 用 Amazon Kinesis Data Streams を無効にする許可は拒否します。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:EnableKinesisStreamingDestination",
                "dynamodb:DescribeKinesisStreamingDestination"
            ],
            "Resource": "arn:aws:dynamodb:us-west-2:111122223333:table/Music"
        },
        {
            "Effect": "Deny",
            "Action": [
                "dynamodb:DisableKinesisStreamingDestination"
            ],
            "Resource": "arn:aws:dynamodb:us-west-2:111122223333:table/Orders"
        }
    ]
}
```

------

## DynamoDB 用 Kinesis Data Streams に対するサービスにリンクされたロールの使用
<a name="kds-service-linked-roles"></a>

Amazon DynamoDB 用 Amazon Kinesis Data Streams は、AWS Identity and Access Management (IAM) の[サービスにリンクされたロール](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_terms-and-concepts.html#iam-term-service-linked-role)を使用します。サービスにリンクされたロールは、一意のタイプの IAM ロールで、DynamoDB 用 Kinesis Data Streams に直接リンクされます。サービスにリンクされたロールは、DynamoDB 用 Kinesis Data Streams によって事前定義されており、お客様の代わりにサービスから他の AWS のサービスを呼び出す必要のあるアクセス許可がすべて含まれています。

サービスにリンクされたロールを使用することで、必要なアクセス権限を手動で追加する必要がなくなるため、DynamoDB 用 Kinesis Data Streams の設定が簡単になります。DynamoDB 用 Kinesis Data Streams は、サービスにリンクされたロールの許可を定義します。特に定義されている場合を除き、DynamoDB 用 Kinesis Data Streams のみがそのロールを引き受けることができます。定義される許可は、信頼ポリシーと許可ポリシーに含まれており、その許可ポリシーを他の IAM エンティティにアタッチすることはできません。

サービスリンクロールをサポートする他のサービスについては、「[IAM と連携する AWS のサービス](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html)」を参照して、**[サービスリンクロール]** 列が **[はい]** になっているサービスを見つけてください。サービスにリンクされたロールに関するドキュメントをサービスで表示するには、[**はい**] リンクを選択します。

### DynamoDB 用 Kinesis Data Streams に対するサービスにリンクされたロールの許可
<a name="slr-permissions"></a>

DynamoDB 用 Kinesis Data Streams では、**AWSServiceRoleForDynamoDBKinesisDataStreamsReplication** という名前の、サービスにリンクされたロールを使用します。サービスにリンクされたロールの目的は、Amazon DynamoDB がお客様に代わって Kinesis Data Streams に対する項目レベルの変更のレプリケーションを管理できるように許可することです。

`AWSServiceRoleForDynamoDBKinesisDataStreamsReplication` サービスリンクロールは、ロールの引き受けについて以下のサービスを信頼します。
+ `kinesisreplication.dynamodb.amazonaws.com`

ロールのアクセス許可ポリシーは、指定したリソースに対して以下のアクションを完了することを DynamoDB 用 Kinesis Data Streams に許可します。
+ アクション: `Kinesis stream` での `Put records and describe`
+ アクション: `AWS KMS` で `Generate data keys`、ユーザーが生成した AWS KMS キーを使用して暗号化された Kinesis ストリームにデータを格納します。

ポリシードキュメントの正確な内容については、「[DynamoDBKinesisReplicationServiceRolePolicy](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/aws-service-role/DynamoDBKinesisReplicationServiceRolePolicy)」を参照してください。

サービスリンクロールの作成、編集、削除を IAM エンティティ (ユーザー、グループ、ロールなど) に許可するにはアクセス許可を設定する必要があります。詳細については、*IAM ユーザーガイド*の「[サービスにリンクされたロールの許可](https://docs.aws.amazon.com/IAM/latest/UserGuide/contributorinsights-service-linked-roles.html#service-linked-role-permissions)」を参照してください。

### DynamoDB 用 Kinesis Data Streams のサービスにリンクされたロールの作成
<a name="create-slr"></a>

サービスにリンクされたロールを手動で作成する必要はありません。AWS マネジメントコンソール、AWS CLI または AWS API で DynamoDB 用 Kinesis Data Streams を有効にすると、DynamoDB 用 Kinesis Data Streams がサービスにリンクされたロールを作成します。

このサービスにリンクされたロールを削除した後で再度作成する必要が生じた場合は、同じ方法でアカウントにロールを再作成できます。DynamoDB 用 Kinesis Data Streams を有効にすると、DynamoDB 用 Kinesis Data Streams がサービスにリンクされたロールを再作成します。

### DynamoDB 用 Kinesis Data Streams のサービスにリンクされたロールの編集
<a name="edit-slr"></a>

DynamoDB 用 Kinesis Data Streams では、`AWSServiceRoleForDynamoDBKinesisDataStreamsReplication` サービスにリンクされたロールを編集することはできません。サービスにリンクされたロールを作成すると、多くのエンティティによってロールが参照される可能性があるため、ロール名を変更することはできません。ただし、IAM を使用してロールの説明を編集することはできます。詳細については、*IAM ユーザーガイド*の「[サービスにリンクされたロールの編集](https://docs.aws.amazon.com/IAM/latest/UserGuide/contributorinsights-service-linked-roles.html#edit-service-linked-role)」を参照してください。

### DynamoDB 用 Kinesis Data Streams のサービスにリンクされたロールの削除
<a name="delete-slr"></a>

サービスにリンクされたロールは、IAM コンソール、AWS CLI 、または AWS API を使用して手動で削除することもできます。そのためにはまず、サービスにリンクされたロールのリソースをクリーンアップする必要があります。その後で、手動で削除できます。

**注記**  
リソースを削除する際に、DynamoDB 用 Kinesis Data Streams サービスでロールが使用されている場合、削除は失敗することがあります。失敗した場合は、数分待ってから再度オペレーションを実行してください。

**IAM を使用してサービスリンクロールを手動で削除するには**

IAM コンソール、AWS CLI、または AWS API を使用して、`AWSServiceRoleForDynamoDBKinesisDataStreamsReplication` サービスリンクロールを削除します。詳細については、*IAM ユーザーガイド*の「[サービスにリンクされたロールの削除](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html)」を参照してください。

# DynamoDB Streams の変更データキャプチャ
<a name="Streams"></a>

 DynamoDB Streams は、DynamoDB テーブル内の項目レベルの変更に関するシーケンスを時間順にキャプチャし、その情報を最大 24 時間ログに保存します。アプリケーションは、このログにアクセスし、データ項目の変更前および変更後の内容をほぼリアルタイムで参照できます。

 保管時の暗号化では、DynamoDB Streams のデータが暗号化されます。詳細については、「[保管時の DynamoDB 暗号化](EncryptionAtRest.md)」を参照してください。

*DynamoDB Streams* は、DynamoDB テーブル内の項目に加えられた変更に関する情報の順序付けされた情報です。テーブルでストリーミングを有効にすると、DynamoDB はテーブル内のデータ項目に加えられた各変更に関する情報をキャプチャします。

アプリケーションがテーブル内の項目を作成、更新、または削除するたびに、DynamoDB Streams は変更された項目のプライマリキー属性を付けてストリーミングレコードを書き込みます。*ストリーミングレコード*には、DynamoDB テーブル内の単一の項目に加えられたデータ変更についての情報が含まれています。ストリームレコードが追加情報（変更された項目の前後のイメージ）をキャプチャするようにストリームを設定できます。

DynamoDB Streams を使用すれば、以下のことを確認できます。
+ 各ストリームレコードは、ストリームに 1 回だけ出現します。
+ DynamoDB テーブルで変更された各項目について、ストリーミングレコードは項目に対する実際の変更と同じ順序で出現します。

DynamoDB Streams は、ストリーミングレコードをほぼリアルタイムで書き込むため、これらのストリーミングを使用し、内容に基づいてアクションを実行するアプリケーションを構築できます。

**Topics**
+ [DynamoDB Streams のエンドポイント](#Streams.Endpoints)
+ [ストリームの有効化](#Streams.Enabling)
+ [ストリームの読み込みと処理](#Streams.Processing)
+ [DynamoDB Streams と有効期限 (TTL)](time-to-live-ttl-streams.md)
+ [DynamoDB Streams Kinesis Adapter を使用したストリームレコードの処理](Streams.KCLAdapter.md)
+ [DynamoDB Streams 低レベル API: Java の例](Streams.LowLevel.Walkthrough.md)
+ [DynamoDB Streams と AWS Lambda のトリガー](Streams.Lambda.md)
+ [DynamoDB Streams と Apache Flink](StreamsApacheFlink.xml.md)

## DynamoDB Streams のエンドポイント
<a name="Streams.Endpoints"></a>

AWS では、DynamoDB と DynamoDB Streams 用に個別のエンドポイントを維持しています。データベースのテーブルとインデックスを使用するには、アプリケーションが DynamoDB エンドポイントにアクセスする必要があります。DynamoDB Streams レコードを読み込んで処理するには、アプリケーションが同じリージョンの DynamoDB Streams エンドポイントにアクセスする必要があります。

DynamoDB Streams には 2 つのエンドポイントセットが用意されています。具体的には次の 2 つです。
+ **IPv4 専用エンドポイント**: `streams.dynamodb.<region>.amazonaws.com` 命名規則を持つエンドポイント。
+ **デュアルスタックエンドポイント**: IPv4 と IPv6 の両方と互換性があり、`streams-dynamodb.<region>.api.aws` 命名規則に従う新しいエンドポイント。

**注記**  
DynamoDB および DynamoDB Streams のリージョンとエンドポイントの完全なリストについては、「*AWS 全般のリファレンス*」の「[リージョンとエンドポイント](https://docs.aws.amazon.com/general/latest/gr/rande.html)」を参照してください。

AWS SDK は、DynamoDB と DynamoDB Streams 用に個別のクライアントを提供します。要件によっては、アプリケーションは、DynamoDB エンドポイント、DynamoDB Streams エンドポイント、または両方に同時にアクセスできます。両方のエンドポイントに接続するには、アプリケーションで 2 つのクライアントをインスタンス化する必要があります。1 つは DynamoDB 用、もう 1 つは DynamoDB Streams 用です。

## ストリームの有効化
<a name="Streams.Enabling"></a>

新しいテーブルでは、AWS CLI または AWS SDK 経由でそのテーブルの作成時にストリームを有効にできます。また、既存のテーブルでストリーミングを有効または無効にすることや、ストリーミングの設定を変更することができます。DynamoDB Streams は非同期的に動作するため、ストリーミングを有効にしてもテーブルのパフォーマンスに影響はありません。

DynamoDB Streams を管理する最も簡単な方法は、AWS マネジメントコンソール を使用することです。

1. AWS マネジメントコンソール にサインインして DynamoDB コンソール ([https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/)) を開きます。

1. DynamoDB コンソールのダッシュボードで、[**Tables (テーブル)**] を選択して既存テーブルを選びます。

1. [**エクスポートとストリーム**] タブを選択します。

1. **[DynamoDB ストリームの詳細]** セクションで、**[オンにする]** を選択します。

1. **[DynamoDB ストリームをオンにする]** ウィンドウで、テーブルのデータが変更されるたびにストリーミングに書き込まれる情報を選択します。
   + [**キー属性のみ**] - 変更された項目のキー属性のみ。
   + **[New image]** (新規イメージ) — 変更後に表示される項目全体。
   + **[Old image]** (古いイメージ) — 変更前に表示されていた項目全体。
   + **[New and old images]** (新規イメージおよび古いイメージ) — 項目の新しいイメージと古いイメージの両方。

   すべての設定が正しいことを確認したら、**[ストリームをオンにする]** を選択します。

1. (オプション) 既存のストリーミングを無効にするには、**[DynamoDB ストリームの詳細]** で **[オフにする]** を選択します。

`CreateTable` または `UpdateTable` API オペレーションを使用して、ストリームを有効にするか、変更することもできます。ストリームの設定内容は、`StreamSpecification` パラメータにより決まります。
+ `StreamEnabled` — テーブルでストリーミングが有効 (`true`) か無効 (`false`) かを指定します。
+ `StreamViewType` — テーブル内のデータが変更されるたびにストリーミングに書き込まれる情報を指定します。
  + `KEYS_ONLY` — 変更された項目のキー属性のみ。
  + `NEW_IMAGE` — 変更後に表示される項目全体。
  + `OLD_IMAGE` — 変更前に表示されていた項目全体。
  + `NEW_AND_OLD_IMAGES` — 項目の新しいイメージと古いイメージの両方。

ストリームはいつでも有効または無効にできます。ただし、既にストリームがあるテーブルでストリームを有効にしようとした場合、`ValidationException` を受け取ります。また、ストリームのないテーブルでストリームを無効にしようとすると、`ValidationException` が発生します。

`StreamEnabled` を `true` に設定すると、一意のストリーミング記述子が割り当てられた新しいストリーミングが DynamoDB で作成されます。テーブルでストリームを無効にして再度有効にすると、新しいストリームは異なるストリーム記述子で作成されます。

各ストリームは、Amazon リソースネーム（ARN）により一意に識別されます。次に、`TestTable` という名前の DynamoDB テーブルにあるストリーミングのサンプル ARN を示します。

```
arn:aws:dynamodb:us-west-2:111122223333:table/TestTable/stream/2015-05-11T21:21:33.291
```

テーブルの最新のストリーミング記述子を調べるには、DynamoDB `DescribeTable` リクエストを発行し、レスポンスで `LatestStreamArn` 要素を探します。

**注記**  
ストリームのセットアップ後は `StreamViewType` を編集できません。セットアップ後にストリームを変更する必要がある場合は、現在のストリームを無効にして新しいストリームを作成する必要があります。

## ストリームの読み込みと処理
<a name="Streams.Processing"></a>

ストリームを読み取って処理するには、アプリケーションから DynamoDB Streams エンドポイントに接続して API リクエストを発行する必要があります。

ストリームは、*ストリームレコード*で構成されています。各ストリーミングレコードは、ストリーミングが属する DynamoDB テーブル内の 1 件のデータ変更を表しています。各ストリームレコードには、レコードがストリームに発行された順序を反映したシーケンス番号が割り当てられます。

ストリームレコードは、グループ (つまり、*シャード*) に整理されます。各シャードは、複数のストリームレコードのコンテナとして機能し、これらのレコードへのアクセスと反復処理に必要な情報が含まれています。シャード内のストリームレコードは 24 時間後に自動的に削除されます。

シャードはエフェメラルであり、必要に応じて自動的に作成および削除されます。また、任意のシャードは複数の新しいシャードに分割できます。これもまた自動的に行われます (親シャードが 1 つの子シャードのみを持つ場合もあります)。アプリケーションが複数のシャードからレコードを並列処理できるように、シャードは親テーブルで高レベルな書き込みアクティビティに応じて分割される場合があります。

ストリームを無効にすると、開かれているシャードは閉じられます。ストリーミング内のデータは 24 時間読み込み可能な状態になります。

シャードには系列 (親と子) があるため、アプリケーションは子シャードを処理する前に、必ず親シャードを処理する必要があります。これにより、ストリームレコードも正しい順序で処理されるようになります。(DynamoDB Streams Kinesis Adapter を使用している場合、これは自動的に処理されます。アプリケーションは、シャードとストリーミングレコードを正しい順序で処理します。アプリケーションの実行中に分割されたシャードに加えて、新しいシャードまたは有効期限切れのシャードは自動的に処理されます。詳細については、「[DynamoDB Streams Kinesis Adapter を使用したストリームレコードの処理](Streams.KCLAdapter.md)」を参照してください。)

次の図は、ストリーム、ストリーム内のシャード、シャード内のストリームレコードの関係を示しています。

![\[DynamoDB Streams 構造。データ変更を表すストリームレコードは、シャードに編成されます。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/streams-terminology.png)


**注記**  
項目内のデータを何も変更しない `PutItem` または `UpdateItem` オペレーションを実行した場合、そのオペレーションのストリーミングレコードは DynamoDB Streams によって書き込まれ*ません*。

ストリームにアクセスしてその中のストリームレコードを処理するには、以下の操作を実行する必要があります。
+ アクセスするストリームの一意の ARN を調べます。
+ 目的のストリームレコードがストリーム内のどのシャードに含まれているかを調べます。
+ シャードにアクセスし、目的のストリームレコードを取得します。

**注記**  
最大でも 2 つを超えるプロセスが、同時に同じストリームシャードから読み込みを行うことはできません。シャードごとに 2 つを超えるリーダーがあると、スロットリングが発生する場合があります。

DynamoDB Streams API は、アプリケーションプログラム用の以下のアクションを提供します。
+  `[ListStreams](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_ListStreams.html)` — 現在のアカウントおよびエンドポイントのストリーミング記述子のリストを返します。必要に応じて、特定のテーブル名のストリーム記述子だけをリクエストできます。
+ `[DescribeStream](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_DescribeStream.html)` — ストリームに関する情報 (例: ストリームの最新ステータス、Amazon リソースネーム (ARN)、シャードの構成、対応する DynamoDB テーブル) を返します。オプションで `ShardFilter` フィールドを使用して、親シャードに関連付けられた既存の子シャードを取得できます。
+ `[GetShardIterator](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_GetShardIterator.html)` — シャード内の場所を表す*シャードイテレーター*を返します。イテレータがストリーム内の最も古いポイント、最も新しいポイント、特定のポイントへのアクセスを提供することをリクエストできます。
+ `[GetRecords](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_GetRecords.html)` — 特定のシャード内からストリーミングレコードを返します。`GetShardIterator` リクエストから返されたシャードイテレーターを指定する必要があります。

リクエストやレスポンスの例など、これらの API オペレーションの詳細な説明については、「[Amazon DynamoDB Streams API リファレンス](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Operations_Amazon_DynamoDB_Streams.html)」を参照してください。

### シャード検出
<a name="Streams.ShardDiscovery"></a>



2 つの強力な方法で、DynamoDB ストリーム内の新しいシャードを検出します。Amazon DynamoDB Streams ユーザーには、新しいシャードを追跡して識別するための 2 つの効果的な方法があります。

**ストリームトポロジ全体のポーリング**  
`DescribeStream` API を使用して、ストリームを定期的にポーリングします。これにより、作成された新しいシャードを含む、ストリーム内のすべてのシャードが返されます。時間経過に伴う結果を比較することで、新しく追加されたシャードを検出できます。

**子シャードの検出**  
`DescribeStream` API を `ShardFilter` パラメータとともに使用して、シャードのサブセットを見つけます。リクエストで親シャードを指定することで、DynamoDB Streams は直下の子シャードを返します。このアプローチは、ストリーム全体をスキャンせずにシャード系統のみを追跡する必要がある場合に役立ちます。  
DynamoDB Streams からデータを使用するアプリケーションは、この `ShardFilter` パラメータを使用して閉じたシャードの読み取りから子シャードに効率的に移行でき、`DescribeStream` API への繰り返しの呼び出しを回避して、すべての閉じたシャードと開いているシャードのシャードマップを取得してトラバースできます。これにより、親シャードが閉じられた後に子シャードをすばやく検出できるため、ストリーム処理アプリケーションの応答性とコスト効率が向上します。

どちらの方法でも、DynamoDB Streams の進化する構造を常に把握できるため、重要なデータの更新やシャードの変更を見逃すことはありません。

### DynamoDB Streams のデータ保持期限
<a name="Streams.DataRetention"></a>

DynamoDB Streams 内のすべてのデータは、24 時間保持されます。特定のテーブルの直近 24 時間のアクティビティを取得して分析できます。ただし、24 時間を超えたデータはすぐにトリミング (削除) される可能性があります。

テーブルのストリームを無効にした場合、ストリーム内のデータは 24 時間読み込み可能な状態になります。この時間が経過すると、データは期限切れになり、ストリームレコードは自動的に削除されます。既存のストリームを手動で削除するためのメカニズムはありません。保持期限 (24 時間) が切れ、すべてのストリームレコードが削除されるまで待つ必要があります。

# DynamoDB Streams と有効期限 (TTL)
<a name="time-to-live-ttl-streams"></a>

テーブルに対して Amazon DynamoDB Streams を有効にし、期限切れの項目のストリーミングレコードを処理することで、[有効期限](TTL.md) (TTL) によって削除された項目をバックアップ (または処理) できます。詳細については、「[ストリームの読み込みと処理](Streams.md#Streams.Processing)」を参照してください。

ストリームレコードにはユーザー ID フィールド `Records[<index>].userIdentity` が含まれます。

有効期限切れの後に有効期限 (TTL) プロセスによって削除された項目には、次のフィールドが含まれています。
+ `Records[<index>].userIdentity.type`

  `"Service"`
+ `Records[<index>].userIdentity.principalId`

  `"dynamodb.amazonaws.com"`

**注記**  
TTL をグローバルテーブルで使用すると、TTL が実行されたリージョンに `userIdentity` フィールドが設定されます。削除が複製されても、このフィールドは他のリージョンには設定されません。

次の JSON は 1 つのストリームレコードの関連する部分を示しています。

```
"Records": [
    {
        ...

        "userIdentity": {
            "type": "Service",
            "principalId": "dynamodb.amazonaws.com"
        }

        ...

    }
]
```

## DynamoDB Streams と Lambda を使用して TTL 削除済みアイテムをアーカイブする
<a name="streams-archive-ttl-deleted-items"></a>

[DynamoDB 有効期限 (TTL)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html)、[DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html) および [AWS Lambda](https://aws.amazon.com/lambda/) を組み合わせると、データのアーカイブを簡素化し、DynamoDB ストレージコストを削減し、コードの複雑さを軽減するのに役立ちます。ストリームコンシューマーとして Lambda を使用すると、Kinesis Client Library (KCL) などの他のコンシューマーと比較してコストが削減されるなど、多くの利点があります。Lambda を使用してイベントを消費する場合、DynamoDB ストリームの `GetRecords` API 呼び出しでは課金が発生しません。Lambda はストリームイベント内の JSON パターンを識別してイベントフィルタリングを提供できます。イベントパターンのコンテンツフィルタリングでは、最大 5 つの異なるフィルターを定義して、処理のために Lambda に送信されるイベントを制御できます。これにより、Lambda 関数の呼び出しを減らしてコードを簡素化し、全体的なコストを削減できます。

DynamoDB Streams には、`Create`、`Modify` および`Remove` アクションなどのすべてのデータ変更が含まれています。これは、アーカイブ Lambda 関数の不要な呼び出しを引き起こす可能性があります。例えば、1 時間あたり 200 万件のデータ変更がストリームに流れ込むテーブルがあり、そのうち 5% 未満が TTL プロセスによって期限切れになり、アーカイブする必要があるアイテム削除であるとします。[Lambda イベントソースフィルター](https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html)を使用すると、Lambda 関数は 1 時間あたり 100,000 回しか呼び出されません。イベントフィルタリングを使用した結果、イベントフィルタリングを行わなければ 200 万回の呼び出しが発生するところを、必要な呼び出しに対してのみ課金されることになります。

イベントフィルタリングは、[Lambda イベントソースマッピング](https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventsourcemapping.html)に適用されます。これは、選択されたイベント (DynamoDB ストリーム) から読み取り、Lambda 関数を呼び出すリソースです。次の図は、ストリームとイベントフィルターを使用して Lambda 関数によって有効期限 (TTL) 削除済みアイテムがどのように消費されるかを示しています。

![\[TTL プロセスによって削除された項目は、ストリームとイベントフィルターを使用する Lambda 関数を開始します。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/streams-lambda-ttl.png)


### DynamoDB の有効期限 (TTL) イベントフィルターパターン
<a name="ttl-event-filter-pattern"></a>

イベントソースマッピングの[フィルター条件](https://docs.aws.amazon.com/lambda/latest/dg/API_FilterCriteria.html)に次の JSON を追加することで、TTL 削除済みアイテムに対してのみ Lambda 関数の呼び出しを許可します。

```
{
    "Filters": [
        {
            "Pattern": { "userIdentity": { "type": ["Service"], "principalId": ["dynamodb.amazonaws.com"] } }
        }
    ]
}
```

### AWS Lambda イベントソースマッピングを作成します。
<a name="create-event-source-mapping"></a>

次のコードスニペットを使用して、テーブルの DynamoDB ストリームに接続できる、フィルター処理されたイベントソースマッピングを作成します。各コードブロックには、イベントフィルターパターンが含まれます。

------
#### [ AWS CLI ]

```
aws lambda create-event-source-mapping \
--event-source-arn 'arn:aws:dynamodb:eu-west-1:012345678910:table/test/stream/2021-12-10T00:00:00.000' \
--batch-size 10 \
--enabled \
--function-name test_func \
--starting-position LATEST \
--filter-criteria '{"Filters": [{"Pattern": "{\"userIdentity\":{\"type\":[\"Service\"],\"principalId\":[\"dynamodb.amazonaws.com\"]}}"}]}'
```

------
#### [ Java ]

```
LambdaClient client = LambdaClient.builder()
        .region(Region.EU_WEST_1)
        .build();

Filter userIdentity = Filter.builder()
        .pattern("{\"userIdentity\":{\"type\":[\"Service\"],\"principalId\":[\"dynamodb.amazonaws.com\"]}}")
        .build();

FilterCriteria filterCriteria = FilterCriteria.builder()
        .filters(userIdentity)
        .build();

CreateEventSourceMappingRequest mappingRequest = CreateEventSourceMappingRequest.builder()
        .eventSourceArn("arn:aws:dynamodb:eu-west-1:012345678910:table/test/stream/2021-12-10T00:00:00.000")
        .batchSize(10)
        .enabled(Boolean.TRUE)
        .functionName("test_func")
        .startingPosition("LATEST")
        .filterCriteria(filterCriteria)
        .build();

try{
    CreateEventSourceMappingResponse eventSourceMappingResponse = client.createEventSourceMapping(mappingRequest);
    System.out.println("The mapping ARN is "+eventSourceMappingResponse.eventSourceArn());

}catch (ServiceException e){
    System.out.println(e.getMessage());
}
```

------
#### [ Node ]

```
const client = new LambdaClient({ region: "eu-west-1" });

const input = {
    EventSourceArn: "arn:aws:dynamodb:eu-west-1:012345678910:table/test/stream/2021-12-10T00:00:00.000",
    BatchSize: 10,
    Enabled: true,
    FunctionName: "test_func",
    StartingPosition: "LATEST",
    FilterCriteria: { "Filters": [{ "Pattern": "{\"userIdentity\":{\"type\":[\"Service\"],\"principalId\":[\"dynamodb.amazonaws.com\"]}}" }] }
}

const command = new CreateEventSourceMappingCommand(input);

try {
    const results = await client.send(command);
    console.log(results);
} catch (err) {
    console.error(err);
}
```

------
#### [ Python ]

```
session = boto3.session.Session(region_name = 'eu-west-1')
client = session.client('lambda')

try:
    response = client.create_event_source_mapping(
        EventSourceArn='arn:aws:dynamodb:eu-west-1:012345678910:table/test/stream/2021-12-10T00:00:00.000',
        BatchSize=10,
        Enabled=True,
        FunctionName='test_func',
        StartingPosition='LATEST',
        FilterCriteria={
            'Filters': [
                {
                    'Pattern': "{\"userIdentity\":{\"type\":[\"Service\"],\"principalId\":[\"dynamodb.amazonaws.com\"]}}"
                },
            ]
        }
    )
    print(response)
except Exception as e:
    print(e)
```

------
#### [ JSON ]

```
{
  "userIdentity": {
     "type": ["Service"],
     "principalId": ["dynamodb.amazonaws.com"]
   }
}
```

------

# DynamoDB Streams Kinesis Adapter を使用したストリームレコードの処理
<a name="Streams.KCLAdapter"></a>

Amazon DynamoDB からのストリーミングを使用するには、Amazon Kinesis Adapter を使用することをお勧めします。DynamoDB Streams API は、意図的に Kinesis Data Streams と似せています。どちらのサービスでも、データストリーミングは、ストリーミングレコードのコンテナであるシャードで構成されています。両方のサービスの API には `ListStreams`、`DescribeStream`、`GetShards`、および `GetShardIterator` オペレーションが含まれています。(これらの DynamoDB Streams アクションは、Kinesis Data Streams の対応するアクションと似ていますが、100% 同一ではありません)。

DynamoDB Streams ユーザーは、KCL 内のデザインパターンを使用して DynamoDB Streams のシャードとストリーミングレコードを処理できます。これを行うには、DynamoDB Streams Kinesis Adapter を使用します。Kinesis Adapter は Kinesis Data Streams インターフェイスを実装しているため、KCL を使用して DynamoDB Streams からのレコードを消費および処理することができます。DynamoDB Streams Kinesis アダプターのセットアップとインストールの方法については、「[GitHub リポジトリ](https://github.com/awslabs/dynamodb-streams-kinesis-adapter)」を参照してください。

Kinesis Client Library (KCL) を使用して、Kinesis Data Streams のアプリケーションを書き込むことができます。KCL は、低レベルの Kinesis Data Streams API の上で役に立つ抽象化を提供することによりコーディングを簡素化します。KCL の詳細については、「[Amazon Kinesis Data Streams デベロッパーガイド](https://docs.aws.amazon.com/kinesis/latest/dev/developing-consumers-with-kcl.html)」の「*Kinesis Client Library を使用したコンシューマーの開発*」を参照してください。

DynamoDB では、AWS SDK for Java v2.x で KCL バージョン 3.x を使用することをお勧めします。現在の DynamoDB Streams Kinesis Adapter バージョン 1.x と AWS SDK for AWS SDK for Java v1.x は、「[AWS SDK とツールのメンテナンスポリシー](https://docs.aws.amazon.com/sdkref/latest/guide/maint-policy.html)」に従って、移行期間中、意図したとおりライフサイクルを通じて引き続き完全にサポートされます。

**注記**  
Amazon Kinesis Client Library (KCL) バージョン 1.x および 2.x は古くなっています。KCL 1.x は 2026 年 1 月 30 日にサポートが終了します。2026 年 1 月 30 日より前に、バージョン 1.x を使用して KCL アプリケーションを最新の KCL バージョンに移行することを強くお勧めします。最新の KCL バージョンを確認するには、GitHub の「[Amazon Kinesis Client Library](https://github.com/awslabs/amazon-kinesis-client)」ページを参照してください。最新の KCL バージョンの詳細については、「[Kinesis Client Library](https://docs.aws.amazon.com/streams/latest/dev/kcl.html)」を参照してください。KCL 1.x から KCL 3.x への移行については、「KCL 1.x から KCL 3.x への移行」を参照してください。

次の図表は、これらのライブラリがどのように連携するかを示しています。

![\[DynamoDB Streams レコードを処理するための DynamoDB Streams、Kinesis Data Streams、および KCL 間のインタラクション。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/streams-kinesis-adapter.png)


DynamoDB Streams Kinesis Adapter を使用すれば、DynamoDB Streams エンドポイントにシームレスに誘導された API コールを使用して、KCL インターフェイスに対して開発を開始できます。

アプリケーションは起動時に KCL をコールしてワーカーをインスタンス化します。ワーカーに、アプリケーションの構成情報 (ストリーミング記述子や AWS 認証情報、および提供するレコードプロセッサクラスの名前など) を提供する必要があります。レコードプロセッサでコードを実行すると、ワーカーは次のタスクを実行します。
+ ストリームに接続する
+ ストリーミング内のシャードを列挙します。
+ ストリーム内の閉じた親シャードの子シャードをチェックして列挙します
+ シャードと他のワーカー (存在する場合) の関連付けを調整する
+ レコードプロセッサで管理する各シャードのレコードプロセッサをインスタンス化する
+ ストリーミングからレコードを取得します。
+ 高スループット時の GetRecords API 呼び出しレートをスケーリングします (キャッチアップモードが設定されている場合)
+ 対応するレコードプロセッサにレコードを送信する
+ 処理されたレコードのチェックポイントを作成する
+ ワーカーのインスタンス数が変化したときに、シャードとワーカーの関連付けを調整する
+ シャードが分割されたときに、シャードとワーカーの関連付けを調整します。

KCL アダプターは、一時的なスループットの増加を処理するための自動呼び出しレート調整機能であるキャッチアップモードをサポートしています。ストリーム処理の遅延が設定可能なしきい値 (デフォルトは 1 分) を超えると、キャッチアップモードでは GetRecords API の呼び出し頻度が設定可能な値 (デフォルトは 3 倍) でスケーリングされ、レコードの取得速度が上がり、遅延が減少すると通常の状態に戻ります。これは、デフォルトのポーリングレートを使用しているコンシューマーが DynamoDB 書き込みアクティビティに圧迫される可能性がある高スループット期間中に役立ちます。キャッチアップモードは、`catchupEnabled` 設定パラメータ (デフォルトは false) を使用して有効にできます。

**注記**  
こちらに記載されている KCL 概念の説明については、「*Amazon Kinesis Data Streams デベロッパーガイド*」の「[Kinesis Client Library を使用したコンシューマーの開発](https://docs.aws.amazon.com/kinesis/latest/dev/developing-consumers-with-kcl.html)」を参照してください。  
AWS Lambda でのストリームの使用の詳細については、「[DynamoDB Streams と AWS Lambda のトリガー](Streams.Lambda.md)」を参照してください。

# KCL 1.x から KCL 3.x への移行
<a name="streams-migrating-kcl"></a>

## 概要
<a name="migrating-kcl-overview"></a>

このガイドでは、コンシューマーアプリケーションを KCL 1.x から KCL 3.x に移行する手順について説明します。KCL 1.x と KCL 3.x のアーキテクチャの違いにより、移行では互換性を確保するために複数のコンポーネントを更新する必要があります。

KCL 1.x は、KCL 3.x とは異なるクラスとインターフェイスを使用します。まずレコードプロセッサ、レコードプロセッサファクトリ、ワーカークラスを KCL 3.x 互換形式に移行し、KCL 1.x から KCL 3.x への移行手順に従う必要があります。

## 移行手順
<a name="migration-steps"></a>

**Topics**
+ [ステップ 1: レコードプロセッサを移行する](#step1-record-processor)
+ [ステップ 2: レコードプロセッサファクトリーを移行する](#step2-record-processor-factory)
+ [ステップ 3: ワーカーを移行する](#step3-worker-migration)
+ [ステップ 4: KCL 3.x 設定の概要と推奨事項](#step4-configuration-migration)
+ [ステップ 5: KCL 2.x から KCL 3.x に移行する](#step5-kcl2-to-kcl3)

### ステップ 1: レコードプロセッサを移行する
<a name="step1-record-processor"></a>

以下の例は、KCL 1.x DynamoDB Streams Kinesis Adapter に実装されたレコードプロセッサを示しています。

```
package com.amazonaws.kcl;

import com.amazonaws.services.kinesis.clientlibrary.exceptions.InvalidStateException;
import com.amazonaws.services.kinesis.clientlibrary.exceptions.ShutdownException;
import com.amazonaws.services.kinesis.clientlibrary.interfaces.IRecordProcessorCheckpointer;
import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IShutdownNotificationAware;
import com.amazonaws.services.kinesis.clientlibrary.lib.worker.ShutdownReason;
import com.amazonaws.services.kinesis.clientlibrary.types.InitializationInput;
import com.amazonaws.services.kinesis.clientlibrary.types.ProcessRecordsInput;
import com.amazonaws.services.kinesis.clientlibrary.types.ShutdownInput;

public class StreamsRecordProcessor implements IRecordProcessor, IShutdownNotificationAware {
    @Override
    public void initialize(InitializationInput initializationInput) {
        //
        // Setup record processor
        //
    }

    @Override
    public void processRecords(ProcessRecordsInput processRecordsInput) {
        for (Record record : processRecordsInput.getRecords()) {
            String data = new String(record.getData().array(), Charset.forName("UTF-8"));
            System.out.println(data);
            if (record instanceof RecordAdapter) {
                // record processing and checkpointing logic
            }
        }
    }

    @Override
    public void shutdown(ShutdownInput shutdownInput) {
        if (shutdownInput.getShutdownReason() == ShutdownReason.TERMINATE) {
            try {
                shutdownInput.getCheckpointer().checkpoint();
            } catch (ShutdownException | InvalidStateException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void shutdownRequested(IRecordProcessorCheckpointer checkpointer) {
        try {
            checkpointer.checkpoint();
        } catch (ShutdownException | InvalidStateException e) {
            //
            // Swallow exception
            //
            e.printStackTrace();
        }
    }
}
```

**RecordProcessor クラスを移行するには**

1. インターフェイスを `com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor` および `com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IShutdownNotificationAware` から `com.amazonaws.services.dynamodbv2.streamsadapter.processor.DynamoDBStreamsShardRecordProcessor` に変更します。以下に例を示します。

   ```
   // import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
   // import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IShutdownNotificationAware;
   
   import com.amazonaws.services.dynamodbv2.streamsadapter.processor.DynamoDBStreamsShardRecordProcessor;
   ```

1. `initialize` メソッド `processRecords` とメソッドの import ステートメントを更新します。

   ```
   // import com.amazonaws.services.kinesis.clientlibrary.types.InitializationInput;
   import software.amazon.kinesis.lifecycle.events.InitializationInput;
   
   // import com.amazonaws.services.kinesis.clientlibrary.types.ProcessRecordsInput;
   import com.amazonaws.services.dynamodbv2.streamsadapter.model.DynamoDBStreamsProcessRecordsInput;
   ```

1. `shutdownRequested` メソッドを以下の新しいメソッドに置き換えます。`leaseLost`、`shardEnded`、および `shutdownRequested`。

   ```
   //    @Override
   //    public void shutdownRequested(IRecordProcessorCheckpointer checkpointer) {
   //        //
   //        // This is moved to shardEnded(...) and shutdownRequested(ShutdownReauestedInput)
   //        //
   //        try {
   //            checkpointer.checkpoint();
   //        } catch (ShutdownException | InvalidStateException e) {
   //            //
   //            // Swallow exception
   //            //
   //            e.printStackTrace();
   //        }
   //    }
   
       @Override
       public void leaseLost(LeaseLostInput leaseLostInput) {
   
       }
   
       @Override
       public void shardEnded(ShardEndedInput shardEndedInput) {
           try {
               shardEndedInput.checkpointer().checkpoint();
           } catch (ShutdownException | InvalidStateException e) {
               //
               // Swallow the exception
               //
               e.printStackTrace();
           }
       }
   
       @Override
       public void shutdownRequested(ShutdownRequestedInput shutdownRequestedInput) {
           try {
               shutdownRequestedInput.checkpointer().checkpoint();
           } catch (ShutdownException | InvalidStateException e) {
               //
               // Swallow the exception
               //
               e.printStackTrace();
           }
       }
   ```

以下に示しているのは、レコードプロセッサのクラスの更新されたバージョンです。

```
package com.amazonaws.codesamples;

import software.amazon.kinesis.exceptions.InvalidStateException;
import software.amazon.kinesis.exceptions.ShutdownException;
import software.amazon.kinesis.lifecycle.events.InitializationInput;
import software.amazon.kinesis.lifecycle.events.LeaseLostInput;
import com.amazonaws.services.dynamodbv2.streamsadapter.model.DynamoDBStreamsProcessRecordsInput;
import software.amazon.kinesis.lifecycle.events.ShardEndedInput;
import software.amazon.kinesis.lifecycle.events.ShutdownRequestedInput;
import software.amazon.dynamodb.streamsadapter.processor.DynamoDBStreamsShardRecordProcessor;
import software.amazon.dynamodb.streamsadapter.adapter.DynamoDBStreamsKinesisClientRecord;
import com.amazonaws.services.dynamodbv2.streamsadapter.processor.DynamoDBStreamsShardRecordProcessor;
import com.amazonaws.services.dynamodbv2.streamsadapter.adapter.DynamoDBStreamsClientRecord;
import software.amazon.awssdk.services.dynamodb.model.Record;

public class StreamsRecordProcessor implements DynamoDBStreamsShardRecordProcessor {

    @Override
    public void initialize(InitializationInput initializationInput) {
        
    }

    @Override
    public void processRecords(DynamoDBStreamsProcessRecordsInput processRecordsInput) {
        for (DynamoDBStreamsKinesisClientRecord record: processRecordsInput.records())
            Record ddbRecord = record.getRecord();
            // processing and checkpointing logic for the ddbRecord
        }
    }

    @Override
    public void leaseLost(LeaseLostInput leaseLostInput) {
        
    }

    @Override
    public void shardEnded(ShardEndedInput shardEndedInput) {
        try {
            shardEndedInput.checkpointer().checkpoint();
        } catch (ShutdownException | InvalidStateException e) {
            //
            // Swallow the exception
            //
            e.printStackTrace();
        }
    }

    @Override
    public void shutdownRequested(ShutdownRequestedInput shutdownRequestedInput) {
        try {
            shutdownRequestedInput.checkpointer().checkpoint();
        } catch (ShutdownException | InvalidStateException e) {
            //
            // Swallow the exception
            //
            e.printStackTrace();
        }
    }
}
```

**注記**  
DynamoDB Streams Kinesis Adapter は SDKv2 レコードモデルを使用するようになりました。SDKv2 では、複雑な `AttributeValue` オブジェクト (`BS`、`NS`、`M`、`L`、`SS`) が null を返すことはありません。`hasBs()`、`hasNs()`、`hasM()`、`hasL()`、`hasSs()` メソッドを使用して、これらの値が存在するかどうかを確認します。

### ステップ 2: レコードプロセッサファクトリーを移行する
<a name="step2-record-processor-factory"></a>

レコードプロセッサファクトリーは、リースが取得された際にレコードプロセッサの作成を担当します。以下に示しているのは、KCL 1.x ファクトリーの例です。

```
package com.amazonaws.codesamples;

import software.amazon.dynamodb.AmazonDynamoDB;
import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessorFactory;

public class StreamsRecordProcessorFactory implements IRecordProcessorFactory {
    
    @Override
    public IRecordProcessor createProcessor() {
        return new StreamsRecordProcessor(dynamoDBClient, tableName);
    }
}
```

**`RecordProcessorFactory` を移行するには**
+ 実装されているインターフェイスを `com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessorFactory` から `software.amazon.kinesis.processor.ShardRecordProcessorFactory` に変更します。以下に例を示します。

  ```
  // import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
  import software.amazon.kinesis.processor.ShardRecordProcessor;
  
  // import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessorFactory;
  import software.amazon.kinesis.processor.ShardRecordProcessorFactory;
  
  // public class TestRecordProcessorFactory implements IRecordProcessorFactory {
  public class StreamsRecordProcessorFactory implements ShardRecordProcessorFactory {
  
  Change the return signature for createProcessor.
  
  // public IRecordProcessor createProcessor() {
  public ShardRecordProcessor shardRecordProcessor() {
  ```

以下は、3.0 のレコードプロセッサファクトリーの例です。

```
package com.amazonaws.codesamples;

import software.amazon.kinesis.processor.ShardRecordProcessor;
import software.amazon.kinesis.processor.ShardRecordProcessorFactory;

public class StreamsRecordProcessorFactory implements ShardRecordProcessorFactory {

    @Override
    public ShardRecordProcessor shardRecordProcessor() {
        return new StreamsRecordProcessor();
    }
}
```

### ステップ 3: ワーカーを移行する
<a name="step3-worker-migration"></a>

バージョン 3.0 の KCL では、新しいクラス **Scheduler** によって **Worker** クラスが置き換えられます。KCL 1.x のワーカーの例を次に示します。

```
final KinesisClientLibConfiguration config = new KinesisClientLibConfiguration(...)
final IRecordProcessorFactory recordProcessorFactory = new RecordProcessorFactory();
final Worker worker = StreamsWorkerFactory.createDynamoDbStreamsWorker(
        recordProcessorFactory,
        workerConfig,
        adapterClient,
        amazonDynamoDB,
        amazonCloudWatchClient);
```

**ワーカーを移行するには**

1. `import` クラスの `Worker` ステートメントを `Scheduler` クラスと `ConfigsBuilder` クラスのインポートステートメントに変更します。

   ```
   // import com.amazonaws.services.kinesis.clientlibrary.lib.worker.Worker;
   import software.amazon.kinesis.coordinator.Scheduler;
   import software.amazon.kinesis.common.ConfigsBuilder;
   ```

1. `StreamTracker` をインポートし、`StreamsWorkerFactory` のインポートを `StreamsSchedulerFactory` に変更します。

   ```
   import software.amazon.kinesis.processor.StreamTracker;
   // import software.amazon.dynamodb.streamsadapter.StreamsWorkerFactory;
   import software.amazon.dynamodb.streamsadapter.StreamsSchedulerFactory;
   ```

1. アプリケーションを起動するポジションを選択します。`TRIM_HORIZON`、`LATEST` のいずれかになります。

   ```
   import software.amazon.kinesis.common.InitialPositionInStream;
   import software.amazon.kinesis.common.InitialPositionInStreamExtended;
   ```

1. `StreamTracker` インスタンスを作成します。

   ```
   StreamTracker streamTracker = StreamsSchedulerFactory.createSingleStreamTracker(
           streamArn,
           InitialPositionInStreamExtended.newInitialPosition(InitialPositionInStream.TRIM_HORIZON)
   );
   ```

1. `AmazonDynamoDBStreamsAdapterClient` オブジェクトを作成します。

   ```
   import software.amazon.dynamodb.streamsadapter.AmazonDynamoDBStreamsAdapterClient; 
   import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
   import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
   
   ...
   
   AwsCredentialsProvider credentialsProvider = DefaultCredentialsProvider.create();
   
   AmazonDynamoDBStreamsAdapterClient adapterClient = new AmazonDynamoDBStreamsAdapterClient(
           credentialsProvider, awsRegion);
   ```

1. `ConfigsBuilder` オブジェクトを作成します。

   ```
   import software.amazon.kinesis.common.ConfigsBuilder;
   
   ...
   ConfigsBuilder configsBuilder = new ConfigsBuilder(
                   streamTracker,
                   applicationName,
                   adapterClient,
                   dynamoDbAsyncClient,
                   cloudWatchAsyncClient,
                   UUID.randomUUID().toString(),
                   new StreamsRecordProcessorFactory());
   ```

1. 次の例に示すように、`ConfigsBuilder` を使用して `Scheduler` を作成します。

   ```
   import java.util.UUID;
   
   import software.amazon.awssdk.regions.Region;
   import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
   import software.amazon.awssdk.services.cloudwatch.CloudWatchAsyncClient;
   import software.amazon.awssdk.services.kinesis.KinesisAsyncClient;
   
   import software.amazon.kinesis.common.KinesisClientUtil;
   import software.amazon.kinesis.coordinator.Scheduler;
   
   ...
   
                   
   DynamoDbAsyncClient dynamoClient = DynamoDbAsyncClient.builder().region(region).build();
   CloudWatchAsyncClient cloudWatchClient = CloudWatchAsyncClient.builder().region(region).build();
   
                   
   DynamoDBStreamsPollingConfig pollingConfig = new DynamoDBStreamsPollingConfig(adapterClient);
   pollingConfig.idleTimeBetweenReadsInMillis(idleTimeBetweenReadsInMillis);
   
   // Use ConfigsBuilder to configure settings
   RetrievalConfig retrievalConfig = configsBuilder.retrievalConfig();
   retrievalConfig.retrievalSpecificConfig(pollingConfig);
   
   CoordinatorConfig coordinatorConfig = configsBuilder.coordinatorConfig();
   coordinatorConfig.clientVersionConfig(CoordinatorConfig.ClientVersionConfig.CLIENT_VERSION_CONFIG_COMPATIBLE_WITH_2X);
                   
   Scheduler scheduler = StreamsSchedulerFactory.createScheduler(
                   configsBuilder.checkpointConfig(),
                   coordinatorConfig,
                   configsBuilder.leaseManagementConfig(),
                   configsBuilder.lifecycleConfig(),
                   configsBuilder.metricsConfig(),
                   configsBuilder.processorConfig(),
                   retrievalConfig,
                   adapterClient
           );
   ```

**重要**  
この `CLIENT_VERSION_CONFIG_COMPATIBLE_WITH_2X` 設定は、KCL v2 と v3 ではなく、KCL v3 と KCL v1 の DynamoDB Streams Kinesis Adapter 間の互換性を維持します。

### ステップ 4: KCL 3.x 設定の概要と推奨事項
<a name="step4-configuration-migration"></a>

KCL 3.x に関連する KCL 1.x 後に導入された設定の詳細については、「[KCL 設定](https://docs.aws.amazon.com//streams/latest/dev/kcl-configuration.html)」と「[KCL 移行クライアント設定](https://docs.aws.amazon.com//streams/latest/dev/kcl-migration.html#client-configuration)」を参照してください。

**重要**  
KCL 3.x 以降のバージョンでは、`checkpointConfig`、`coordinatorConfig`、`leaseManagementConfig`、`metricsConfig`、`processorConfig`、および `retrievalConfig` オブジェクトを直接作成する代わりに、スケジューラの初期化の問題を回避するために、`ConfigsBuilder` を使用して設定を行うことをお勧めします。`ConfigsBuilder` を使用すると、KCL アプリケーションをより柔軟で保守性の高い方法で構成できます。

#### KCL 3.x でデフォルト値を更新する設定
<a name="kcl3-configuration-overview"></a>

`billingMode`  
KCL バージョン 1.x では、`billingMode` のデフォルト値は `PROVISIONED` に設定されます。ただし、KCL バージョン 3.x では、デフォルトの `billingMode` は `PAY_PER_REQUEST` (オンデマンドモード) です。使用量に基づいて容量を自動的に調整するには、リーステーブルのオンデマンドキャパシティモードを使用することをお勧めします。リーステーブルにプロビジョンドキャパシティを使用するガイダンスについては、「[プロビジョンドキャパシティモードのリーステーブルのベストプラクティス](https://docs.aws.amazon.com//streams/latest/dev/kcl-migration-lease-table.html)」を参照してください。

`idleTimeBetweenReadsInMillis`  
KCL バージョン 1.x では、`idleTimeBetweenReadsInMillis` のデフォルト値は 1,000 (または 1 秒) に設定されています。KCL バージョン 3.x は i`dleTimeBetweenReadsInMillis` のデフォルト値を 1,500 (または 1.5 秒) に設定しますが、Amazon DynamoDB Streams Kinesis Adapter はデフォルト値を 1,000 (または 1 秒) に上書きします。

#### KCL 3.x の新しい設定
<a name="kcl3-new-configs"></a>

`leaseAssignmentIntervalMillis`  
この設定では、新しく検出されたシャードが処理を開始するまでの時間間隔を定義し、1.5 × `leaseAssignmentIntervalMillis` として計算されます。この設定が明示的に設定されていない場合、時間間隔はデフォルトで 1.5 × `failoverTimeMillis` に設定されます。新しいシャードの処理には、リーステーブルをスキャンし、リーステーブルのグローバルセカンダリインデックス (GSI) をクエリする必要があります。`leaseAssignmentIntervalMillis` を小さくすると、これらのスキャンおよびクエリオペレーションの頻度が増加し、DynamoDB のコストが高くなります。新しいシャードの処理の遅延を最小限に抑えるために、この値を 2,000 (または 2 秒) に設定することをお勧めします。

`shardConsumerDispatchPollIntervalMillis`  
この設定では、シャードコンシューマーによる連続するポーリング間の間隔を定義して、状態遷移をトリガーします。KCL バージョン 1.x では、この動作は `idleTimeInMillis` パラメータによって制御され、設定可能な設定として公開されませんでした。KCL バージョン 3.x では、KCL バージョン 1.x のセットアップで ` idleTimeInMillis` で使用される値と一致するようにこの設定を行うことをお勧めします。

### ステップ 5: KCL 2.x から KCL 3.x に移行する
<a name="step5-kcl2-to-kcl3"></a>

最新の Kinesis Client Library (KCL) バージョンとのスムーズな移行と互換性を確保するには、[KCL 2.x から KCL 3.x へのアップグレード](https://docs.aws.amazon.com//streams/latest/dev/kcl-migration-from-2-3.html#kcl-migration-from-2-3-worker-metrics)に関する移行ガイドの手順のステップ 5～8 に従います。

一般的な KCL 3.x のトラブルシューティングの問題については、「[Troubleshooting KCL consumer applications](https://docs.aws.amazon.com//streams/latest/dev/troubleshooting-consumers.html)」を参照してください。

# 以前のバージョンにロールバックする
<a name="kcl-migration-rollback"></a>

このトピックでは、コンシューマーアプリケーションを以前の KCL バージョンにロールバックする方法について説明します。このロールバックプロセスは次の 2 つのステップで構成されています。

1. [KCL 移行ツール](https://github.com/awslabs/amazon-kinesis-client/blob/master/amazon-kinesis-client/scripts/KclMigrationTool.py)を実行します。

1. 以前の KCL バージョンコードを再デプロイします。

## ステップ 1: KCL 移行ツールを実行する
<a name="kcl-migration-rollback-step1"></a>

以前の KCL バージョンにロールバックする必要がある場合は、KCL 移行ツールを実行する必要があります。このツールは 2 つの重要なタスクを実行します。
+ これにより、DynamoDB のワーカーメトリクステーブルと呼ばれるメタデータテーブルとリーステーブルのグローバルセカンダリインデックスが削除されます。これらのアーティファクトは KCL 3.x によって作成されますが、以前のバージョンにロールバックするときには必要ありません。
+ これにより、すべてのワーカーが KCL 1.x と互換性のあるモードで実行され、以前の KCL バージョンで使用されていた負荷分散アルゴリズムの使用が開始されます。KCL 3.x の新しい負荷分散アルゴリズムに問題がある場合、この問題はすぐに軽減されます。

**重要**  
DynamoDB のコーディネーター状態テーブルが存在し、移行、ロールバック、ロールフォワードプロセス中に削除されていない必要があります。

**注記**  
コンシューマーアプリケーションのすべてのワーカーが、一度に同じ負荷分散アルゴリズムを使用することが重要です。KCL 移行ツールを使用すると、KCL 3.x コンシューマーアプリケーション内のすべてのワーカーが KCL 1.x 互換モードに切り替えられ、アプリケーションが以前の KCL バージョンにロールバックしている間、すべてのワーカーが同じ負荷分散アルゴリズムを実行します。

[KCL 移行ツール](https://github.com/awslabs/amazon-kinesis-client/blob/master/amazon-kinesis-client/scripts/KclMigrationTool.py)は、[KCL GitHub リポジトリ](https://github.com/awslabs/amazon-kinesis-client/tree/master)のスクリプトディレクトリでダウンロードできます。コーディネーター状態テーブル、ワーカーメトリクステーブル、リーステーブルに書き込むための適切なアクセス許可を持つワーカーまたはホストからスクリプトを実行します。KCL コンシューマーアプリケーションに適切な [IAM アクセス許可](https://docs.aws.amazon.com/streams/latest/dev/kcl-iam-permissions.html)が設定されていることを確認します。指定されたコマンドを使用して、KCL アプリケーションごとにスクリプトを 1 回だけ実行します。

```
python3 ./KclMigrationTool.py --region region --mode rollback [--application_name applicationName] [--lease_table_name leaseTableName] [--coordinator_state_table_name coordinatorStateTableName] [--worker_metrics_table_name workerMetricsTableName]
```

### パラメータ
<a name="kcl-migration-rollback-parameters"></a>

`--region`  
*リージョン*をお客様の AWS リージョンに置き換えてください。

`--application_name`  
このパラメータは、DynamoDB メタデータテーブル (リーステーブル、コーディネーター状態テーブル、ワーカーメトリクステーブル) にデフォルト名を使用している場合に必要です。これらのテーブルにカスタム名を指定している場合は、このパラメータを省略できます。*applicationName* を実際の KCL アプリケーションの名前に置き換えます。カスタム名が指定されていない場合、ツールはこの名前を使用してデフォルトのテーブル名を取得します。

`--lease_table_name`  
このパラメータは、KCL 設定でリーステーブルのカスタム名を設定している場合に必要です。デフォルトのテーブル名を使用している場合は、このパラメータを省略できます。*leaseTableName* をリーステーブルに指定したカスタムテーブル名に置き換えます。

`--coordinator_state_table_name`  
このパラメータは、KCL 設定でコーディネーター状態テーブルのカスタム名を設定している場合に必要です。デフォルトのテーブル名を使用している場合は、このパラメータを省略できます。*coordinatorStateTableName* を、コーディネーター状態テーブルに指定したカスタムテーブル名に置き換えます。

`--worker_metrics_table_name`  
このパラメータは、KCL 設定でワーカーメトリクステーブルのカスタム名を設定している場合に必要です。デフォルトのテーブル名を使用している場合は、このパラメータを省略できます。*workerMetricsTableName* を、ワーカーメトリクステーブルに指定したカスタムテーブル名に置き換えてください。

## ステップ 2: 以前の KCL バージョンでコードを再デプロイする
<a name="kcl-migration-rollback-step2"></a>

**重要**  
KCL 移行ツールによって生成された出力でバージョン 2.x に言及する場合は、KCL バージョン 1.x を参照していると解釈する必要があります。スクリプトを実行しても完全なロールバックは行われず、負荷分散アルゴリズムを KCL バージョン 1.x で使用されるものに切り替えるだけです。

ロールバック用に KCL 移行ツールを実行すると、次のいずれかのメッセージが表示されます。

メッセージ 1  
「ロールバックが完了しました。アプリケーションは 2x と互換性のある機能を実行していました。以前の KCL バージョンでコードをデプロイして、以前のアプリケーションバイナリにロールバックしてください。」  
**必要なアクション:** これは、ワーカーが KCL 1.x 互換モードで実行されていたことを意味します。以前の KCL バージョンのコードをワーカーに再デプロイしてください。

メッセージ 2  
「ロールバックが完了しました。KCL アプリケーションは 3x の機能を実行しており、2x と互換性のある機能にロールバックします。しばらくしても移行が見られない場合は、以前の KCL バージョンでコードをデプロイして、以前のアプリケーションバイナリにロールバックしてください。」  
**必要なアクション:** これはワーカーが KCL 3.x モードで実行され、KCL 移行ツールがすべてのワーカーを KCL 1.x 互換モードに切り替えたことを意味します。以前の KCL バージョンのコードをワーカーに再デプロイしてください。

メッセージ 3  
「アプリケーションは既にロールバックされています。削除できる KCLv3 リソースは、移行でアプリケーションをロールフォワードできるようになるまで料金が発生しないようにクリーンアップされました。」  
**必要なアクション:** これは、ワーカーが既にロールバックされ、KCL 1.x 互換モードで実行されていることを意味します。以前の KCL バージョンのコードをワーカーに再デプロイしてください。

# ロールバック後に KCL 3.x にロールフォワードする
<a name="kcl-migration-rollforward"></a>

このトピックでは、ロールバック後にコンシューマーアプリケーションを KCL 3.x にロールフォワードする方法について説明します。ロールフォワードが必要な場合は、2 ステップのプロセスを完了する必要があります。

1. [KCL 移行ツール](https://github.com/awslabs/amazon-kinesis-client/blob/master/amazon-kinesis-client/scripts/KclMigrationTool.py)を実行します。

1. KCL 3.x を使用してコードをデプロイします。

## ステップ 1: KCL 移行ツールを実行する
<a name="kcl-migration-rollforward-step1"></a>

次のコマンドを使用して KCL 移行ツールを実行し、KCL 3.x にロールフォワードします。

```
python3 ./KclMigrationTool.py --region region --mode rollforward [--application_name applicationName] [--coordinator_state_table_name coordinatorStateTableName]
```

### パラメータ
<a name="kcl-migration-rollforward-parameters"></a>

`--region`  
*リージョン*をお客様の AWS リージョンに置き換えてください。

`--application_name`  
このパラメータは、コーディネーター状態テーブルにデフォルト名を使用している場合に必要です。コーディネーター状態テーブルにカスタム名を指定している場合は、このパラメータを省略できます。*applicationName* を実際の KCL アプリケーションの名前に置き換えます。カスタム名が指定されていない場合、ツールはこの名前を使用してデフォルトのテーブル名を取得します。

`--coordinator_state_table_name`  
このパラメータは、KCL 設定でコーディネーター状態テーブルのカスタム名を設定している場合に必要です。デフォルトのテーブル名を使用している場合は、このパラメータを省略できます。*coordinatorStateTableName* を、コーディネーター状態テーブルに指定したカスタムテーブル名に置き換えます。

移行ツールをロールフォワードモードで実行すると、KCL は KCL 3.x に必要な次の DynamoDB リソースを作成します。
+ リーステーブルのグローバルセカンダリインデックス
+ ワーカーメトリクステーブル

## ステップ 2: KCL 3.x を使用してコードをデプロイする
<a name="kcl-migration-rollforward-step2"></a>

ロールフォワードの KCL 移行ツールを実行したら、KCL 3.x を使用してコードをワーカーにデプロイします。移行を完了するには、「[ステップ 8: 移行を完了する](https://docs.aws.amazon.com/streams/latest/dev/kcl-migration-from-2-3.html#kcl-migration-from-2-3-finish)」を参照してください。

# チュートリアル: DynamoDB Streams Kinesis Adapter
<a name="Streams.KCLAdapter.Walkthrough"></a>

このセクションは、Amazon Kinesis Client Library と Amazon DynamoDB Streams Kinesis Adapter を使用する Java アプリケーションのチュートリアルです。アプリケーションには、データレプリケーションの例が表示されます。データレプリケーションでは、1 つのテーブルからの書き込みアクティビティが 2 番目のテーブルに適用され、両方のテーブルの内容が同期されます。ソースコードについては、「[完成したプログラム: DynamoDB Streams Kinesis Adapter](Streams.KCLAdapter.Walkthrough.CompleteProgram.md)」を参照してください。

このプログラムでは、次のような処理を実行します。

1. `KCL-Demo-src` と `KCL-Demo-dst` という 2 つの DynamoDB テーブルを作成します。これらの各テーブルでは、ストリームが有効になっています。

1. 項目を追加、更新、削除することで、ソーステーブルで更新アクティビティを生成します。これにより、データがテーブルのストリームに書き込まれます。

1. ストリーミングからレコードを読み込んで、DynamoDB リクエストとして再構築し、ターゲットテーブルにリクエストを適用します。

1. ソーステーブルとターゲットテーブルをスキャンし、内容が同じであることを確認します。

1. テーブルを削除してクリーンアップします。

これらのステップについては次のセクションで説明します。完成したアプリケーションは、チュートリアルの最後に示します。

**Topics**
+ [ステップ 1: DynamoDB テーブルを作成する](#Streams.KCLAdapter.Walkthrough.Step1)
+ [ステップ 2: ソーステーブルに更新アクティビティを生成する](#Streams.KCLAdapter.Walkthrough.Step2)
+ [ステップ 3: ストリームを処理する](#Streams.KCLAdapter.Walkthrough.Step3)
+ [ステップ 4: 両方のテーブルの内容が同じであることを確認する](#Streams.KCLAdapter.Walkthrough.Step4)
+ [ステップ 5：クリーンアップ](#Streams.KCLAdapter.Walkthrough.Step5)
+ [完成したプログラム: DynamoDB Streams Kinesis Adapter](Streams.KCLAdapter.Walkthrough.CompleteProgram.md)

## ステップ 1: DynamoDB テーブルを作成する
<a name="Streams.KCLAdapter.Walkthrough.Step1"></a>

最初のステップでは、2 つの DynamoDB テーブル (送信元テーブルと送信先テーブル) を作成します。ソーステーブルのストリームにある `StreamViewType` は `NEW_IMAGE` です。これは、このテーブルで項目が変更されると必ず、イメージの "後の" 項目がストリームに書き込まれることを意味します。このようにして、ストリームはテーブル内のすべての書き込みアクティビティを記録します。

次の例は、両方のテーブルを作成するためのコードを示しています。

```
java.util.List<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();
attributeDefinitions.add(new AttributeDefinition().withAttributeName("Id").withAttributeType("N"));

java.util.List<KeySchemaElement> keySchema = new ArrayList<KeySchemaElement>();
keySchema.add(new KeySchemaElement().withAttributeName("Id").withKeyType(KeyType.HASH)); // Partition
                                                                                         // key

ProvisionedThroughput provisionedThroughput = new ProvisionedThroughput().withReadCapacityUnits(2L)
    .withWriteCapacityUnits(2L);

StreamSpecification streamSpecification = new StreamSpecification();
streamSpecification.setStreamEnabled(true);
streamSpecification.setStreamViewType(StreamViewType.NEW_IMAGE);
CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName)
    .withAttributeDefinitions(attributeDefinitions).withKeySchema(keySchema)
    .withProvisionedThroughput(provisionedThroughput).withStreamSpecification(streamSpecification);
```

## ステップ 2: ソーステーブルに更新アクティビティを生成する
<a name="Streams.KCLAdapter.Walkthrough.Step2"></a>

次のステップでは、ソーステーブルにいくつかの書き込みアクティビティを生成します。このアクティビティの実行中、ソーステーブルのストリームもほぼリアルタイムで更新されます。

アプリケーションは、データを書き込むための `PutItem`、`UpdateItem`、および `DeleteItem` API オペレーションを呼び出すメソッドを持つヘルパークラスを定義します。次の例は、これらのメソッドの使用方法を示しています。

```
StreamsAdapterDemoHelper.putItem(dynamoDBClient, tableName, "101", "test1");
StreamsAdapterDemoHelper.updateItem(dynamoDBClient, tableName, "101", "test2");
StreamsAdapterDemoHelper.deleteItem(dynamoDBClient, tableName, "101");
StreamsAdapterDemoHelper.putItem(dynamoDBClient, tableName, "102", "demo3");
StreamsAdapterDemoHelper.updateItem(dynamoDBClient, tableName, "102", "demo4");
StreamsAdapterDemoHelper.deleteItem(dynamoDBClient, tableName, "102");
```

## ステップ 3: ストリームを処理する
<a name="Streams.KCLAdapter.Walkthrough.Step3"></a>

ここでは、プログラムがストリームの処理を開始します。DynamoDB Streams Kinesis Adapter は、低レベルの DynamoDB Streams コールを行わなくてもコードが KCL を十分に使用できるように、KCL と DynamoDB Streams エンドポイントの間の透過的なレイヤーとして機能します。このプログラムでは次のタスクを実行しています。
+ KCL インターフェイス定義に従ったメソッド（`StreamsRecordProcessor`、`initialize`、`processRecords`）を使用して、レコードプロセッサクラス `shutdown` を定義します。`processRecords` メソッドには、ソーステーブルのストリームからの読み込みとターゲットテーブルへの書き込みに必要なロジックが含まれています。
+ レコードプロセッサクラスのクラスファクトリを定義します（`StreamsRecordProcessorFactory`）。これは、KCL を使用する Java プログラムに必要です。
+ クラスファクトリに関連付けられた新しい KCL `Worker` をインスタンス化します。
+ レコード処理が完了すると、`Worker` をシャットダウンします。

必要に応じて、Streams KCL Adapter 設定でキャッチアップモードを有効にして、ストリーム処理の遅延が 1 分 (デフォルト) を超えたときに GetRecords API 呼び出しレートを 3 倍 (デフォルト) に自動的にスケーリングし、ストリームコンシューマーがテーブル内の高スループットのスパイクを処理できるようにします。

KCL インターフェイス定義の詳細については、「*Amazon Kinesis Data Streams デベロッパーガイド*」の「[Kinesis Client Library を使用したコンシューマーの開発](https://docs.aws.amazon.com/kinesis/latest/dev/developing-consumers-with-kcl.html)」を参照してください。

次の例は、`StreamsRecordProcessor` におけるメインループを示しています。`case` ステートメントは、ストリームレコードに出現する `OperationType` に基づいて、実行するアクションを決定します。

```
for (Record record : records) {
    String data = new String(record.getData().array(), Charset.forName("UTF-8"));
    System.out.println(data);
    if (record instanceof RecordAdapter) {
                software.amazon.dynamodb.model.Record streamRecord = ((RecordAdapter) record)
                    .getInternalObject();

                switch (streamRecord.getEventName()) {
                    case "INSERT":
                    case "MODIFY":
                        StreamsAdapterDemoHelper.putItem(dynamoDBClient, tableName,
                            streamRecord.getDynamodb().getNewImage());
                        break;
                    case "REMOVE":
                        StreamsAdapterDemoHelper.deleteItem(dynamoDBClient, tableName,
                            streamRecord.getDynamodb().getKeys().get("Id").getN());
                }
    }
    checkpointCounter += 1;
    if (checkpointCounter % 10 == 0) {
        try {
            checkpointer.checkpoint();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

## ステップ 4: 両方のテーブルの内容が同じであることを確認する
<a name="Streams.KCLAdapter.Walkthrough.Step4"></a>

この時点で、ソーステーブルとターゲットテーブルの内容が同期されています。アプリケーションは、両方のテーブルに対して `Scan` リクエストを発行し、内容が実際に同じであることを確認します。

`DemoHelper` クラスには、低レベルの `ScanTable` API を呼び出す `Scan` メソッドが含まれています。次の例は、その使用方法を示しています。

```
if (StreamsAdapterDemoHelper.scanTable(dynamoDBClient, srcTable).getItems()
    .equals(StreamsAdapterDemoHelper.scanTable(dynamoDBClient, destTable).getItems())) {
    System.out.println("Scan result is equal.");
}
else {
    System.out.println("Tables are different!");
}
```

## ステップ 5：クリーンアップ
<a name="Streams.KCLAdapter.Walkthrough.Step5"></a>

デモは完了したため、アプリケーションによりソーステーブルとターゲットテーブルが削除されます。次のコード例を参照してください。テーブルが削除されても、そのストリームは最大 24 時間使用可能です。その後、自動的に削除されます。

```
dynamoDBClient.deleteTable(new DeleteTableRequest().withTableName(srcTable));
dynamoDBClient.deleteTable(new DeleteTableRequest().withTableName(destTable));
```

# 完成したプログラム: DynamoDB Streams Kinesis Adapter
<a name="Streams.KCLAdapter.Walkthrough.CompleteProgram"></a>

次に、[チュートリアル: DynamoDB Streams Kinesis Adapter](Streams.KCLAdapter.Walkthrough.md) で説明したタスクを実行する、完成した Java プログラムを次に示します。実行すると、次のような出力が表示されます。

```
Creating table KCL-Demo-src
Creating table KCL-Demo-dest
Table is active.
Creating worker for stream: arn:aws:dynamodb:us-west-2:111122223333:table/KCL-Demo-src/stream/2015-05-19T22:48:56.601
Starting worker...
Scan result is equal.
Done.
```

**重要**  
 このプログラムを実行するには、クライアントアプリケーションがポリシーを使用して DynamoDB および Amazon CloudWatch にアクセスできることを確認します。詳細については、「[DynamoDB のアイデンティティベースのポリシー](security_iam_service-with-iam.md#security_iam_service-with-iam-id-based-policies)」を参照してください。

ソースコードは、4 つの `.java` ファイルから構成されています。このプログラムを構築するには、Amazon Kinesis Client Library (KCL) 3.x と AWS SDK for Java v2 を含む次の依存関係を推移的な依存関係として追加します。

------
#### [ Maven ]

```
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>dynamodb-streams-kinesis-adapter</artifactId>
    <version>2.1.0</version>
</dependency>
```

------
#### [ Gradle ]

```
implementation 'com.amazonaws:dynamodb-streams-kinesis-adapter:2.1.0'
```

------

ソースファイルは次のとおりです。
+ `StreamsAdapterDemo.java`
+ `StreamsRecordProcessor.java`
+ `StreamsRecordProcessorFactory.java`
+ `StreamsAdapterDemoHelper.java`

## StreamsAdapterDemo.java
<a name="Streams.KCLAdapter.Walkthrough.CompleteProgram.StreamsAdapterDemo"></a>

```
package com.amazonaws.codesamples;

import com.amazonaws.services.dynamodbv2.streamsadapter.AmazonDynamoDBStreamsAdapterClient;
import com.amazonaws.services.dynamodbv2.streamsadapter.StreamsSchedulerFactory;
import com.amazonaws.services.dynamodbv2.streamsadapter.polling.DynamoDBStreamsPollingConfig;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchAsyncClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse;
import software.amazon.kinesis.common.ConfigsBuilder;
import software.amazon.kinesis.common.InitialPositionInStream;
import software.amazon.kinesis.common.InitialPositionInStreamExtended;
import software.amazon.kinesis.coordinator.Scheduler;
import software.amazon.kinesis.processor.ShardRecordProcessorFactory;
import software.amazon.kinesis.processor.StreamTracker;
import software.amazon.kinesis.retrieval.RetrievalConfig;

public class StreamsAdapterDemo {

    private static DynamoDbAsyncClient dynamoDbAsyncClient;
    private static CloudWatchAsyncClient cloudWatchAsyncClient;
    private static AmazonDynamoDBStreamsAdapterClient amazonDynamoDbStreamsAdapterClient;

    private static String tablePrefix = "KCL-Demo";
    private static String streamArn;

    private static Region region = Region.US_EAST_1;
    private static AwsCredentialsProvider credentialsProvider = DefaultCredentialsProvider.create();

    public static void main( String[] args ) throws Exception {
        System.out.println("Starting demo...");
        dynamoDbAsyncClient = DynamoDbAsyncClient.builder()
                .credentialsProvider(credentialsProvider)
                .region(region)
                .build();
        cloudWatchAsyncClient = CloudWatchAsyncClient.builder()
                .credentialsProvider(credentialsProvider)
                .region(region)
                .build();
        amazonDynamoDbStreamsAdapterClient = new AmazonDynamoDBStreamsAdapterClient(credentialsProvider, region);

        String srcTable = tablePrefix + "-src";
        String destTable = tablePrefix + "-dest";

        setUpTables();

        StreamTracker streamTracker = StreamsSchedulerFactory.createSingleStreamTracker(streamArn,
                InitialPositionInStreamExtended.newInitialPosition(InitialPositionInStream.TRIM_HORIZON));

        ShardRecordProcessorFactory shardRecordProcessorFactory =
                new StreamsAdapterDemoProcessorFactory(dynamoDbAsyncClient, destTable);

        ConfigsBuilder configsBuilder = new ConfigsBuilder(
                streamTracker,
                "streams-adapter-demo",
                amazonDynamoDbStreamsAdapterClient,
                dynamoDbAsyncClient,
                cloudWatchAsyncClient,
                "streams-demo-worker",
                shardRecordProcessorFactory
        );

        DynamoDBStreamsPollingConfig pollingConfig = new DynamoDBStreamsPollingConfig(amazonDynamoDbStreamsAdapterClient);
        RetrievalConfig retrievalConfig = configsBuilder.retrievalConfig();
        retrievalConfig.retrievalSpecificConfig(pollingConfig);

        System.out.println("Creating scheduler for stream " + streamArn);
        Scheduler scheduler = StreamsSchedulerFactory.createScheduler(
                configsBuilder.checkpointConfig(),
                configsBuilder.coordinatorConfig(),
                configsBuilder.leaseManagementConfig(),
                configsBuilder.lifecycleConfig(),
                configsBuilder.metricsConfig(),
                configsBuilder.processorConfig(),
                retrievalConfig,
                amazonDynamoDbStreamsAdapterClient
        );

        System.out.println("Starting scheduler...");
        Thread t = new Thread(scheduler);
        t.start();

        Thread.sleep(250000);

        System.out.println("Stopping scheduler...");
        scheduler.shutdown();
        t.join();

        if (StreamsAdapterDemoHelper.scanTable(dynamoDbAsyncClient, srcTable).items()
                .equals(StreamsAdapterDemoHelper.scanTable(dynamoDbAsyncClient, destTable).items())) {
            System.out.println("Scan result is equal.");
        } else {
            System.out.println("Tables are different!");
        }

        System.out.println("Done.");
        cleanupAndExit(0);
    }

    private static void setUpTables() {
        String srcTable = tablePrefix + "-src";
        String destTable = tablePrefix + "-dest";
        streamArn = StreamsAdapterDemoHelper.createTable(dynamoDbAsyncClient, srcTable);
        StreamsAdapterDemoHelper.createTable(dynamoDbAsyncClient, destTable);

        awaitTableCreation(srcTable);

        performOps(srcTable);
    }

    private static void awaitTableCreation(String tableName) {
        Integer retries = 0;
        Boolean created = false;
        while (!created && retries < 100) {
            DescribeTableResponse result = StreamsAdapterDemoHelper.describeTable(dynamoDbAsyncClient, tableName);
            created = result.table().tableStatusAsString().equals("ACTIVE");
            if (created) {
                System.out.println("Table is active.");
                return;
            } else {
                retries++;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // do nothing
                }
            }
        }
        System.out.println("Timeout after table creation. Exiting...");
        cleanupAndExit(1);
    }

    private static void performOps(String tableName) {
        StreamsAdapterDemoHelper.putItem(dynamoDbAsyncClient, tableName, "101", "test1");
        StreamsAdapterDemoHelper.updateItem(dynamoDbAsyncClient, tableName, "101", "test2");
        StreamsAdapterDemoHelper.deleteItem(dynamoDbAsyncClient, tableName, "101");
        StreamsAdapterDemoHelper.putItem(dynamoDbAsyncClient, tableName, "102", "demo3");
        StreamsAdapterDemoHelper.updateItem(dynamoDbAsyncClient, tableName, "102", "demo4");
        StreamsAdapterDemoHelper.deleteItem(dynamoDbAsyncClient, tableName, "102");
    }

    private static void cleanupAndExit(Integer returnValue) {
        String srcTable = tablePrefix + "-src";
        String destTable = tablePrefix + "-dest";
        dynamoDbAsyncClient.deleteTable(DeleteTableRequest.builder().tableName(srcTable).build());
        dynamoDbAsyncClient.deleteTable(DeleteTableRequest.builder().tableName(destTable).build());
        System.exit(returnValue);
    }
}
```

## StreamsRecordProcessor.java
<a name="Streams.KCLAdapter.Walkthrough.CompleteProgram.StreamsRecordProcessor"></a>

```
package com.amazonaws.codesamples;

import com.amazonaws.services.dynamodbv2.streamsadapter.adapter.DynamoDBStreamsClientRecord;
import com.amazonaws.services.dynamodbv2.streamsadapter.model.DynamoDBStreamsProcessRecordsInput;
import com.amazonaws.services.dynamodbv2.streamsadapter.processor.DynamoDBStreamsShardRecordProcessor;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.model.Record;
import software.amazon.kinesis.exceptions.InvalidStateException;
import software.amazon.kinesis.exceptions.ShutdownException;
import software.amazon.kinesis.lifecycle.events.InitializationInput;
import software.amazon.kinesis.lifecycle.events.LeaseLostInput;
import software.amazon.kinesis.lifecycle.events.ShardEndedInput;
import software.amazon.kinesis.lifecycle.events.ShutdownRequestedInput;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class StreamsRecordProcessor implements DynamoDBStreamsShardRecordProcessor {

    private Integer checkpointCounter;

    private final DynamoDbAsyncClient dynamoDbAsyncClient;
    private final String tableName;

    public StreamsRecordProcessor(DynamoDbAsyncClient dynamoDbAsyncClient, String tableName) {
        this.dynamoDbAsyncClient = dynamoDbAsyncClient;
        this.tableName = tableName;
    }

    @Override
    public void initialize(InitializationInput initializationInput) {
        this.checkpointCounter = 0;
    }

    @Override
    public void processRecords(DynamoDBStreamsProcessRecordsInput dynamoDBStreamsProcessRecordsInput) {
        for (DynamoDBStreamsClientRecord record: dynamoDBStreamsProcessRecordsInput.records()) {
            String data = new String(record.data().array(), StandardCharsets.UTF_8);
            System.out.println(data);
            Record streamRecord = record.getRecord();

            switch (streamRecord.eventName()) {
                case INSERT:
                case MODIFY:
                    StreamsAdapterDemoHelper.putItem(dynamoDbAsyncClient, tableName,
                            streamRecord.dynamodb().newImage());
                case REMOVE:
                    StreamsAdapterDemoHelper.deleteItem(dynamoDbAsyncClient, tableName,
                            streamRecord.dynamodb().keys().get("Id").n());
            }
            checkpointCounter += 1;
            if (checkpointCounter % 10 == 0) {
                try {
                    dynamoDBStreamsProcessRecordsInput.checkpointer().checkpoint();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void leaseLost(LeaseLostInput leaseLostInput) {
        System.out.println("Lease Lost");
    }

    @Override
    public void shardEnded(ShardEndedInput shardEndedInput) {
        try {
            shardEndedInput.checkpointer().checkpoint();
        } catch (ShutdownException | InvalidStateException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void shutdownRequested(ShutdownRequestedInput shutdownRequestedInput) {
        try {
            shutdownRequestedInput.checkpointer().checkpoint();
        } catch (ShutdownException | InvalidStateException e) {
            e.printStackTrace();
        }
    }
}
```

## StreamsRecordProcessorFactory.java
<a name="Streams.KCLAdapter.Walkthrough.CompleteProgram.StreamsRecordProcessorFactory"></a>

```
package com.amazonaws.codesamples;

import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.kinesis.processor.ShardRecordProcessor;
import software.amazon.kinesis.processor.ShardRecordProcessorFactory;

public class StreamsAdapterDemoProcessorFactory implements ShardRecordProcessorFactory {
    private final String tableName;
    private final DynamoDbAsyncClient dynamoDbAsyncClient;

    public StreamsAdapterDemoProcessorFactory(DynamoDbAsyncClient asyncClient, String tableName) {
        this.tableName = tableName;
        this.dynamoDbAsyncClient = asyncClient;
    }

    @Override
    public ShardRecordProcessor shardRecordProcessor() {
        return new StreamsRecordProcessor(dynamoDbAsyncClient, tableName);
    }
}
```

## StreamsAdapterDemoHelper.java
<a name="Streams.KCLAdapter.Walkthrough.CompleteProgram.StreamsAdapterDemoHelper"></a>

```
package com.amazonaws.codesamples;

import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.BillingMode;
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
import software.amazon.awssdk.services.dynamodb.model.CreateTableResponse;
import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse;
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
import software.amazon.awssdk.services.dynamodb.model.KeyType;
import software.amazon.awssdk.services.dynamodb.model.OnDemandThroughput;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import software.amazon.awssdk.services.dynamodb.model.ResourceInUseException;
import software.amazon.awssdk.services.dynamodb.model.ScanRequest;
import software.amazon.awssdk.services.dynamodb.model.ScanResponse;
import software.amazon.awssdk.services.dynamodb.model.StreamSpecification;
import software.amazon.awssdk.services.dynamodb.model.StreamViewType;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class StreamsAdapterDemoHelper {

    /**
     * @return StreamArn
     */
    public static String createTable(DynamoDbAsyncClient client, String tableName) {
        List<AttributeDefinition> attributeDefinitions = new ArrayList<>();
        attributeDefinitions.add(AttributeDefinition.builder()
                .attributeName("Id")
                .attributeType("N")
                .build());

        List<KeySchemaElement> keySchema = new ArrayList<>();
        keySchema.add(KeySchemaElement.builder()
                .attributeName("Id")
                .keyType(KeyType.HASH) // Partition key
                .build());

        StreamSpecification streamSpecification = StreamSpecification.builder()
                .streamEnabled(true)
                .streamViewType(StreamViewType.NEW_IMAGE)
                .build();

        CreateTableRequest createTableRequest = CreateTableRequest.builder()
                .tableName(tableName)
                .attributeDefinitions(attributeDefinitions)
                .keySchema(keySchema)
                .billingMode(BillingMode.PAY_PER_REQUEST)
                .streamSpecification(streamSpecification)
                .build();

        try {
            System.out.println("Creating table " + tableName);
            CreateTableResponse result = client.createTable(createTableRequest).join();
            return result.tableDescription().latestStreamArn();
        } catch (Exception e) {
            if (e.getCause() instanceof ResourceInUseException) {
                System.out.println("Table already exists.");
                return describeTable(client, tableName).table().latestStreamArn();
            }
            throw e;
        }
    }

    public static DescribeTableResponse describeTable(DynamoDbAsyncClient client, String tableName) {
        return client.describeTable(DescribeTableRequest.builder()
                        .tableName(tableName)
                        .build())
                .join();
    }

    public static ScanResponse scanTable(DynamoDbAsyncClient dynamoDbClient, String tableName) {
        return dynamoDbClient.scan(ScanRequest.builder()
                        .tableName(tableName)
                        .build())
                .join();
    }

    public static void putItem(DynamoDbAsyncClient dynamoDbClient, String tableName, String id, String val) {
        Map<String, AttributeValue> item = new HashMap<>();
        item.put("Id", AttributeValue.builder().n(id).build());
        item.put("attribute-1", AttributeValue.builder().s(val).build());

        putItem(dynamoDbClient, tableName, item);
    }

    public static void putItem(DynamoDbAsyncClient dynamoDbClient, String tableName,
                               Map<String, AttributeValue> items) {
        PutItemRequest putItemRequest = PutItemRequest.builder()
                .tableName(tableName)
                .item(items)
                .build();
        dynamoDbClient.putItem(putItemRequest).join();
    }

    public static void updateItem(DynamoDbAsyncClient dynamoDbClient, String tableName, String id, String val) {
        Map<String, AttributeValue> key = new HashMap<>();
        key.put("Id", AttributeValue.builder().n(id).build());

        Map<String, String> expressionAttributeNames = new HashMap<>();
        expressionAttributeNames.put("#attr2", "attribute-2");

        Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
        expressionAttributeValues.put(":val", AttributeValue.builder().s(val).build());

        UpdateItemRequest updateItemRequest = UpdateItemRequest.builder()
                .tableName(tableName)
                .key(key)
                .updateExpression("SET #attr2 = :val")
                .expressionAttributeNames(expressionAttributeNames)
                .expressionAttributeValues(expressionAttributeValues)
                .build();

        dynamoDbClient.updateItem(updateItemRequest).join();
    }

    public static void deleteItem(DynamoDbAsyncClient dynamoDbClient, String tableName, String id) {
        Map<String, AttributeValue> key = new HashMap<>();
        key.put("Id", AttributeValue.builder().n(id).build());

        DeleteItemRequest deleteItemRequest = DeleteItemRequest.builder()
                .tableName(tableName)
                .key(key)
                .build();
        dynamoDbClient.deleteItem(deleteItemRequest).join();
    }
}
```

# DynamoDB Streams 低レベル API: Java の例
<a name="Streams.LowLevel.Walkthrough"></a>

**注記**  
このページのコードはすべてを網羅していないため、Amazon DynamoDB Streams を使用するすべてのシナリオに対応しているわけではありません。DynamoDB からストリーミングレコードを使用する場合は、[DynamoDB Streams Kinesis Adapter を使用したストリームレコードの処理](Streams.KCLAdapter.md) で説明されているとおり、Kinesis Client Library (KCL) を使用し、Amazon Kinesis Adapter を介して行うことを推奨します。

このセクションには、動作中の DynamoDB Streams を示す Java プログラムが含まれています。このプログラムでは、次のような処理を実行します。

1. ストリーミングが有効になった DynamoDB テーブルを作成します。

1. このテーブルのストリーム設定を記述します。

1. テーブル内のデータを変更します。

1. ストリーム内のシャードを記述します。

1. シャードからストリームレコードを読み込みます。

1. 子シャードを取得し、レコードの読み取りを続行します。

1. クリーンアップします。

プログラムを実行すると、以下のような出力が表示されます。

```
Testing Streams Demo
Creating an Amazon DynamoDB table TestTableForStreams with a simple primary key: Id
Waiting for TestTableForStreams to be created...
Current stream ARN for TestTableForStreams: arn:aws:dynamodb:us-east-2:123456789012:table/TestTableForStreams/stream/2018-03-20T16:49:55.208
Stream enabled: true
Update view type: NEW_AND_OLD_IMAGES

Performing write activities on TestTableForStreams
Processing item 1 of 100
Processing item 2 of 100
Processing item 3 of 100
...
Processing item 100 of 100
Shard: {ShardId: shardId-1234567890-...,SequenceNumberRange: {StartingSequenceNumber: 100002572486797508907,},}
    Shard iterator: EjYFEkX2a26eVTWe...
        StreamRecord(ApproximateCreationDateTime=2025-04-09T13:11:58Z, Keys={Id=AttributeValue(S=4)}, NewImage={Message=AttributeValue(S=New Item!), Id=AttributeValue(S=4)}, SequenceNumber=2000001584047545833909, SizeBytes=22, StreamViewType=NEW_AND_OLD_IMAGES)
        StreamRecord(ApproximateCreationDateTime=2025-04-09T13:11:58Z, Keys={Id=AttributeValue(S=4)}, NewImage={Message=AttributeValue(S=This is an updated item), Id=AttributeValue(S=4)}, OldImage={Message=AttributeValue(S=New Item!), Id=AttributeValue(S=4)}, SequenceNumber=2100003604869767892701, SizeBytes=55, StreamViewType=NEW_AND_OLD_IMAGES)
        StreamRecord(ApproximateCreationDateTime=2025-04-09T13:11:58Z, Keys={Id=AttributeValue(S=4)}, OldImage={Message=AttributeValue(S=This is an updated item), Id=AttributeValue(S=4)}, SequenceNumber=2200001099771112898434, SizeBytes=36, StreamViewType=NEW_AND_OLD_IMAGES)
...
Deleting the table...
Table StreamsDemoTable deleted.
Demo complete
```

**Example 例**  

```
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeAction;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.AttributeValueUpdate;
import software.amazon.awssdk.services.dynamodb.model.BillingMode;
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
import software.amazon.awssdk.services.dynamodb.model.CreateTableResponse;
import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest;
import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeStreamRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeStreamResponse;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.GetRecordsRequest;
import software.amazon.awssdk.services.dynamodb.model.GetRecordsResponse;
import software.amazon.awssdk.services.dynamodb.model.GetShardIteratorRequest;
import software.amazon.awssdk.services.dynamodb.model.GetShardIteratorResponse;
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
import software.amazon.awssdk.services.dynamodb.model.KeyType;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import software.amazon.awssdk.services.dynamodb.model.Record;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import software.amazon.awssdk.services.dynamodb.model.Shard;
import software.amazon.awssdk.services.dynamodb.model.ShardFilter;
import software.amazon.awssdk.services.dynamodb.model.ShardFilterType;
import software.amazon.awssdk.services.dynamodb.model.ShardIteratorType;
import software.amazon.awssdk.services.dynamodb.model.StreamSpecification;
import software.amazon.awssdk.services.dynamodb.model.TableDescription;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
import software.amazon.awssdk.services.dynamodb.streams.DynamoDbStreamsClient;
import software.amazon.awssdk.services.dynamodb.waiters.DynamoDbWaiter;

public class StreamsLowLevelDemo {


    public static void main(String[] args) {
        final String usage = "Testing Streams Demo";
        try {
            System.out.println(usage);

            String tableName = "StreamsDemoTable";
            String key = "Id";
            System.out.println("Creating an Amazon DynamoDB table " + tableName + " with a simple primary key: " + key);
            Region region = Region.US_WEST_2;
            DynamoDbClient ddb = DynamoDbClient.builder()
                    .region(region)
                    .build();

            DynamoDbStreamsClient ddbStreams = DynamoDbStreamsClient.builder()
                    .region(region)
                    .build();
            DescribeTableRequest describeTableRequest = DescribeTableRequest.builder()
                    .tableName(tableName)
                    .build();
            TableDescription tableDescription = null;
            try{
                tableDescription = ddb.describeTable(describeTableRequest).table();
            }catch (Exception e){
                System.out.println("Table " + tableName + " does not exist.");
                tableDescription = createTable(ddb, tableName, key);
            }

            // Print the stream settings for the table
            String streamArn = tableDescription.latestStreamArn();
           
            StreamSpecification streamSpec = tableDescription.streamSpecification();
            System.out.println("Current stream ARN for " + tableDescription.tableName() + ": " +
                   streamArn);
            System.out.println("Stream enabled: " + streamSpec.streamEnabled());
            System.out.println("Update view type: " + streamSpec.streamViewType());
            System.out.println();
            // Generate write activity in the table
            System.out.println("Performing write activities on " + tableName);
            int maxItemCount = 100;
            for (Integer i = 1; i <= maxItemCount; i++) {
                System.out.println("Processing item " + i + " of " + maxItemCount);
                // Write a new item
                putItemInTable(key, i, tableName, ddb);
                // Update the item
                updateItemInTable(key, i, tableName, ddb);
                // Delete the item
                deleteDynamoDBItem(key, i, tableName, ddb);
            }

            // Process Stream
            processStream(streamArn, maxItemCount, ddb, ddbStreams, tableName);

            // Delete the table
            System.out.println("Deleting the table...");
            DeleteTableRequest deleteTableRequest = DeleteTableRequest.builder()
                    .tableName(tableName)
                    .build();
            ddb.deleteTable(deleteTableRequest);
            System.out.println("Table " + tableName + " deleted.");
            System.out.println("Demo complete");
            ddb.close();
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        }
    }

    private static void processStream(String streamArn, int maxItemCount, DynamoDbClient ddb, DynamoDbStreamsClient ddbStreams, String tableName) {
        // Get all the shard IDs from the stream. Note that DescribeStream returns
        // the shard IDs one page at a time.
        String lastEvaluatedShardId = null;
        do {
            DescribeStreamRequest describeStreamRequest = DescribeStreamRequest.builder()
                    .streamArn(streamArn)
                    .exclusiveStartShardId(lastEvaluatedShardId).build();
            DescribeStreamResponse describeStreamResponse = ddbStreams.describeStream(describeStreamRequest);

            List<Shard> shards = describeStreamResponse.streamDescription().shards();

            // Process each shard on this page

            fetchShardsAndReadRecords(streamArn, maxItemCount, ddbStreams, shards);

            // If LastEvaluatedShardId is set, then there is
            // at least one more page of shard IDs to retrieve
            lastEvaluatedShardId = describeStreamResponse.streamDescription().lastEvaluatedShardId();

        } while (lastEvaluatedShardId != null);

    }

    private static void fetchShardsAndReadRecords(String streamArn, int maxItemCount, DynamoDbStreamsClient ddbStreams, List<Shard> shards) {
        for (Shard shard : shards) {
            String shardId = shard.shardId();
            System.out.println("Shard: " + shard);

            // Get an iterator for the current shard
            GetShardIteratorRequest shardIteratorRequest = GetShardIteratorRequest.builder()
                    .streamArn(streamArn).shardId(shardId)
                    .shardIteratorType(ShardIteratorType.TRIM_HORIZON).build();

            GetShardIteratorResponse getShardIteratorResult = ddbStreams.getShardIterator(shardIteratorRequest);

            String currentShardIter = getShardIteratorResult.shardIterator();

            // Shard iterator is not null until the Shard is sealed (marked as READ_ONLY).
            // To prevent running the loop until the Shard is sealed, we process only the
            // items that were written into DynamoDB and then exit.
            int processedRecordCount = 0;
            while (currentShardIter != null && processedRecordCount < maxItemCount) {
                // Use the shard iterator to read the stream records
                GetRecordsRequest getRecordsRequest = GetRecordsRequest.builder()
                        .shardIterator(currentShardIter).build();
                GetRecordsResponse getRecordsResult = ddbStreams.getRecords(getRecordsRequest);
                List<Record> records = getRecordsResult.records();
                for (Record record : records) {
                    System.out.println("        " + record.dynamodb());
                }
                processedRecordCount += records.size();
                currentShardIter = getRecordsResult.nextShardIterator();
            }
            if (currentShardIter == null){
                System.out.println("Shard has been fully processed. Shard iterator is null.");
                System.out.println("Fetch the child shard to continue processing instead of bulk fetching all shards");
                DescribeStreamRequest describeStreamRequestForChildShards = DescribeStreamRequest.builder()
                        .streamArn(streamArn)
                        .shardFilter(ShardFilter.builder()
                                .type(ShardFilterType.CHILD_SHARDS)
                                .shardId(shardId).build())
                        .build();
                DescribeStreamResponse describeStreamResponseChildShards = ddbStreams.describeStream(describeStreamRequestForChildShards);
                fetchShardsAndReadRecords(streamArn, maxItemCount, ddbStreams, describeStreamResponseChildShards.streamDescription().shards());
            }
        }
    }

    private static void putItemInTable(String key, Integer i, String tableName, DynamoDbClient ddb) {
        Map<String, AttributeValue> item = new HashMap<>();
        item.put(key, AttributeValue.builder()
                .s(i.toString())
                .build());
        item.put("Message", AttributeValue.builder()
                .s("New Item!")
                .build());
        PutItemRequest request = PutItemRequest.builder()
                .tableName(tableName)
                .item(item)
                .build();
        ddb.putItem(request);
    }

    private static void updateItemInTable(String key, Integer i, String tableName, DynamoDbClient ddb) {

        HashMap<String, AttributeValue> itemKey = new HashMap<>();
        itemKey.put(key, AttributeValue.builder()
                .s(i.toString())
                .build());


        HashMap<String, AttributeValueUpdate> updatedValues = new HashMap<>();
        updatedValues.put("Message", AttributeValueUpdate.builder()
                .value(AttributeValue.builder().s("This is an updated item").build())
                .action(AttributeAction.PUT)
                .build());

        UpdateItemRequest request = UpdateItemRequest.builder()
                .tableName(tableName)
                .key(itemKey)
                .attributeUpdates(updatedValues)
                .build();
        ddb.updateItem(request);
    }

    public static void deleteDynamoDBItem(String key, Integer i, String tableName, DynamoDbClient ddb) {
        HashMap<String, AttributeValue> keyToGet = new HashMap<>();
        keyToGet.put(key, AttributeValue.builder()
                .s(i.toString())
                .build());

        DeleteItemRequest deleteReq = DeleteItemRequest.builder()
                .tableName(tableName)
                .key(keyToGet)
                .build();
        ddb.deleteItem(deleteReq);
    }

    public static TableDescription createTable(DynamoDbClient ddb, String tableName, String key) {
        DynamoDbWaiter dbWaiter = ddb.waiter();
        StreamSpecification streamSpecification = StreamSpecification.builder()
                .streamEnabled(true)
                .streamViewType("NEW_AND_OLD_IMAGES")
                .build();
        CreateTableRequest request = CreateTableRequest.builder()
                .attributeDefinitions(AttributeDefinition.builder()
                        .attributeName(key)
                        .attributeType(ScalarAttributeType.S)
                        .build())
                .keySchema(KeySchemaElement.builder()
                        .attributeName(key)
                        .keyType(KeyType.HASH)
                        .build())
                .billingMode(BillingMode.PAY_PER_REQUEST) //  DynamoDB automatically scales based on traffic.
                .tableName(tableName)
                .streamSpecification(streamSpecification)
                .build();

        TableDescription newTable;
        try {
            CreateTableResponse response = ddb.createTable(request);
            DescribeTableRequest tableRequest = DescribeTableRequest.builder()
                    .tableName(tableName)
                    .build();
                    
            System.out.println("Waiting for " + tableName + " to be created...");

            // Wait until the Amazon DynamoDB table is created.
            WaiterResponse<DescribeTableResponse> waiterResponse = dbWaiter.waitUntilTableExists(tableRequest);
            waiterResponse.matched().response().ifPresent(System.out::println);
            newTable = response.tableDescription();
            return newTable;

        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        return null;
    }



}
```

# DynamoDB Streams と AWS Lambda のトリガー
<a name="Streams.Lambda"></a>

Amazon DynamoDB は AWS Lambda と統合されているため、*トリガー* (DynamoDB Streams 内のイベントに自動的に応答するコードの一部) を作成できます。トリガーを使用すると、DynamoDB テーブル内のデータ変更に対応するアプリケーションを構築できます。

**Topics**
+ [チュートリアル \$11: AWS CLI を使用した Amazon DynamoDB と AWS Lambda での、フィルターを使ったすべてのイベントの処理](Streams.Lambda.Tutorial.md)
+ [チュートリアル \$12: DynamoDB と Lambda での、フィルターを使用したいくつかのイベントの処理。](Streams.Lambda.Tutorial2.md)
+ [Lambda での DynamoDB ストリームの使用に関するベストプラクティス](Streams.Lambda.BestPracticesWithDynamoDB.md)

テーブルで DynamoDB Streams を有効にした場合、書き込む AWS Lambda 関数にストリーミングの Amazon リソースネーム (ARN) を関連付けることができます。その後、その DynamoDB テーブルに対するすべてのミューテーションアクションをストリーム上の項目としてキャプチャできます。例えば、テーブル内の項目が変更されたときに、そのテーブルのストリームに新しいレコードがすぐに表示されるようにトリガーを設定できます。

**注記**  
3 つ以上の Lambda 関数を 1 つの DynamoDB ストリームにサブスクライブすると、読み込みスロットリングが発生する可能性があります。

[AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html) サービスは、1 秒に 4 回、ストリームをポーリングして新しいレコードを探します。新しいストリームレコードが利用可能になると、Lambda 関数が同期的に呼び出されます。同じ DynamoDB ストリームに最大 2 つの Lambda 関数をサブスクライブできます。同じ DynamoDB ストリームに 3 つ以上の Lambda 関数をサブスクライブすると、読み込みスロットリングが発生する可能性があります。

Lambda 関数は、通知の送信やワークフローの開始など、指定した他の多くのアクションを実行できます。各ストリームレコードを Amazon S3 File Gateway (Amazon S3) などの永続的ストレージにコピーするだけで、書き込みアクティビティの永続的な監査追跡をテーブルに作成する Lambda 関数を記述できます。または、`GameScores` テーブルに書き込みを行うモバイルゲームアプリがあるとします。`TopScore` テーブルの `GameScores` 属性が更新されるたびに、対応するストリームレコードがテーブルのストリームに書き込まれます。その後、このイベントによって Lambda 関数をトリガーし、ソーシャルメディアネットワークにおめでとうメッセージを投稿できます この関数は、`GameScores` の更新でないストリームレコードや、`TopScore` 属性を変更しないストリームレコードを無視するように記述できます。

関数がエラーを返した場合、処理が正常に完了するか、データの有効期限が切れるまで Lambda はバッチを再試行します。Lambda では、より小さなバッチで再試行したり、再試行回数を制限したり、古すぎるレコードを破棄したりするよう設定できます。

パフォーマンスのベストプラクティスとして、Lambda 関数は存続期間を短くする必要があります。また、不必要な処理の遅延が発生するのを防ぐため、複雑なロジックは実行しないでください。特に高速ストリームの場合は、同期的に長時間実行する Lambda よりも、非同期的な後処理ステップ関数ワークフローをトリガーすることをお勧めします。

 DynamoDB ストリームでリソースベースのポリシーを設定し、Lambda 関数へのクロスアカウント読み取りアクセスを許可することで、異なる AWS アカウント間で Lambda トリガーを使用できます。クロスアカウントアクセスを許可するようにストリームを設定する方法については、DynamoDB 開発者ガイドの「[クロスアカウントの AWS Lambda 関数を使用したアクセスの共有](rbac-cross-account-access.md#shared-access-cross-acount-lambda)」を参照してください。

AWS Lambda の詳細については、「[AWS Lambda デベロッパーガイド](https://docs.aws.amazon.com/lambda/latest/dg/)」を参照してください。

# チュートリアル \$11: AWS CLI を使用した Amazon DynamoDB と AWS Lambda での、フィルターを使ったすべてのイベントの処理
<a name="Streams.Lambda.Tutorial"></a>

 

このチュートリアルでは、AWS Lambda トリガーを作成して、DynamoDB テーブルからのストリーミングを処理します。

**Topics**
+ [ステップ 1: ストリーミングが有効になった DynamoDB テーブルを作成する](#Streams.Lambda.Tutorial.CreateTable)
+ [ステップ 2: Lambda 実行ロールを作成する](#Streams.Lambda.Tutorial.CreateRole)
+ [ステップ 3: Amazon SNS トピックを作成する](#Streams.Lambda.Tutorial.SNSTopic)
+ [ステップ 4: Lambda 関数を作成してテストする](#Streams.Lambda.Tutorial.LambdaFunction)
+ [ステップ 5: トリガーを作成してテストする](#Streams.Lambda.Tutorial.CreateTrigger)

このチュートリアルのシナリオは、シンプルなソーシャルネットワークである Woofer です。Woofer ユーザーは、他の Woofer ユーザーに送信される *bark* (短いテキストメッセージ) を使用して通信します。次の図は、このアプリケーションのコンポーネントとワークフローを示しています。

![\[DynamoDB テーブル、ストリームレコード、Lambda 関数、および Amazon SNS トピックの Woofer アプリケーションワークフロー。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/StreamsAndTriggers.png)


1. ユーザーは DynamoDB テーブル (`BarkTable`) に項目を書き込みます。テーブルの各項目は bark を表します。

1. 新しいストリームレコードが書き込まれ、新しい項目が `BarkTable` に追加されたことを反映します。

1. 新しいストリームレコードは AWS Lambda 関数 (`publishNewBark`) をトリガーします。

1. ストリーミングレコードに、新しい項目が `BarkTable` に追加されたことが示された場合、Lambda 関数はストリーミングレコードからデータを読み込み、Amazon Simple Notification Service (Amazon SNS) のトピックにメッセージを発行します。

1. メッセージは Amazon SNS トピックの受信者によって受信されます (このチュートリアルでは、唯一の受信者は E メールアドレスです)。

**開始する前に**  
このチュートリアルでは AWS Command Line Interface AWS CLI を使用します。まだ行っていない場合は、「[AWS Command Line Interface ユーザーガイド](https://docs.aws.amazon.com/cli/latest/userguide/)」の手順に従って、AWS CLI をインストールおよび設定します。

## ステップ 1: ストリーミングが有効になった DynamoDB テーブルを作成する
<a name="Streams.Lambda.Tutorial.CreateTable"></a>

このステップでは、Woofer ユーザーからのすべての bark を保存する DynamoDB テーブル (`BarkTable`) を作成します。プライマリキーは、`Username` (パーティションキー) と `Timestamp` (ソートキー) で構成されます。これらの属性は両方とも文字列型になります。

`BarkTable` ではストリームが有効になっています。このチュートリアルの後半では、AWS Lambda 関数をストリームと関連付けてトリガーを作成します。

1. 次のコマンドを入力して、テーブルを作成します。

   ```
   aws dynamodb create-table \
       --table-name BarkTable \
       --attribute-definitions AttributeName=Username,AttributeType=S AttributeName=Timestamp,AttributeType=S \
       --key-schema AttributeName=Username,KeyType=HASH  AttributeName=Timestamp,KeyType=RANGE \
       --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
       --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
   ```

1. 出力で、`LatestStreamArn` を探します。

   ```
   ...
   "LatestStreamArn": "arn:aws:dynamodb:region:accountID:table/BarkTable/stream/timestamp
   ...
   ```

   `region` と `accountID` をメモしておきます。これらは、このチュートリアルの他のステップで必要になります。

## ステップ 2: Lambda 実行ロールを作成する
<a name="Streams.Lambda.Tutorial.CreateRole"></a>

このステップでは、AWS Identity and Access Management (IAM) ロール (`WooferLambdaRole`) を作成し、それにアクセス権限を割り当てます。このロールは、[ステップ 4: Lambda 関数を作成してテストする](#Streams.Lambda.Tutorial.LambdaFunction) で作成する Lambda 関数で使用されます。

また、ロールのポリシーを作成します。このポリシーには、Lambda 関数がランタイム時に必要とするすべてのアクセス許可が含まれます。

1. 次の内容で、`trust-relationship.json` というファイルを作成します。

------
#### [ JSON ]

****  

   ```
   {
      "Version":"2012-10-17",		 	 	 
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": "lambda.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
   ```

------

1. `WooferLambdaRole` を作成するため、以下のコマンドを入力します。

   ```
   aws iam create-role --role-name WooferLambdaRole \
       --path "/service-role/" \
       --assume-role-policy-document file://trust-relationship.json
   ```

1. 次の内容で、`role-policy.json` というファイルを作成します。(`region` および `accountID` をお客様の AWS リージョンとアカウント ID に置き換えます。)

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "logs:CreateLogGroup",
                   "logs:CreateLogStream",
                   "logs:PutLogEvents"
               ],
               "Resource": "arn:aws:logs:us-east-1:111122223333:*"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "dynamodb:DescribeStream",
                   "dynamodb:GetRecords",
                   "dynamodb:GetShardIterator",
                   "dynamodb:ListStreams"
               ],
               "Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/BarkTable/stream/*"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "sns:Publish"
               ],
               "Resource": [
                   "*"
               ]
           }
       ]
   }
   ```

------

   ポリシーには 4 つのステートメントがあり、これにより `WooferLambdaRole` は以下を実行できます。
   + Lambda 関数 (`publishNewBark`) を実行します。このチュートリアルの後半で、この関数を作成します。
   + Amazon CloudWatch Logs のアクセス。Lambda 関数はランタイム時に診断を CloudWatch Logs に書き込みます。
   + `BarkTable` の DynamoDB Streams からデータを読み込みます。
   + Amazon SNS にメッセージを公開します。

1. 次のコマンドを入力して、`WooferLambdaRole` にポリシーをアタッチします。

   ```
   aws iam put-role-policy --role-name WooferLambdaRole \
       --policy-name WooferLambdaRolePolicy \
       --policy-document file://role-policy.json
   ```

## ステップ 3: Amazon SNS トピックを作成する
<a name="Streams.Lambda.Tutorial.SNSTopic"></a>

このステップでは、Amazon SNSトピック (`wooferTopic`) を作成し、そのトピックに E メールアドレスをサブスクライブします。Lambda 関数はこのトピックを使用して、Woofer ユーザーからの新しい bark を公開します。

1. 次のコマンドを入力して、新しい Amazon SNS トピックを作成します。

   ```
   aws sns create-topic --name wooferTopic
   ```

1. 次のコマンドを入力して、`wooferTopic` に E メールアドレスをサブスクライブします (`region` および `accountID` は AWS リージョンとアカウント ID に置き換え、`example@example.com` は有効な E メールアドレスと置き換えます)。

   ```
   aws sns subscribe \
       --topic-arn arn:aws:sns:region:accountID:wooferTopic \
       --protocol email \
       --notification-endpoint example@example.com
   ```

1. Amazon SNS は E メールアドレスに確認メッセージを送信します。そのメッセージの [**サブスクリプションを確認**] リンクを選択して、サブスクリプションプロセスを完了します。

## ステップ 4: Lambda 関数を作成してテストする
<a name="Streams.Lambda.Tutorial.LambdaFunction"></a>

このステップでは、AWS Lambda 関数 (`publishNewBark`) を作成して `BarkTable` からのストリームレコードを処理します。

`publishNewBark` 関数は、`BarkTable` の新しい項目に対応するストリームイベントのみを処理します。この関数は、そのようなイベントからデータを読み取ってから、Amazon SNS をコールしてデータを公開します。

1. 次の内容で、`publishNewBark.js` というファイルを作成します。`region` および `accountID` をお客様の AWS リージョンとアカウント ID に置き換えます。

   ```
   'use strict';
   var AWS = require("aws-sdk");
   var sns = new AWS.SNS();
   
   exports.handler = (event, context, callback) => {
   
       event.Records.forEach((record) => {
           console.log('Stream record: ', JSON.stringify(record, null, 2));
   
           if (record.eventName == 'INSERT') {
               var who = JSON.stringify(record.dynamodb.NewImage.Username.S);
               var when = JSON.stringify(record.dynamodb.NewImage.Timestamp.S);
               var what = JSON.stringify(record.dynamodb.NewImage.Message.S);
               var params = {
                   Subject: 'A new bark from ' + who,
                   Message: 'Woofer user ' + who + ' barked the following at ' + when + ':\n\n ' + what,
                   TopicArn: 'arn:aws:sns:region:accountID:wooferTopic'
               };
               sns.publish(params, function(err, data) {
                   if (err) {
                       console.error("Unable to send message. Error JSON:", JSON.stringify(err, null, 2));
                   } else {
                       console.log("Results from sending message: ", JSON.stringify(data, null, 2));
                   }
               });
           }
       });
       callback(null, `Successfully processed ${event.Records.length} records.`);
   };
   ```

1. `publishNewBark.js` を含める zip ファイルを作成します。zip コマンドラインユーティリティがある場合は、次のコマンドを入力してこれを行うことができます。

   ```
   zip publishNewBark.zip publishNewBark.js
   ```

1. Lambda 関数を作成する場合は、[ステップ 2: Lambda 実行ロールを作成する](#Streams.Lambda.Tutorial.CreateRole) で作成した `WooferLambdaRole` の Amazon リソースネーム (ARN) を指定します。この ARN を取得するには、次のコマンドを入力します。

   ```
   aws iam get-role --role-name WooferLambdaRole
   ```

   出力で、`WooferLambdaRole` の ARN を探します。

   ```
   ...
   "Arn": "arn:aws:iam::region:role/service-role/WooferLambdaRole"
   ...
   ```

   次のコマンドを入力して、Lambda 関数を作成します。*roleARN* を `WooferLambdaRole` の ARN に置き換えます。

   ```
   aws lambda create-function \
       --region region \
       --function-name publishNewBark \
       --zip-file fileb://publishNewBark.zip \
       --role roleARN \
       --handler publishNewBark.handler \
       --timeout 5 \
       --runtime nodejs16.x
   ```

1. ここで、`publishNewBark` をテストして、これが動作することを確認します。これを行うには、DynamoDB Streams の実際のレコードに似た情報を入力します。

   次の内容で、`payload.json` というファイルを作成します。`region` および `accountID` をお客様の AWS リージョンとアカウント ID に置き換えます。

   ```
   {
       "Records": [
           {
               "eventID": "7de3041dd709b024af6f29e4fa13d34c",
               "eventName": "INSERT",
               "eventVersion": "1.1",
               "eventSource": "aws:dynamodb",
               "awsRegion": "region",
               "dynamodb": {
                   "ApproximateCreationDateTime": 1479499740,
                   "Keys": {
                       "Timestamp": {
                           "S": "2016-11-18:12:09:36"
                       },
                       "Username": {
                           "S": "John Doe"
                       }
                   },
                   "NewImage": {
                       "Timestamp": {
                           "S": "2016-11-18:12:09:36"
                       },
                       "Message": {
                           "S": "This is a bark from the Woofer social network"
                       },
                       "Username": {
                           "S": "John Doe"
                       }
                   },
                   "SequenceNumber": "13021600000000001596893679",
                   "SizeBytes": 112,
                   "StreamViewType": "NEW_IMAGE"
               },
               "eventSourceARN": "arn:aws:dynamodb:region:account ID:table/BarkTable/stream/2016-11-16T20:42:48.104"
           }
       ]
   }
   ```

   次のコマンドを入力して、`publishNewBark` 関数をテストします。

   ```
   aws lambda invoke --function-name publishNewBark --payload file://payload.json --cli-binary-format raw-in-base64-out output.txt
   ```

   テストが成功すると、次の出力が表示されます。

   ```
   {
       "StatusCode": 200,
       "ExecutedVersion": "$LATEST"
   }
   ```

   さらに、`output.txt` ファイルには次のテキストが含まれます。

   ```
   "Successfully processed 1 records."
   ```

   また、数分以内に新しい E メールメッセージが届きます。
**注記**  
AWS Lambda は、Amazon CloudWatch Logs に診断情報を書き込みます。Lambda 関数でエラーが発生した場合、この診断情報をトラブルシューティングに使用できます。  
CloudWatch コンソール ([https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/)) を開きます。
ナビゲーションペインで [**ログ**] を選択します。
次のロググループを選択: `/aws/lambda/publishNewBark`
最新のログストリーミングを選択して、関数からの出力 (およびエラー) を表示します。

## ステップ 5: トリガーを作成してテストする
<a name="Streams.Lambda.Tutorial.CreateTrigger"></a>

[ステップ 4: Lambda 関数を作成してテストする](#Streams.Lambda.Tutorial.LambdaFunction) で、Lambda 関数をテストして、正しく実行されたことを確認しました。このステップでは、Lambda 関数 (`publishNewBark`) をイベントソース (`BarkTable` ストリーミング) に関連付けることで*トリガー*を作成します。

1. トリガーを作成する場合、`BarkTable` ストリーム用の ARN を指定する必要があります。この ARN を取得するには、次のコマンドを入力します。

   ```
   aws dynamodb describe-table --table-name BarkTable
   ```

   出力で、`LatestStreamArn` を探します。

   ```
   ...
    "LatestStreamArn": "arn:aws:dynamodb:region:accountID:table/BarkTable/stream/timestamp
   ...
   ```

1. 次のコマンドを入力してトリガーを作成します `streamARN` を実際のストリーム ARN に置き換えます。

   ```
   aws lambda create-event-source-mapping \
       --region region \
       --function-name publishNewBark \
       --event-source streamARN  \
       --batch-size 1 \
       --starting-position TRIM_HORIZON
   ```

1. トリガーをテストします。次のコマンドを入力して、`BarkTable` に項目を追加します。

   ```
   aws dynamodb put-item \
       --table-name BarkTable \
       --item Username={S="Jane Doe"},Timestamp={S="2016-11-18:14:32:17"},Message={S="Testing...1...2...3"}
   ```

   数分以内に新しい E メールメッセージが届きます。

1. DynamoDB コンソールを開き、さらにいくつかの項目を `BarkTable` に追加します。`Username` および `Timestamp` 属性の値を指定する必要があります (必須ではないものの、`Message` の値を指定する必要があります)。`BarkTable` に追加した各項目について、新しい E メールメッセージが届きます。

   Lambda 関数は、`BarkTable` に追加した新しい項目のみを処理します。テーブル内の項目を更新または削除すると、この関数は何も行いません。

**注記**  
AWS Lambda は、Amazon CloudWatch Logs に診断情報を書き込みます。Lambda 関数でエラーが発生した場合、この診断情報をトラブルシューティングに使用できます。  
CloudWatch コンソール ([https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/)) を開きます。
ナビゲーションペインで [**ログ**] を選択します。
次のロググループを選択: `/aws/lambda/publishNewBark`
最新のログストリーミングを選択して、関数からの出力 (およびエラー) を表示します。

# チュートリアル \$12: DynamoDB と Lambda での、フィルターを使用したいくつかのイベントの処理。
<a name="Streams.Lambda.Tutorial2"></a>

このチュートリアルでは、AWS Lambda トリガーを作成して、DynamoDB テーブルからのストリームの一部のイベントのみを処理します。

**Topics**
+ [すべてをまとめる - CloudFormation](#Streams.Lambda.Tutorial2.Cloudformation)
+ [すべてをまとめる - CDK](#Streams.Lambda.Tutorial2.CDK)

[Lambda イベントフィルタリング](https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html)では、フィルター式を使用して、処理のために Lambda が関数に送信するイベントを制御できます。DynamoDB ストリームごとに最大 5 つの異なるフィルターを設定できます。バッチ処理ウィンドウを使用している場合、Lambda は新しいイベントそれぞれにフィルター条件を適用して、現在のバッチに含めるかどうかを確認します。

フィルターは `FilterCriteria` と呼ばれる構造を介して適用されます。`FilterCriteria` の 3 つの主な属性は `metadata properties`、`data properties`、および `filter patterns` です。

DynamoDB Streams イベントの構造の例を次に示します。

```
{
  "eventID": "c9fbe7d0261a5163fcb6940593e41797",
  "eventName": "INSERT",
  "eventVersion": "1.1",
  "eventSource": "aws:dynamodb",
  "awsRegion": "us-east-2",
  "dynamodb": {
    "ApproximateCreationDateTime": 1664559083.0,
    "Keys": {
      "SK": { "S": "PRODUCT#CHOCOLATE#DARK#1000" },
      "PK": { "S": "COMPANY#1000" }
    },
    "NewImage": {
      "quantity": { "N": "50" },
      "company_id": { "S": "1000" },
      "fabric": { "S": "Florida Chocolates" },
      "price": { "N": "15" },
      "stores": { "N": "5" },
      "product_id": { "S": "1000" },
      "SK": { "S": "PRODUCT#CHOCOLATE#DARK#1000" },
      "PK": { "S": "COMPANY#1000" },
      "state": { "S": "FL" },
      "type": { "S": "" }
    },
    "SequenceNumber": "700000000000888747038",
    "SizeBytes": 174,
    "StreamViewType": "NEW_AND_OLD_IMAGES"
  },
  "eventSourceARN": "arn:aws:dynamodb:us-east-2:111122223333:table/chocolate-table-StreamsSampleDDBTable-LUOI6UXQY7J1/stream/2022-09-30T17:05:53.209"
}
```

`metadata properties` は、イベントオブジェクトのフィールドです。DynamoDB Streams の場合、`metadata properties` は `dynamodb` や `eventName` のようなフィールドです。

`data properties` は、イベント本文のフィールドです。`data properties` をフィルタリングするには、それらを適切なキー内の `FilterCriteria` に含めるようにしてください。DynamoDB イベントソースのデータキーは `NewImage` または `OldImage` です。

最後に、フィルタールールは、特定のプロパティに適用するフィルター式を定義します。次に例を示します。


| 比較演算子 | 例 | ルール構文 (一部) | 
| --- | --- | --- | 
|  Null  |  製品タイプは NULL  |  `{ "product_type": { "S": null } } `  | 
|  空  |  製品名は空白  |  `{ "product_name": { "S": [ ""] } } `  | 
|  Equals  |  州はフロリダに等しい  |  `{ "state": { "S": ["FL"] } } `  | 
|  And  |  製品州はフロリダ、製品カテゴリはチョコレート  |  `{ "state": { "S": ["FL"] } , "category": { "S": [ "CHOCOLATE"] } } `  | 
|  または  |  製品州はフロリダまたはカリフォルニア  |  `{ "state": { "S": ["FL","CA"] } } `  | 
|  Not  |  製品州はフロリダ州ではない  |  `{"state": {"S": [{"anything-but": ["FL"]}]}}`  | 
|  存在する  |  地産品は存在する  |  `{"homemade": {"S": [{"exists": true}]}}`  | 
|  存在しない  |  地産品は存在しない  |  `{"homemade": {"S": [{"exists": false}]}}`  | 
|  Begins with  |  PK は COMPANY から始まる  |  `{"PK": {"S": [{"prefix": "COMPANY"}]}}`  | 

Lambda 関数には、最大 5 つのイベントフィルタリングパターンを指定できます。これら 5 つのイベントのそれぞれが論理 OR として評価されることに注意してください。そのため、`Filter_One` および `Filter_Two` という名前の 2 つのフィルターを設定すると、Lambda 関数は `Filter_One` OR `Filter_Two` を実行します。

**注記**  
[Lambda イベントのフィルタリング](https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html)ページには、数値をフィルタリングして比較するオプションがいくつかありますが、DynamoDB のフィルタイベントの場合、DynamoDB の数値は文字列として保存されるため、適用されません。例えば ` "quantity": { "N": "50" }` の場合は、`"N"` プロパティのおかげでそれが数字だとわかります。

## すべてをまとめる - CloudFormation
<a name="Streams.Lambda.Tutorial2.Cloudformation"></a>

実際のイベントフィルタリング機能を見ていただくために、CloudFormation テンプレートのサンプルを以下に示します。このテンプレートは、Amazon DynamoDB Streams を有効にしたパーティションキー PK とソートキー SK を含むシンプルな DynamoDB テーブルを生成します。これにより、Amazon Cloudwatch へのログの書き込みと Amazon DynamoDB Streams からのイベントの読み取りを許可する Lambda 関数とシンプルな Lambda 実行ロールが作成されます。また、DynamoDB Streams と Lambda 関数間にイベントソースマッピングも追加されるため、Amazon DynamoDB Streams にイベントが発生するたびに関数を実行できます。

```
AWSTemplateFormatVersion: "2010-09-09"

Description: Sample application that presents AWS Lambda event source filtering 
with Amazon DynamoDB Streams.

Resources:
  StreamsSampleDDBTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: "PK"
          AttributeType: "S"
        - AttributeName: "SK"
          AttributeType: "S"
      KeySchema:
        - AttributeName: "PK"
          KeyType: "HASH"
        - AttributeName: "SK"
          KeyType: "RANGE"
      StreamSpecification:
        StreamViewType: "NEW_AND_OLD_IMAGES"
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 5

  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17",		 	 	 
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: "/"
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: "2012-10-17",		 	 	 
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: arn:aws:logs:*:*:*
              - Effect: Allow
                Action:
                  - dynamodb:DescribeStream
                  - dynamodb:GetRecords
                  - dynamodb:GetShardIterator
                  - dynamodb:ListStreams
                Resource: !GetAtt StreamsSampleDDBTable.StreamArn

  EventSourceDDBTableStream:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 1
      Enabled: True
      EventSourceArn: !GetAtt StreamsSampleDDBTable.StreamArn
      FunctionName: !GetAtt ProcessEventLambda.Arn
      StartingPosition: LATEST

  ProcessEventLambda:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.7
      Timeout: 300
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: |
          import logging

          LOGGER = logging.getLogger()
          LOGGER.setLevel(logging.INFO)

          def handler(event, context):
            LOGGER.info('Received Event: %s', event)
            for rec in event['Records']:
              LOGGER.info('Record: %s', rec)

Outputs:
  StreamsSampleDDBTable:
    Description: DynamoDB Table ARN created for this example
    Value: !GetAtt StreamsSampleDDBTable.Arn
  StreamARN:
    Description: DynamoDB Table ARN created for this example
    Value: !GetAtt StreamsSampleDDBTable.StreamArn
```

この CloudFormation テンプレートをデプロイすると、次の Amazon DynamoDB 項目を挿入できます。

```
{
 "PK": "COMPANY#1000",
 "SK": "PRODUCT#CHOCOLATE#DARK",
 "company_id": "1000",
 "type": "",
 "state": "FL",
 "stores": 5,
 "price": 15,
 "quantity": 50,
 "fabric": "Florida Chocolates"
}
```

この CloudFormation テンプレートにインラインで組み込まれているシンプルな Lambda 関数により、Lambda 関数の Amazon CloudWatch ロググループのイベントは次のように表示されます。

```
{
  "eventID": "c9fbe7d0261a5163fcb6940593e41797",
  "eventName": "INSERT",
  "eventVersion": "1.1",
  "eventSource": "aws:dynamodb",
  "awsRegion": "us-east-2",
  "dynamodb": {
    "ApproximateCreationDateTime": 1664559083.0,
    "Keys": {
      "SK": { "S": "PRODUCT#CHOCOLATE#DARK#1000" },
      "PK": { "S": "COMPANY#1000" }
    },
    "NewImage": {
      "quantity": { "N": "50" },
      "company_id": { "S": "1000" },
      "fabric": { "S": "Florida Chocolates" },
      "price": { "N": "15" },
      "stores": { "N": "5" },
      "product_id": { "S": "1000" },
      "SK": { "S": "PRODUCT#CHOCOLATE#DARK#1000" },
      "PK": { "S": "COMPANY#1000" },
      "state": { "S": "FL" },
      "type": { "S": "" }
    },
    "SequenceNumber": "700000000000888747038",
    "SizeBytes": 174,
    "StreamViewType": "NEW_AND_OLD_IMAGES"
  },
  "eventSourceARN": "arn:aws:dynamodb:us-east-2:111122223333:table/chocolate-table-StreamsSampleDDBTable-LUOI6UXQY7J1/stream/2022-09-30T17:05:53.209"
}
```

**フィルター例**
+ **特定の州に一致する商品のみ**

この例では、CloudFormation テンプレートを変更して、フロリダで生産されたすべての製品 (略称「FL」) と一致するフィルターを含めます。

```
EventSourceDDBTableStream:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 1
      Enabled: True
      FilterCriteria:
        Filters:
          - Pattern: '{ "dynamodb": { "NewImage": { "state": { "S": ["FL"] } } } }'
      EventSourceArn: !GetAtt StreamsSampleDDBTable.StreamArn
      FunctionName: !GetAtt ProcessEventLambda.Arn
      StartingPosition: LATEST
```

スタックを再デプロイしたら、テーブルに次の DynamoDB 項目を追加できます。この例での製品はカリフォルニア産なので、Lambda 関数ログには表示されないことに注意してください。

```
{
 "PK": "COMPANY#1000",
 "SK": "PRODUCT#CHOCOLATE#DARK#1000",
 "company_id": "1000",
 "fabric": "Florida Chocolates",
 "price": 15,
 "product_id": "1000",
 "quantity": 50,
 "state": "CA",
 "stores": 5,
 "type": ""
}
```
+ **PK と SK のうち、ある値で始まるアイテムのみ**

この例では、CloudFormation テンプレートを変更して次の条件を含めます。

```
EventSourceDDBTableStream:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 1
      Enabled: True
      FilterCriteria:
        Filters:
          - Pattern: '{"dynamodb": {"Keys": {"PK": { "S": [{ "prefix": "COMPANY" }] },"SK": { "S": [{ "prefix": "PRODUCT" }] }}}}'
      EventSourceArn: !GetAtt StreamsSampleDDBTable.StreamArn
      FunctionName: !GetAtt ProcessEventLambda.Arn
      StartingPosition: LATEST
```

AND 条件では、条件がパターン内になければならないことに注意してください。ここで、キー PK と SK は同じ式内にあり、カンマで区切られます。

PK と SK がある値から始まるか、特定の州産かのどちらかです。

この例では、CloudFormation テンプレートを変更して次の条件を含めます。

```
  EventSourceDDBTableStream:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 1
      Enabled: True
      FilterCriteria:
        Filters:
          - Pattern: '{"dynamodb": {"Keys": {"PK": { "S": [{ "prefix": "COMPANY" }] },"SK": { "S": [{ "prefix": "PRODUCT" }] }}}}'
          - Pattern: '{ "dynamodb": { "NewImage": { "state": { "S": ["FL"] } } } }'
      EventSourceArn: !GetAtt StreamsSampleDDBTable.StreamArn
      FunctionName: !GetAtt ProcessEventLambda.Arn
      StartingPosition: LATEST
```

フィルターセクションに新しいパターンを導入することで、OR 条件が追加されます。

## すべてをまとめる - CDK
<a name="Streams.Lambda.Tutorial2.CDK"></a>

次のサンプル CDK プロジェクト形成テンプレートでは、イベントフィルタリング機能について説明します。この CDK プロジェクトに取り組む前に、[準備スクリプトの実行](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-python.html)を含む[前提条件をインストール](https://docs.aws.amazon.com/cdk/v2/guide/work-with.html)する必要があります。

**CDK プロジェクトを作成する**

まず、空のディレクトリで `cdk init` を呼び出して、新しい AWS CDK プロジェクトを作成します。

```
mkdir ddb_filters
cd ddb_filters
cdk init app --language python
```

この `cdk init` コマンドは、プロジェクトフォルダの名前を使用して、クラス、サブフォルダ、ファイルなど、プロジェクトのさまざまな要素に名前を付けます。フォルダ名に含まれるハイフンはすべてアンダースコアに変換されます。それ以外の場合、名前は Python 識別子の形式に従う必要があります。例えば、数字で始めたり、スペースを含めたりはしないでください。

新しいプロジェクトで作業するには、その仮想環境を有効にします。これにより、プロジェクトの依存関係をグローバルにインストールするのではなく、プロジェクトフォルダにローカルにインストールできます。

```
source .venv/bin/activate
python -m pip install -r requirements.txt
```

**注記**  
これは、仮想環境をアクティブにする Mac/Linux コマンドとして認識されるかもしれません。Python テンプレートには、同じコマンドを Windows で使用できるようにするバッチファイル、`source.bat` が含まれています。従来の Windows コマンド、`.venv\Scripts\activate.bat` も機能します。AWS CDK Toolkit v1.70.0 以前を使用して AWS CDK プロジェクトを初期化した場合、仮想環境は `.venv` ではなく `.env` ディレクトリにあります。

**基本インフラストラクチャ**

任意のテキストエディタでファイル `./ddb_filters/ddb_filters_stack.py` を開きます。このファイルは、AWS CDK プロジェクトを作成したときに自動生成されました。

次に、`_create_ddb_table` および `_set_ddb_trigger_function` 関数を追加します。これらの関数は、プロビジョニングモードのオンデマンドモードでパーティションキー PK とソートキー SK を含む DynamoDB テーブルを作成します。Amazon DynamoDB Streams をデフォルトで有効にして、新しいイメージと古いイメージを表示できます。

Lambda 関数はファイル `app.py` の下のフォルダ `lambda` に保存されます。このファイルは後で作成されます。これには環境変数 `APP_TABLE_NAME` が含まれます。この変数は、このスタックによって作成される Amazon DynamoDB テーブルの名前になります。同じ関数で、Lambda 関数にストリーム読み取り権限を付与します。最後に、Lambda 関数のイベントソースとして DynamoDB Streams にサブスクライブします。

`__init__` メソッド内のファイルの最後で、それぞれの構成を呼び出してスタック内で初期化します。追加のコンポーネントやサービスを必要とする大規模なプロジェクトでは、これらの構成を基本スタックの外部で定義するのが最適な場合があります。

```
import os
import json

import aws_cdk as cdk
from aws_cdk import (
    Stack,
    aws_lambda as _lambda,
    aws_dynamodb as dynamodb,
)
from constructs import Construct


class DdbFiltersStack(Stack):

    def _create_ddb_table(self):
        dynamodb_table = dynamodb.Table(
            self,
            "AppTable",
            partition_key=dynamodb.Attribute(
                name="PK", type=dynamodb.AttributeType.STRING
            ),
            sort_key=dynamodb.Attribute(
                name="SK", type=dynamodb.AttributeType.STRING),
            billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST,
            stream=dynamodb.StreamViewType.NEW_AND_OLD_IMAGES,
            removal_policy=cdk.RemovalPolicy.DESTROY,
        )

        cdk.CfnOutput(self, "AppTableName", value=dynamodb_table.table_name)
        return dynamodb_table

    def _set_ddb_trigger_function(self, ddb_table):
        events_lambda = _lambda.Function(
            self,
            "LambdaHandler",
            runtime=_lambda.Runtime.PYTHON_3_9,
            code=_lambda.Code.from_asset("lambda"),
            handler="app.handler",
            environment={
                "APP_TABLE_NAME": ddb_table.table_name,
            },
        )

        ddb_table.grant_stream_read(events_lambda)

        event_subscription = _lambda.CfnEventSourceMapping(
            scope=self,
            id="companyInsertsOnlyEventSourceMapping",
            function_name=events_lambda.function_name,
            event_source_arn=ddb_table.table_stream_arn,
            maximum_batching_window_in_seconds=1,
            starting_position="LATEST",
            batch_size=1,
        )

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        ddb_table = self._create_ddb_table()
        self._set_ddb_trigger_function(ddb_table)
```

次に、Amazon CloudWatch にログを出力する、非常にシンプルな Lambda 関数を作成します。それには、`lambda` という新しいフォルダを作成します。

```
mkdir lambda
touch app.py
```

任意のテキストエディタを使用して、次の内容を `app.py` ファイルに追加します。

```
import logging

LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)


def handler(event, context):
    LOGGER.info('Received Event: %s', event)
    for rec in event['Records']:
        LOGGER.info('Record: %s', rec)
```

`/ddb_filters/` フォルダにいることを確認し、次のコマンドを入力してサンプルアプリケーションを作成します。

```
cdk deploy
```

ある時点で、ソリューションをデプロイするかどうか確認する画面が表示されます。`Y` を入力して変更を確定します。

```
├───┼──────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${LambdaHandler/ServiceRole} │ arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole │
└───┴──────────────────────────────┴────────────────────────────────────────────────────────────────────────────────┘

Do you wish to deploy these changes (y/n)? y

...

✨  Deployment time: 67.73s

Outputs:
DdbFiltersStack.AppTableName = DdbFiltersStack-AppTable815C50BC-1M1W7209V5YPP
Stack ARN:
arn:aws:cloudformation:us-east-2:111122223333:stack/DdbFiltersStack/66873140-40f3-11ed-8e93-0a74f296a8f6
```

変更がデプロイされたら、AWS コンソールを開いてテーブルに項目を 1 つ追加します。

```
{
 "PK": "COMPANY#1000",
 "SK": "PRODUCT#CHOCOLATE#DARK",
 "company_id": "1000",
 "type": "",
 "state": "FL",
 "stores": 5,
 "price": 15,
 "quantity": 50,
 "fabric": "Florida Chocolates"
}
```

これで CloudWatch ログには、このエントリの情報がすべて含まれているはずです。

**フィルター例**
+ **特定の州に一致する商品のみ**

ファイル `ddb_filters/ddb_filters/ddb_filters_stack.py` を開き、「FL」に等しいすべての製品と一致するフィルターを含めるように変更します。これは 45 行目の `event_subscription` のすぐ下で修正できます。

```
event_subscription.add_property_override(
    property_path="FilterCriteria",
    value={
        "Filters": [
            {
                "Pattern": json.dumps(
                    {"dynamodb": {"NewImage": {"state": {"S": ["FL"]}}}}
                )
            },
        ]
    },
)
```
+ **PK と SK のうち、ある値で始まる項目のみ**

Python スクリプトを次の条件を含むように変更します。

```
event_subscription.add_property_override(
    property_path="FilterCriteria",
    value={
        "Filters": [
            {
                "Pattern": json.dumps(
                    {
                        {
                            "dynamodb": {
                                "Keys": {
                                    "PK": {"S": [{"prefix": "COMPANY"}]},
                                    "SK": {"S": [{"prefix": "PRODUCT"}]},
                                }
                            }
                        }
                    }
                )
            },
        ]
    },
```
+ **PK と SK がある値から始まるか、特定の州産かのどちらかです。**

Python スクリプトを次の条件を含むように変更します。

```
event_subscription.add_property_override(
    property_path="FilterCriteria",
    value={
        "Filters": [
            {
                "Pattern": json.dumps(
                    {
                        {
                            "dynamodb": {
                                "Keys": {
                                    "PK": {"S": [{"prefix": "COMPANY"}]},
                                    "SK": {"S": [{"prefix": "PRODUCT"}]},
                                }
                            }
                        }
                    }
                )
            },
            {
                "Pattern": json.dumps(
                    {"dynamodb": {"NewImage": {"state": {"S": ["FL"]}}}}
                )
            },
        ]
    },
)
```

Filters 配列に要素を追加することで OR 条件が追加されることに注意してください。

**クリーンアップ**

作業ディレクトリのベースにあるフィルタースタックを見つけて、`cdk destroy` を実行します。リソースの削除を確認するメッセージが表示されます。

```
cdk destroy
Are you sure you want to delete: DdbFiltersStack (y/n)? y
```

# Lambda での DynamoDB ストリームの使用に関するベストプラクティス
<a name="Streams.Lambda.BestPracticesWithDynamoDB"></a>

AWS Lambda 関数は、他の関数から分離された実行環境である*コンテナ*内で実行されます。この関数を初めて実行すると、AWS Lambda は新しいコンテナを作成し、関数のコードを実行し始めます。

Lambda 関数には、コールごとに 1 回実行される*ハンドラ*があります。ハンドラには、関数用の主要なビジネスロジックが含まれます。たとえば、[ステップ 4: Lambda 関数を作成してテストする](Streams.Lambda.Tutorial.md#Streams.Lambda.Tutorial.LambdaFunction) に示す Lambda 関数には、DynamoDB Streams のレコードを処理できるハンドラがあります。

コンテナの作成後、AWS Lambda が初めてハンドラーを実行する前に、1 回だけ実行される初期化コードを提供することもできます。[ステップ 4: Lambda 関数を作成してテストする](Streams.Lambda.Tutorial.md#Streams.Lambda.Tutorial.LambdaFunction) に示す Lambda 関数には、SDK for JavaScript in Node をインポートし、Amazon SNS 用のクライアントを作成する初期化コードがあります。これらのオブジェクトはハンドラの外部で 1 回のみ定義します。

関数の実行後、AWS Lambda は関数のそれ以降の呼び出しに対してコンテナを再利用する場合があります。この場合、関数ハンドラは、初期化コードで定義したリソースを再利用できる可能性があります (AWS Lambda がコンテナを保持する期間や、コンテナを再利用するかどうかを制御することはできません)。

AWS Lambda を使用した DynamoDB トリガーの場合は、次のことをお勧めします。
+ AWS のサービスのクライアントは、ハンドラではなく初期化コードでインスタンス化する必要があります。これにより、AWS Lambda コンテナは、コンテナの有効期間中は既存の接続を再利用することができます。
+ 通常、お客様が明示的に接続を管理したり、接続プールを実装したりする必要はありません。これは AWS Lambda によって自動的に管理されます。

DynamoDB ストリームの Lambda コンシューマーは、正確に一度だけ配信されることを保証するものではなく、時折重複が発生する可能性があります。重複処理が原因で予期しない問題が発生しないように、Lambda 関数コードは必ずべき等性にしてください。

詳細については、「*AWS Lambda デベロッパーガイド*」の「[AWS Lambda 関数を操作するためのベストプラクティス](https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html)」を参照してください。

# DynamoDB Streams と Apache Flink
<a name="StreamsApacheFlink.xml"></a>

Apache Flink で Amazon DynamoDB Streams レコードを使用できます。[Amazon Managed Service for Apache Flink](https://aws.amazon.com/managed-service-apache-flink/) を使用すると、Apache Flink を使用してストリーミングデータをリアルタイムで変換および分析できます。Apache Flink は、リアルタイムデータを処理するためのオープンソースのストリーム処理フレームワークです。Apache Flink 用 Amazon DynamoDB Streams コネクタは、Apache Flink ワークロードの構築と管理を簡素化し、アプリケーションを他の AWS のサービスと統合できるようにします。

Amazon Managed Service for Apache Flink は、ログ分析、クリックストリーム分析、モノのインターネット (IoT)、アドテック、ゲームなどのためのエンドツーエンドのストリーム処理アプリケーションを迅速に構築するのに役立ちます。最も一般的な 4 つのユースケースは、ストリーミングの抽出変換ロード (ETL)、イベント駆動型アプリケーション、応答性の高いリアルタイム分析、データストリームのインタラクティブなクエリです。Amazon DynamoDB Streams から Apache Flink への書き込みの詳細については、「[Amazon DynamoDB Connector](https://nightlies.apache.org/flink/flink-docs-master/docs/connectors/datastream/dynamodb/)」を参照してください。