DynamoDBについて調べたこと

# この記事について
DynamoDBに関して調べたことについてまとめました。この記事では、参照記事の内容を抜粋して掲載しています。詳細は参考URLをご覧ください。

基本的には日本語の記事を使用していますが、物によっては曖昧な翻訳があります。

# NoSQLとは

NoSQL（一般に "Not only SQL" と解釈される）とは、関係データベース管理システム (RDBMS) 以外のデータベース管理システムを指すおおまかな分類語である。

参考：https://ja.wikipedia.org/wiki/NoSQL

NoSQL データベースは特定のデータモデル専用に構築されており、最新のアプリケーションに合わせて簡単にスケールできる柔軟なスキーマにデータを格納します。NoSQL データベースは、開発、機能性、パフォーマンスを大規模かつ容易に実現できるという点で広く評価されています。

参考：https://aws.amazon.com/jp/nosql/

## NoSQLの歴史

下記記事で詳細にまとめられていたので、ご紹介させていただきます。

https://qiita.com/1amageek/items/3dbbc3112493a73880d0

# DynamoDBとは

あらゆる規模で一桁ミリ秒のパフォーマンスを実現する、サーバーレス NoSQL フルマネージドデータベース

- AP型
- 従量課金制
- コールドスタート、バージョンアップグレード、メンテナンスウィンドウ、パッチ適用、ダウンタイムメンテナンスなし

参考：https://aws.amazon.com/jp/dynamodb/
AP型(CAP定理)：https://ja.wikipedia.org/wiki/CAP%E5%AE%9A%E7%90%86

## 仕組み

### パーティショニング

Amazon DynamoDB は、データをパーティションに保存します。パーティションは、ソリッドステートドライブ (SSD) によってバックアップされ、AWS リージョン内の複数のアベイラビリティーゾーン間で自動的にレプリケートされる、テーブル用のストレージの割り当てです。パーティション管理は DynamoDB によって完全に処理されます。パーティションをご自身が管理する必要はありません。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.Partitions.html

### B-Tree

パーティショニングが水平スケーリングを可能にする一方で、単一のリクエスト内で、ある一定範囲の関連したアイテムを取得しななければならないこともよくあります。ここでDynamoDBのふたつめの核となる仕組みが登場します。B-tree は、ソート済みデータの保持に効率の良い方法です。これは多くのデータアプリケーションにおいて有効で、例えばユーザネームをアルファベット順に並べ替えたり、注文時刻によって電子取引の注文を並べ替えたりします。

DynamoDB は各パーティション上のアイテムを、パーティションキーと（もしそのテーブルで使われていてれば）ソートキーに従って整列されたB-Tree に格納します。このB-tree は、キーの探索において対数的な時間計算量を実現します。データのサブセットに対してB-Tree を使用することで、同一パーティションキーを持つアイテムの高効率な範囲クエリーを可能にしています。

参考：https://aws.amazon.com/jp/blogs/news/single-table-vs-multi-table-design-in-amazon-dynamodb/

## 基礎概念

### Tables

他のデータベースシステムと同様、DynamoDB はデータをテーブルに保存します。テーブルは、データのコレクションです。たとえば、テーブルの例 (People) を参照してください。このテーブルは、友人、家族、関心のある人に関する個人の連絡先情報を保存するのに使用できます。また、その人たちが運転する車に関する情報を保存する Cars テーブルを作成することもできます。

### Items

各テーブルにはゼロ以上の項目が含まれています。項目は、他のすべての項目間で一意に識別可能な属性のグループです。People テーブルの各項目は、人を表します。Cars テーブルの各項目は 1 台の車を表します。DynamoDB の項目は、多くの点で他のデータベースシステムの行、レコード、またはタプルに似ています。DynamoDB では、テーブルに保存できる項目数に制限はありません。

### Attributes

各項目は 1 つ以上の属性で構成されます。属性は、基盤となるデータ要素であり、それ以上分割する必要がないものです。例えば、People テーブルの項目には、PersonID、LastName、FirstName といった名前の属性が含まれます。Department テーブルでは、項目が DepartmentID、Name、Manager などの属性を設定することができます。DynamoDB 内の属性は、多くの点で他のデータベースシステムのフィールドや列に似ています。

### Peopleテーブルの例

```
People

{
    "PersonID": 101,
    "LastName": "Smith",
    "FirstName": "Fred",
    "Phone": "555-4321"
}

{
    "PersonID": 102,
    "LastName": "Jones",
    "FirstName": "Mary",
    "Address": {
                "Street": "123 Main",
                "City": "Anytown",
                "State": "OH",
                "ZIPCode": 12345
    }
}

{
    "PersonID": 103,
    "LastName": "Stephens",
    "FirstName": "Howard",
    "Address": {
                "Street": "123 Main",
                "City": "London",                                    
                "PostalCode": "ER3 5K8"
    },
    "FavoriteColor": "Blue"
}
```

