

# 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 はその項目のデータをインデックスに書き込みません。