DynamoDB の S3への増分エクスポートを試してみた

# 背景・目的
AWSが2023年9月26日に[Amazon DynamoDB(DDB)からAmazon S3(S3)へのインクリメンタルエクスポート機能を発表](https://aws.amazon.com/jp/about-aws/whats-new/2023/09/incremental-export-s3-amazon-dynamodb/)いたしました。この新機能により、どのようにデータ管理が変わるか、実際に手を動かしながら基本的な挙動を確認してみます。


# まとめ
- AWSの増分エクスポート機能により、挿入、更新、または削除されたデータを少量ずつエクスポートでき、数メガバイトからテラバイトのデータ範囲に対応しています。
- ポイントインタイムリカバリが有効なDDBテーブルが対象です。
- 指定した期間の増分データをS3バケットにエクスポートします。
- この機能はフルマネージドであり、DDBの容量を消費せず、定期的なデータ更新が容易になります。
- サポートされているデータ形式はDynamoDB JSONとAmazon Ionです。
- これにより、テーブル全体を毎回エクスポートする必要がなく、変更データのみを効率的にダウンストリームデータレイクや分析ターゲットに送ることができます。


# 概要
[Announcing incremental export to S3 for Amazon DynamoDB](https://aws.amazon.com/jp/about-aws/whats-new/2023/09/incremental-export-s3-amazon-dynamodb/)を元に概要を整理します。

> 本日、Amazon DynamoDB は、指定された時間間隔内に変更されたデータのみをエクスポートできる S3 への増分エクスポートの一般提供を発表しました。増分エクスポートを使用すると、挿入、更新、または削除されたデータを少しずつエクスポートできるようになりました。AWS マネジメントコンソール、API 呼び出し、または AWS コマンドラインインターフェイスで数回クリックするだけで、数メガバイトからテラバイトの範囲で変更されたデータをエクスポートできます。ポイントインタイムリカバリが有効になっている DynamoDB テーブルを選択し、増分データが必要なエクスポート期間を指定し、ターゲットの Amazon S3 バケットを選択して、エクスポートします。

> 増分エクスポートを使用すると、テーブル全体のエクスポートを毎回実行することなく、変更データ キャプチャ パイプラインをセットアップしてダウンストリーム データ レイクまたは分析ターゲットを定期的に更新できます。S3 への増分エクスポートはフルマネージド機能であり、DynamoDB テーブルの容量を消費しません。サポートされているデータ形式は、DynamoDB JSON と Amazon Ion です。増分エクスポートは、すべての AWS 商用リージョンと GovCloud で利用できます。

- AWSの増分エクスポート機能はDDBからS3へのデータエクスポートを効率化。
- 挿入、更新、削除されたデータを少量ずつエクスポートする。
- データ範囲は数メガバイトからテラバイトまで対応。
- 操作はマネコン、API、CLIから実行可能
- ポイントインタイムリカバリ（PITR）が有効なDynamoDBテーブルで、指定期間の増分データをS3にエクスポート可能。
- フルマネージドサービスで、DynamoDBの容量は消費されない。
- 定期的なデータ更新が容易になる
- サポートされるデータ形式はDynamoDB JSONとAmazon Ion。
- テーブル全体をエクスポートする必要がなく、変更データのみをダウンストリームデータレイクや分析ターゲットに送れる。
- すべてのAWSリージョンで利用可能

## 機能
[Introducing incremental export from Amazon DynamoDB to Amazon S3](https://aws.amazon.com/jp/blogs/database/introducing-incremental-export-from-amazon-dynamodb-to-amazon-s3/)を元に整理します。

- Incremental Exportでは、下記が選択可能です。
    - 期間
    - 出力イメージ：「New only」または「New and Old」
    - Encryption：「S3-SSE」、「SSE-KMS」

# 実践
下記について試してみます。
- Incremental Exportでタイムウインドウを変更した際の比較
- Incremental Exportの「New only」と「New and old」の比較

なお、本検証は、[こちらのドキュメント](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/S3DataExport.HowItWorks.html)を参考に進めていきます。

## 事前準備
### DDBの準備
#### テーブルの作成
1. 下記の内容を入力し、「テーブルの作成」ボタンをクリックします。
    - テーブル名：ddb_incremental_export_table（任意）
    - パーティションキー：id（任意）
    - キャパシティ：オンデマンド
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/fcb49361-da91-31c8-e250-d1b36429c6b8.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/6dd6c3fb-635f-3bed-f933-30fdfc00e365.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/5ba925bd-8787-6ac8-92b8-df98b3763a4f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/321a631a-8575-0a72-f2e9-8553876d053e.png)

#### PITRの有効化
1. 作成したテーブルで、①「バックアップ」タブ、②「編集」をクリックします。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/17c1fda3-3e7e-84cf-30fb-bdb1126c07e1.png)

1. 「ポイントタイムリカバリの有効化」をチェックし、「変更を保存」をクリックします。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/28d48ae4-f276-864a-b598-b920797c62b0.png)