- テーブルの各項目には一意の識別子があります。これは、テーブルの他のすべての項目からその項目を区別するプライマリキーです。People テーブルで、プライマリキーは 1 つの属性 (PersonID) で構成されます。

- プライマリキー以外、People テーブルはスキーマレスです。つまり、属性またはデータ型を事前に定義する必要はありません。各項目は、独自の固有の属性を持つことができます。

- 属性のほとんどはスカラーです。つまり、1 つの値のみを持つことができます。文字列と数値はスカラーの一般的な例です。

- 一部の項目には、ネストされた属性 (アドレス) があります。DynamoDB は深さが最大 32 レベルの入れ子の属性をサポートします。

参考(上記関連項目)：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.TablesItemsAttributes

### パーティションキー（Partition Key）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3790952/845735af-ca48-ff84-dcb2-a68d2453a1ea.png)
引用元：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.Partitions.html

DynamoDB は、パーティションキーの値を内部ハッシュ関数への入力として使用します。ハッシュ関数からの出力により、項目が保存されるパーティション (DynamoDB 内部の物理ストレージ) が決まります。
パーティションキーのみを含むテーブルでは、2 つの項目が同じパーティションキー値を持つことはできません。


参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey

### ソートキー（Sort Key）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3790952/df5fd7e0-f66c-76c7-d275-944b8442449c.png)

引用元：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.Partitions.html

同じパーティションキー値を持つすべての項目は、ソートキー値でソートされてまとめて保存されます。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey

パーティションキーの値が同じ項目は互いに近く、ソートキー属性の値によってソートされた順序になる傾向があります

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.Partitions.html#HowItWorks.Partitions.CompositeKey

### 複合ソートキー（Composite sort keys）

> 複合ソートキーは、`#`や`:`といった区切る記号を用いて作成されたキーのことを指します。

複合ソートキーを作成すれば、データの階層的 (1 対多) な関係を定義して、任意の階層レベルでクエリを実行することができます。

例えば、地理的場所を示すテーブルでは、次のようにソートキーを構成できます。

```
[country]#[region]#[state]#[county]#[city]#[neighborhood]
```

これにより、これらの集計レベルのいずれかの場所のリスト (country から neighborhood、またその間にあるすべてのもの) に対して、範囲のクエリを効率的に行うことができます。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/bp-sort-keys.html

### Primary Key

- 必須項目
- ユニークな値
- 内部ハッシュ関数を
- 2種類の異なるプライマリキーをサポート
    - パーティションキー
    - パーティションキーとソートキー（混合プライマリキー・Composite Primary Key）

参考文献：

https://aws.amazon.com/jp/builders-flash/202404/learn-dynamodb-primary-key/

https://aws.amazon.com/jp/blogs/database/choosing-the-right-dynamodb-partition-key/

### データ型

- スカラー型
    - 数値
    - 文字列
    - バイナリ
    - ブール
    - null
- ドキュメント型
    - リスト
    - マップ
- セット型
    - 文字セット
    - 数値セット
    - バイナリセット

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes

### Capacity Modes

#### オンデマンドモード

キャパシティプランニングなしで 1 秒あたり数百万ものリクエストを処理できるサーバーレス請求オプションです。DynamoDB オンデマンドは、読み込みおよび書き込みリクエストの料金が従量制であるため、使用した分だけを支払います。オンデマンドモードのテーブルでは、アプリケーションで実行することが予測される読み込みおよび書き込みスループットを指定する必要はありません。

オンデマンドモードでは、DynamoDB がスループットを全面的に管理します。ユーザーはテーブルのスループットキャパシティを管理することなく、必要に応じて API コールを実行できます。

- テスト・プロトタイプ時
- トラフィックの予測が難しい場合
に最適

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/capacity-mode.html

#### プロビジョンモード

プロビジョンドモードでは、アプリケーションに必要な 1 秒あたりの読み込みと書き込みの回数を指定します。プロビジョンドキャパシティを十分に活用していない場合でも、スループットキャパシティに対して課金されます。プロビジョニングした時間ごとの読み込みおよび書き込みキャパシティに基づいて課金されます。自動スケーリングを使用すると、トラフィックの変更に応じて、テーブルのプロビジョンドキャパシティを自動的に調整できます。これにより、コストの予測可能性を得るため、定義されたリクエストレート以下に維持されるように DynamoDB を制御することができます。