### S3の作成
1. S3バケットを作成します。

### データの準備
1. 下記のデータを新規に登録します。

    ```
    id value
    ===
    1 test1
    2 test200
    ```

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/ae125ba9-8172-6c46-959e-6a04eeb02152.png)


## Incremental Export (New only)


1. S3へのエクスポートを選択します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/fd8fc0d0-6966-ad29-bff9-34a0a520ed54.png)

1. S3バケットを指定して、エクスポートを設定します。
    - エクスポート期間を、最後の30min
    - JSONフォーマット
    - 新しいイメージのみ
    - 暗号化キータイプは、SSE-S3

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/56a00048-3e1c-8e74-1510-2eee0a5b3955.png)


1. 出力されました。下記のうち、20.0B以外の2つのファイルを確認します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/3334dd9a-c252-87d3-8e55-bf4f6a6a191c.png)

1. NewImagesの属性に更新後のデータのみ含まれていることがわかりました。
    ```
    $ ls -l *json
    -rw-r--r--  1 XXX  XXX  139  9 27 09:21 4kkyzi6ibqzw3loswrastf2g7u.json
    -rw-r--r--  1 XXX  XXX  137  9 27 09:21 e2uc3uj2kuygnkitwuz3iueo7e.json
    $ cat *json
    {"Metadata":{"WriteTimestampMicros":{"N":"1695772297480792"}},"Keys":{"id":{"N":"2"}},"NewImage":{"id":{"N":"2"},"value":{"S":"test200"}}}
    {"Metadata":{"WriteTimestampMicros":{"N":"1695772182799033"}},"Keys":{"id":{"N":"1"}},"NewImage":{"id":{"N":"1"},"value":{"S":"test1"}}}
    $ 
    ```

### 補足：Full Exportもあらためて確認
1. フルエクスポートも試してみます。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/078de66e-1230-562c-2f79-c342c2152de2.png)

1. 出力されました。下記のうち、20.0B以外の2つのファイルを確認します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/7974e9be-1970-1776-43b9-64e29e102059.png)

1. Full Exportの場合は、想定通り最後のデータが出力されています。
    ```
    $ ls *json
    h5vnx46qoe23xjz6227jvau6ii.json	rb3bvaj7vu2ptkbglxzmiy2lli.json
    $ cat *json
    {"Item":{"id":{"N":"2"},"value":{"S":"test200"}}}
    {"Item":{"id":{"N":"1"},"value":{"S":"test1"}}}
    $ 
    ```

## Incremental Export (New and old)
1. データを更新・追加・削除します。
    - id1を削除
    - id2のvalueをtest200からtest2に変更
    - id4を追加

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/831a2001-47bc-d13f-0bd6-41eace9e4677.png)

1. 新旧イメージでエクスポートします。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/3574e44d-d2ca-ba10-f910-7b9d986640fe.png)

1. 出力されました。下記のうち、20.0B以外の2つのファイルを確認します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/4b278971-0dd7-e685-a509-fd3f281ef8ee.png)