- トラフィックが予測可能・周期的な場合
- トラフィックの急増が限定的かつ短期な場合
に最適

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/capacity-mode.html

**読み取り容量ユニット (RCU):**
テーブルからデータを読み取るための各 API コールを読み取りリクエストといいます。読み取りリクエストは、強力な整合性のある読み取り、結果整合性のある読み取り、またはトランザクション読み取りとなります。項目のサイズが 4 KB までなら、1 RCU で、強力な整合性のある読み取りリクエストを 1 秒あたり 1 回実行できます。項目が 4 KB より大きい場合、追加の RCU が必要です。項目のサイズが 4 KB までなら、1 RCU で、結果整合性のある読み取りリクエストを 1 秒あたり 2 回実行できます。トランザクション読み取りリクエストでは、4 KB までの項目を 1 秒あたり 1 回読み取るのに 2 RCU が必要です。例えば、8 KB の項目であれば、強力な整合性のある読み取りには 2 RCU、結果整合性のある読み取りには 1 RCU、トランザクション読み取りには 4 RCU がそれぞれ必要になります。

**書き込み容量ユニット (WCU):**
テーブルにデータを書き込むための各 API コールを書き込みリクエストといいます。項目のサイズが 1 KB までなら、WCU 1 個で、標準の書き込み要求を 1 秒あたり 1 回実行できます。項目が 1 KB より大きい場合、追加の WCU が必要です。トランザクション書き込み要求では、1 KB までの項目を 1 秒あたり 1 回書き込むのに WCU 2 個が必要です。たとえば、1 KB の項目の標準書き込み要求には WCU 1 個、3 KB の項目の標準書き込み要求には WCU 3 個、3 KB の項目のトランザクション書き込み要求には WCU 6 個が必要になります。

参考：https://aws.amazon.com/jp/dynamodb/pricing/provisioned/

#### バーストキャパシティ

DynamoDB は、バーストキャパシティにより、スループットプロビジョニングに柔軟性をもたらします。利用可能なスループットを使い切っていない場合、DynamoDB は、キャパシティの未使用分を、後のスループットのバーストに備えて留保しておき、使用量のスパイクに対応します。バーストキャパシティーにより、スロットリングされていた可能性のある読み込みまたは書き込みリクエストが成功します。

現在、DynamoDB は、未使用の読み込みおよび書き込みキャパシティを最大 5 分 (300 秒) 保持します。読み込みまたは書き込みアクティビティのバーストが発生した場合、これらの余分なキャパシティユニットをすばやく消費できます。これは、テーブルに定義した 1 秒あたりのプロビジョンドスループットキャパシティよりも高速です。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/burst-adaptive-capacity.html#burst-capacity

#### アダプティブキャパシティ

DynamoDB では、パーティション間にデータを自動的に分散します。これらは、AWS クラウドの複数のサーバーに保存されます。読み込みおよび書き込みアクティビティは常に均等に分散できるとは限りません。データアクセスが不均等の場合は、他のパーティションと比較して、「ホット」パーティションに大容量の読み書きトラフィックが送信されることがあります。パーティションの読み込みおよび書き込みオペレーションは個別に管理されるため、単一のパーティションが 3,000 を超える読み込みオペレーションまたは 1,000 を超える書き込みオペレーションを受け取ると、スロットリングが発生します。アダプティブキャパシティーでは、さらに多くのトラフィックを受け取るパーティションのスループットキャパシティーを自動的に増加させます。

不均等なアクセスパターンに対応できるように、DynamoDB アダプティブキャパシティを使用することで、スロットリングすることなく、アプリケーションでホットパーティションに対して読み書きを継続できます。ただし、トラフィックは、プロビジョニングされた合計容量またはパーティションの最大容量を超えないものとします。アダプティブキャパシティでは、さらに多くのトラフィックを受け取るパーティションのスループットキャパシティを自動的かつ瞬時に増加させます。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/burst-adaptive-capacity.html#adaptive-capacity

#### スロットリング

テーブルまたはインデックスでプロビジョニングされたスループットキャパシティを超過したアプリケーションは、リクエストスロットリングの対象になります。スロットリングは、アプリケーションで大量のキャパシティーユニットが消費されるのを防ぎます。DynamoDB は、読み込みや書き込みのオペレーションをスロットリングすると、発信者に `ProvisionedThroughputExceededException` を返します。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/TroubleshootingThrottling-common-issues.html

オンデマンドキャパシティーモードを使用する DynamoDB テーブルは、アプリケーションのトラフィックボリュームに自動的に対応します。ただし、その場合でも、オンデマンドモードを使用するテーブルでスロットリングが発生する可能性があります。

発生条件例：
- トラフィックが前のピークの 2 倍を超えている
- トラフィックがパーティションあたりの最大数を超えている
- ホットキーが原因でスロットリングの問題が発生している可能性がある
- トラフィックがテーブルごとのアカウントクォータを超えている

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/TroubleshootingThrottlingOnDemand.html

#### エラーハンドリング

各 AWS SDK は、自動的に再試行ロジックを実装しています。
簡単な再試行に加えて、各 AWS SDK はエクスポネンシャルバックオフアルゴリズムを実装し、フロー制御を改善します。エクスポネンシャルバックオフは、再試行間の待機時間を累進的に長くして、連続的なエラー応答を受信するという概念に基づいています。たとえば、1 回目の再試行の前に最大 50 ミリ秒、2 回目の前に最大 100 ミリ秒、3 回目の前に最大 200 ミリ秒のようになります。ただし 1 分を経過してもリクエストが成功しない場合は、問題の原因はリクエストの速度ではなく、プロビジョニングしたスループットをリクエストのサイズが超えたためである可能性があります。1 分程度で再試行が停止するように最大回数を設定します。リクエストが失敗した場合は、プロビジョニングされたスループットのオプションを調べてください。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Programming.Errors.html#Programming.Errors.RetryAndBackoff

### DynamoDB API

#### コントロールプレーン

コントロールプレーンのオペレーションでは、DynamoDB テーブルを作成および管理できます。また、インデックス、ストリーム、およびテーブルに依存する他のオブジェクトを操作できます。

- `CreateTable`
- `DescribeTable`
- `ListTables`
- `UpdateTable`
- `DeleteTable`

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.API.html#HowItWorks.API.ControlPlane

#### データプレーン

データプレーンオペレーションでは、テーブルのデータで、作成、読み込み、更新、および削除 (CRUD とも呼ばれる) アクションを実行できます。一部のデータプレーンオペレーションでも、セカンダリインデックスからデータを読み込むことができます。

- PartiQL: Amazon DynamoDB 用の SQL 互換クエリ言語
    - `ExecuteStatement`
    - `BatchExecuteStatement`
- Classic API
    - データの作成
        - `PutItem`
        - `BatchWriteItem`
    - データの読み込み
        - `GetItem`
        - `BatchGetItem`
        - `Query`
        - `Scan`
    - データの更新
        - `UpdateItem`
    - データの削除
        - `DeleteItem`
        - `BatchWriteItem`
    
参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.API.html#HowItWorks.API.DataPlane

## セカンダリインデックス

Amazon DynamoDB によって、プライマリキーの値を指定して、テーブルの項目に高速アクセスすることが可能になります。しかし多くのアプリケーションでは、プライマリキー以外の属性を使って、データに効率的にアクセスできるようにセカンダリ（または代替）キーを 1 つ以上設定することで、メリットが得られることがあります。これに対応するために、1 つのテーブルで 1 つ以上のセカンダリインデックスを作成して、それらのインデックスに対して Query または Scan リクエストを実行することができます。

セカンダリインデックス は、テーブルからの属性のサブセットと、Query オペレーションをサポートする代替キーで構成されるデータ構造です。Query をテーブルで使用する場合と同じように、Query を使用してインデックスからデータを取得できます。テーブルには、複数のセカンダリインデックスを含めることができます。これにより、アプリケーションは複数の異なるクエリパターンにアクセスできます。

### Global Secondary Index (GSI)

パーティションキーおよびソートキーを持つインデックス。ベーステーブルのものとは異なる場合があります。このインデックスに関するクエリがすべてのパーティションにまたがり、ベーステーブル内のすべてのデータを対象とする可能性があるため、グローバルセカンダリインデックスは「グローバル」と見なされます。グローバルセカンダリインデックスは、ベーステーブルとは別に独自のパーティション領域に保存され、ベーステーブルとは別にスケーリングします。

### Local Secondary Index (LSI)

パーティションキーはベーステーブルと同じで、ソートキーが異なるインデックス。ローカルセカンダリインデックスは、ローカルセカンダリインデックスのすべてのパーティションの範囲が同じパーティションキーバリューを持つベーステーブルのパーティションに限定されるという意味で「ローカル」です。

参考 (上記関連事項)：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/SecondaryIndexes.html

### スパースインデックス

グローバルセカンダリインデックスは、デフォルトでスパースです。グローバルセカンダリインデックスを作成する際、パーティションキーおよびソートキー (オプション) を指定します。インデックスには、これらの属性を含むベーステーブル内の項目のみ、表示されます。