1. NewImageとOldImageの属性が出力されました。下記の確認ができました。
    - 削除したid1については、OldImageのみ
    - 変更したid2については、OldImageとNewImage
    - 追加したid4については、NewImageのみ
    ```
    $ cat *json
    {"Metadata":{"WriteTimestampMicros":{"N":"1695775410591400"}},"Keys":{"id":{"N":"1"}},"OldImage":{"id":{"N":"1"},"value":{"S":"test1"}}}
    {"Metadata":{"WriteTimestampMicros":{"N":"1695775397956928"}},"Keys":{"id":{"N":"2"}},"OldImage":{"id":{"N":"2"},"value":{"S":"test200"}},"NewImage":{"id":{"N":"2"},"value":{"S":"test2"}}}
    {"Metadata":{"WriteTimestampMicros":{"N":"1695775321187562"}},"Keys":{"id":{"N":"4"}},"NewImage":{"id":{"N":"4"},"value":{"S":"test4"}}}
    $
    ```

## Incremental Export (New) 2回目
上記の状態でNewのみで出力した場合の挙動を確認してみます。


1. 新しいイメージのみを選択し、「エクスポート」を選択します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/53ba8d5b-1af0-cc81-746e-ca95e2a3c00c.png)

1. 出力されました。下記のうち、20.0B以外の2つのファイルを確認します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/c8c7b428-360c-eb0f-d2bc-893d3ac053b7.png)

1. id2とid4以外にも、id1の値が出力されています。
    ```
    $ ls *json
    7wm5lpcvzu3khph4usyxbnybdy.json	dvixszpzsu2tpjzoogjevvt3ii.json
    $ cat *json
    {"Metadata":{"WriteTimestampMicros":{"N":"1695775410591400"}},"Keys":{"id":{"N":"1"}}}
    {"Metadata":{"WriteTimestampMicros":{"N":"1695775397956928"}},"Keys":{"id":{"N":"2"}},"NewImage":{"id":{"N":"2"},"value":{"S":"test2"}}}
    {"Metadata":{"WriteTimestampMicros":{"N":"1695775321187562"}},"Keys":{"id":{"N":"4"}},"NewImage":{"id":{"N":"4"},"value":{"S":"test4"}}}
    $ 
    ```


こちらの[ブログの記事](https://aws.amazon.com/jp/blogs/database/introducing-incremental-export-from-amazon-dynamodb-to-amazon-s3/)でいうところの、id1はKeysのみ表示されているようです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/8a7ddbbb-4208-eea6-a2ea-3c5c28c31bba.png" width=500>

※出典：[Introducing incremental export from Amazon DynamoDB to Amazon S3](https://aws.amazon.com/jp/blogs/database/introducing-incremental-export-from-amazon-dynamodb-to-amazon-s3/)

## Incremental Export (New) 3回目(更新されないデータは出力されるのか確認)
上記から変更せずに、エクスポート期間を短縮しエクスポートし、どのようなデータが出力されるか確認します。

1. 期間を15min（この期間に更新はしていない。）に変更し、新しいイメージのみを選択し、「エクスポート」を選択します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/fcc9cbb1-f0a9-f499-beec-4684f10c829e.png)

1. 出力されました。20.0Bのファイルしかないようです。念のため内容を確認してみます。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/1785923a-506f-0a0a-1fe8-55fb9477bef5.png)

1. やはり出力されていないようです。
    ```
    $ ls *json
    3dqevhuv2m2mpa2zm3b63izloq.json	evskgte45m6q5du3b7kbh5imci.json	sars3z627i2xdgkwj62y6rv6yq.json	ztjtq4vwwe65jnhmgh6ypyheoe.json
    $ cat *json
    $
    ```


# 考察
- 更新・削除・追加のステータスを確認するには、「New Image Only」の場合、下記のように確認すればよさそうです。
    - New Imageがある場合、InsertかUpdateされていること
    - New Imageがない場合、Deleteされていること
- Incremental Exportを利用すれば、DDBからS3上での差分更新などを効率よく行えそうです。



# 参考
https://aws.amazon.com/jp/about-aws/whats-new/2023/09/incremental-export-s3-amazon-dynamodb/

https://aws.amazon.com/jp/blogs/database/introducing-incremental-export-from-amazon-dynamodb-to-amazon-s3/

https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/S3DataExport.HowItWorks.html

https://qiita.com/zumax/items/5fb56939162fa1660e0e