グローバルセカンダリインデックスをスパースに設計することにより、優れたパフォーマンスを達成しながら、ベーステーブルの書き込みスループットよりも低くプロビジョニングできます。

テーブル内の項目について、DynamoDB は項目内にインデックスソートのキーバリューがある場合のみ、対応するインデックスエントリを書き込みます。ソートキーがすべてのテーブル項目に表示されない場合や、インデックスパーティションキーが項目に存在しない場合、インデックスはスパースであると言います。

スパースインデックスは、テーブルの小さなサブセクションに対して行うクエリに役立ちます。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/bp-indexes-general-sparse-indexes.html

### 読み取り整合性

テーブルと LSI のどちらにも、結果整合性のある読み込み (デフォルト) と強力な整合性のある読み込みという 2 つの読み込み整合性オプションが用意されています。GSI とストリームからのすべての読み取りは、結果整合性のある読み込みです。

#### 結果整合性のある読み込み (Eventually Consistent Reads)

結果整合性のある読み込みは、すべての読み取りオペレーションのデフォルトの読み込み整合性モデルです。DynamoDB テーブルまたはインデックスに対して結果整合性のある読み込みを発行すると、最近完了した書き込みオペレーションの結果が応答に反映されない場合があります。少し時間が経ってから読み取りリクエストを繰り返すと、最終的に、より最新の項目が応答で返されるはずです。結果整合性のある読み込みは、テーブル、ローカルセカンダリインデックス、およびグローバルセカンダリインデックスでサポートされています。

#### 強力な整合性のある読み込み (Strongly Consistent Reads)

読み取りオペレーション (`GetItem`、`Query`、`Scan` など) には、オプションの `ConsistentRead` パラメータがあります。`ConsistentRead` を true に設定すると、DynamoDB は、成功した以前のすべての書き込みオペレーションからの更新を反映した、最新データを含む応答を返します。強力な整合性のある読み込みは、テーブルとセカンダリインデックスでのみサポートされています。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html

### ベストプラクティス

- インデックスを効率的に使用する
インデックス数は最小限に抑えます。頻繁にクエリを行わない属性では、セカンダリインデックスを作成しないようにします。
- 計画を慎重に選択する
セカンダリインデックスはストレージとプロビジョニング済みのスループットを消費するため、インデックスのサイズは可能な限り小さくすべきです。
- フェッチを回避するための頻繁なクエリの最適化
レイテンシーを可能な限り小さくしてクエリを最速にするには、クエリによって返ることが予想されるすべての属性を計画します。
- ローカルセカンダリインデックス作成時に項目コレクションのサイズ制限に注意する
ローカルセカンダリインデックスは、一度作成すると削除できません。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/bp-indexes-general.html

## Data Modeling

### シングルテーブル設計(Single-Table Design)

公式の出す直接的な記述がなかったため、下記URLに記載されている情報を要約します。

シングルテーブル設計とは、複数の異なるエンティティを1つのテーブルにまとめて格納するデータモデリング手法です。

従来のリレーショナルデータベースでは、個別のテーブルに1つのエンティティを持つ設計が主流でしたが、シングルテーブル設計では、関連するアイテムを同一のパーティションキーでグループ化し、効率的なクエリでデータの取得を可能にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3790952/7d4446e7-bcd2-d940-0d82-3168d7f45aa3.png)
引用元：https://aws.amazon.com/jp/blogs/news/single-table-vs-multi-table-design-in-amazon-dynamodb/

上図のように、同一のパーティションキーに対しアクセスパターンに応じた内容を格納することで、効率的なクエリーを実現します。

https://aws.amazon.com/jp/blogs/compute/creating-a-single-table-design-with-amazon-dynamodb/

https://aws.amazon.com/jp/blogs/database/single-table-vs-multi-table-design-in-amazon-dynamodb/

### 非正規化（Denormalization）

〈原文要約〉
基本的に、DynamoDBテーブルは下記の理由から非正規化されたスキーマを採用するべきである。
- DynamoDBはスキーマレスであるという点
- 非正規化されたデータでは、テーブルの一部のみをクエリーできパフォーマンスが良いという点
- DynamoDBでは、JOIN句が使用できないという点
- マイクロサービスアーキテクチャでは、各サービスがそれぞれの責務に関するデータを保存し処理を行うため、非正規化されたデータの方がスケールしやすいという点
- DynamoDB自体がJOIN句を用いらなくとも、効果的に関連するデータを取得できる設計であるという点

参考：https://aws.amazon.com/jp/blogs/database/should-your-dynamodb-table-be-normalized-or-denormalized/

### アクセスパターン（Access Pattern）

データを抽象化して正規化するのではなく、まずアクセスパターンに焦点を当てる必要があることに気づき、NoSQLデータストアをモデル化してパフォーマンスを最適化するための主要な戦略を学びます。

参考：https://aws.amazon.com/jp/blogs/news/single-table-vs-multi-table-design-in-amazon-dynamodb/

### 実例

下記URLで詳細に紹介されているので、一読をおすすめします。

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/bp-modeling-nosql-B.html

## データ操作

下記CRUD項目は、下記URLのClassic APIセクションに記載されている内容の転載です。
参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.API.html

### Create

#### PutItem

テーブルに単一の項目を書き込みます。プライマリキー属性を指定する必要がありますが、その他の属性を指定する必要はありません。

#### BatchWriteItem

最大 25 個の項目をテーブルに書き込みます。これは、PutItem を複数回呼び出すよりも効率的です。アプリケーションで項目を書き込むために、1 回のネットワークラウンドトリップのみで済むためです。

### Read

#### GetItem

テーブルから単一の項目を取り出します。目的の項目のプライマリキーを指定する必要があります。項目全体またはその属性のサブセットのみを取り出すことができます。

#### BatchGetItem

1 つ以上のテーブルから最大 100 個の項目を取り出します。これは、`GetItem` を複数回呼び出すよりも効率的です。アプリケーションで項目を読み込むために、1 回のネットワークラウンドトリップのみで済むためです。

#### Query

Amazon DynamoDB の Query オペレーションを使用して、プライマリキーの値に基づいて項目を探すことができます。

パーティションキーの属性名、および属性の単一値を指定する必要があります。Query はそのパーティションキー値を持つすべての項目を返します。必要に応じて、ソートキーの属性を指定し、比較演算子を使用して、検索結果をさらに絞り込むことができます。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Query.html

#### Scan

Amazon DynamoDB の Scan オペレーションでは、テーブルまたはセカンダリインデックスのすべての項目を読み込みます。デフォルトでは、Scan オペレーションはテーブルまたはインデックスのすべての項目のデータ属性を返します。

Scan は常に結果セットを返します。一致する項目がない場合、結果セットは空になります。

1 回の Scan リクエストで、最大 1 MB のデータを取得できます。DynamoDB では、必要に応じてこのデータにフィルター式を適用して、結果をユーザーに返す前に絞り込むことができます。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Scan.html

### Update

#### UpdateItem

項目の 1 つ以上の属性を変更します。変更する項目のプライマリキーを指定する必要があります。新しい属性を追加したり、既存の属性を変更または削除したりできます。ユーザー定義の条件を満たす場合にのみ更新が成功するように、条件付きの更新を実行できます。オプションで、アトミックカウンターを実装できます。このカウンタは、他の書き込みリクエストを妨害することなく、数値属性をインクリメントまたはデクリメントします。

### Delete

#### DeleteItem

テーブルから単一の項目を削除します。削除する項目のプライマリキーを指定する必要があります。

#### BatchWriteItem

1 つ以上のテーブルから最大 25 個の項目を削除します。これは、`DeleteItem` を複数回呼び出すよりも効率的です。アプリケーションで項目を削除するために、1 回のネットワークラウンドトリップのみで済むためです。

`BatchWriteItem` は、データの作成とデータの削除の両方に使用できます。

#### TTL

DynamoDB の Time to Live (TTL) は、不要になった項目を削除するためのコスト効率に優れた方法です。TTL では、項目がいつ不要になるかを示す有効期限タイムスタンプを項目ごとに定義できます。DynamoDB は、書き込みスループットを消費することなく、有効期限が切れてから数日以内に期限切れの項目を自動的に削除します。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/TTL.html

## 条件付きの書き込み

デフォルトでは、DynamoDB 書き込みオペレーション (PutItem、UpdateItem、DeleteItem) は無条件です。つまり、これらの各オペレーションでは、指定されたプライマリキーを持つ既存の項目が上書きされます。

DynamoDB はオプションでこれらのオペレーションの条件付き書き込みをサポートしています。条件付き書き込みが成功するのは、項目の属性が 1 つ以上の想定条件を満たす場合のみです。それ以外の場合は、エラーが返されます。

条件付き書き込みでは、その条件について項目の最新更新バージョンと照合します。なお、項目が以前に存在しなかった場合や、その項目に対して最後に成功した操作が削除であった場合、条件付き書き込みでは以前の項目は検出されません。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/WorkingWithItems.html#WorkingWithItems.ConditionalUpdate

## トランザクション（Transaction）

トランザクションによって不可分性、一貫性、分離性、耐久性 (ACID) が実現されるため、アプリケーション内でのデータの精度を維持することがさらに容易になります。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.API.html#HowItWorks.API.Transactions

Amazon DynamoDB Transactions を使用すれば、複数のアクションをまとめてグループ化し、1 つのオールオアナッシングの TransactWriteItems または TransactGetItems オペレーションとして送信できます。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/transaction-apis.html

### TransactWriteItems API

最大 100 の書き込みアクションを 1 つのオールオアナッシングオペレーションにグループ化する、同期的でべき等な書き込みオペレーションです。これらのアクションは、同じ AWS アカウントおよび同じリージョン内の 1 つ以上の DynamoDB テーブルにある最大 100 個の異なる項目をターゲットにすることができます。トランザクション内のアイテムの合計サイズは 4 MB を超えることはできません。すべて成功するかどれも成功しないかのどちらとなるように、アトミックに実行されます。

呼び出しを行ってリクエストが冪等であることを確認する場合、オプションでクライアントトークンを含めることができます。トランザクションを冪等にすると、接続のタイムアウトや他の接続問題に伴って同じオペレーションが複数回送信された場合に、アプリケーションエラーを防ぐことができます。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txwriteitems

### TransactGetItems API

最大 100 個の `Get` アクションをまとめてグループ化する同期読み取りオペレーションです。これらのアクションは、同じ AWS アカウントおよびリージョン内の 1 つ以上の DynamoDB テーブルにある最大 100 個の異なる項目をターゲットにすることができます。トランザクション内の項目の合計サイズは 4 MB を超えることはできません。

### 分離レベル

トランザクションオペレーション (`TransactWriteItems` または `TransactGetItems`) と他のオペレーションの分離レベルは、次のとおりです。

**SERIALIZABLE**
直列化可能分離レベルでは、複数の同時オペレーションの結果は、前のオペレーションが完了するまでオペレーションが開始されない場合と同じになります。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-isolation

### 競合の処理

- 項目に対する `PutItem`、`UpdateItem`、または `DeleteItem` リクエストが、同じ項目を含む継続中の `TransactWriteItems` リクエストと競合する。
- `TransactWriteItems` リクエスト内の項目が、継続中の別の `TransactWriteItems` リクエストの一部である。
- `TransactGetItems` リクエスト内の項目が、継続中の `TransactWriteItems`、`BatchWriteItem`、`PutItem`、`UpdateItem`、または `DeleteItem` リクエストの一部である。

### DynamoDB アクセラレーター(DAX)

Amazon DynamoDB Accelerator (DAX) は、Amazon DynamoDB 用に構築されたフルマネージド型で可用性の高いキャッシュサービスです。DAX は、1 秒あたり数百万のリクエストにおいても、ミリ秒からマイクロ秒へと最大 10 倍のパフォーマンス向上を実現します。DAX は、開発者がキャッシュの無効化、データ集計、またはクラスター管理を行う必要なく、DynamoDB テーブルへのインメモリアクセラレーションの追加に必要な作業をすべて担います。DAX は既存の DynamoDB API コールと互換性があるため、アプリケーションロジックを変更する必要はありません。 DynamoDB 同様、プロビジョニングした容量に対してのみのお支払いとなります。スケーリングのパフォーマンスについて心配することなく、顧客向けのアプリケーションの構築に集中できます。
参考：https://aws.amazon.com/jp/dynamodbaccelerator/

`TransactWriteItems` と `TransactGetItems` が、どちらも DynamoDB と同じ分離レベルで DynamoDB アクセラレーター (DAX) でサポートされています。

`TransactWriteItems` は DAX を介して書き込みます。DAX は DynamoDB に `TransactWriteItems` コールを渡し、応答を返します。書き込み後にキャッシュにデータを追加するために、DAX は、`TransactWriteItems` オペレーション内の各項目に対してバックグラウンドで TransactGetItems をコールします。これにより、追加の読み込み容量単位が消費されます。この機能により、アプリケーションロジックをシンプルに保ち、トランザクション処理と非トランザクション処理の両方に DAX を使用できます。

`TransactGetItems` コールは、項目がローカルにキャッシュされることなく DAX を通過します。これは、DAX の強い整合性のある読み込み API と同じです。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-dax

## DynamoDB Stream

DynamoDB Streams は、DynamoDB テーブル内の項目レベルの変更に関するシーケンスを時間順にキャプチャし、その情報を最大 24 時間ログに保存します。アプリケーションは、このログにアクセスし、データ項目の変更前および変更後の内容をほぼリアルタイムで参照できます。

DynamoDB Streams は、DynamoDB テーブル内の項目レベルの変更に関するシーケンスを時間順にキャプチャし、その情報を最大 24 時間ログに保存します。アプリケーションは、このログにアクセスし、データ項目の変更前および変更後の内容をほぼリアルタイムで参照できます。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Streams.html

## メトリックスとディメンション

DynamoDB を操作すると、メトリクスとディメンションが CloudWatch に送信されます。

DynamoDB は、消費されたプロビジョンドスループットを 1 分間出力します。消費したキャパシティが、設定したターゲット使用率を 2 分間連続して超過すると、自動スケーリングがトリガーされます。CloudWatch アラームは、自動スケーリングをトリガーする前に、最大数分の短い遅延を伴う場合があります。この遅延により、CloudWatch メトリクスの正確な評価が保証されます。消費されたスループットのスパイク間隔が 1 分を超えると、自動スケーリングはトリガーされない場合があります。同様に、15 個の連続するデータポイントがターゲット使用率を下回ると、スケールダウンイベントが発生する場合があります。いずれの場合も、自動スケーリングのトリガー後に、UpdateTable API が呼び出されます。テーブルやインデックスのプロビジョンドキャパシティの更新には、数分かかる場合があります。この間に、テーブルの前のプロビジョンドキャパシティを超えるリクエストはスロットリングされます。

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/metrics-dimensions.html

以下一部抜粋

参考：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/metrics-dimensions.html#dynamodb-metrics-dimensions

読み込み/書き込み容量モード：https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/capacity-mode.html

### ConsumedReadCapacityUnits

プロビジョンドキャパシティとオンデマンドキャパシティの両方について、指定された期間内に消費された読み取りキャパシティユニットの数。これにより、使用されたスループットの量を追跡できます。テーブルとそのすべてのグローバルセカンダリインデックス、または特定のグローバルセカンダリインデックスの消費された読み込み容量の合計を取得できます。

TableName ディメンションは、グローバルセカンダリインデックスではなくテーブルの ConsumedReadCapacityUnits を返します。グローバルセカンダリインデックスの ConsumedReadCapacityUnits を表示するには、TableName と GlobalSecondaryIndexName の両方を指定する必要があります。

### ConsumedWriteCapacityUnits

プロビジョンドキャパシティとオンデマンドキャパシティの両方について、指定された期間内に消費された書き込みキャパシティユニットの数。これにより、使用されたスループットの量を追跡できます。テーブルとそのすべてのグローバルセカンダリインデックス、または特定のグローバルセカンダリインデックスの消費された書き込み容量の合計を取得できます。詳細については、「読み込み/書き込み容量モード」を参照してください。

TableName ディメンションは、グローバルセカンダリインデックスではなくテーブルの ConsumedWriteCapacityUnits を返します。グローバルセカンダリインデックスの ConsumedWriteCapacityUnits を表示するには、TableName と GlobalSecondaryIndexName の両方を指定する必要があります。

### ProvisionedReadCapacityUnits

テーブルまたはグローバルセカンダリインデックスのプロビジョニング済み読み込み容量ユニットの数。TableName ディメンションは、グローバルセカンダリインデックスではなくテーブルの ProvisionedReadCapacityUnits を返します。グローバルセカンダリインデックスの ProvisionedReadCapacityUnits を表示するには、TableName と GlobalSecondaryIndexName の両方を指定する必要があります。

### ProvisionedWriteCapacityUnits

テーブルまたはグローバルセカンダリインデックスのプロビジョニング済み書き込み容量ユニットの数。

TableName ディメンションは、グローバルセカンダリインデックスではなくテーブルの ProvisionedWriteCapacityUnits を返します。グローバルセカンダリインデックスの ProvisionedWriteCapacityUnits を表示するには、TableName と GlobalSecondaryIndexName の両方を指定する必要があります。

### ThrottledRequests

リソース (テーブルやインデックスなど) のプロビジョニング済みスループット制限を超える DynamoDB へのリクエスト。

リクエスト内でいずれかのイベントがプロビジョニング済みスループットクォータを超過した場合、ThrottledRequests が 1 つ増加します。例えば、グローバルセカンダリインデックスを持つテーブル内の項目を更新する場合、テーブルへの書き込みと各インデックスへの書き込みという複数のイベントが発生します。これらのイベントの 1 つまたは複数がスロットリングされている場合、ThrottledRequests が 1 つ増加します。

### SuccessfulRequestLatency

指定した期間中に成功した DynamoDB または Amazon DynamoDB Streams へのリクエストのレイテンシー。

## DynamoDBとの相性が悪いこと

こちらの記事にまとまっているので、ご紹介させていただきます。

https://zenn.dev/hsaki/articles/aws-dynamodb-non-suited


---

Happy Coding〜