😇

DynamoDB入門:テーブル作成からGSIを使ったアイテムの取得まで!

こんにちは!
Rondの野田です。

今回はDynamoDBを利用してみました。
軽く触ったことはあったのですが導入からやったことはなかったので勉強になりました。
とりあえずアプリケーションから接続して登録・削除ができれば良かったのでDynamoDB Localを利用しました。
ローカルに簡単にDynamoDBを構築できるので便利です。

DynamoDB Localとdynamodb-adminの構築

以下のようなdocker-compose.ymlを用意します。

docker-compose.yml
version: '3.3'
services:
  dynamodb-local:
    container_name: dynamodb-local
    image: amazon/dynamodb-local:latest
    user: root
    command: -jar DynamoDBLocal.jar -sharedDb -dbPath /data
    volumes:
      - dynamodb-local-data:/data
    ports:
      - '8000:8000'
    networks:
      - dynamodb-local-network

  dynamodb-admin:
    container_name: dynamodb-admin
    image: aaronshaf/dynamodb-admin:latest
    environment:
      - DYNAMO_ENDPOINT=dynamodb-local:8000
    ports:
      - '8001:8001'
    depends_on:
      - dynamodb-local
    networks:
      - dynamodb-local-network

volumes:
  dynamodb-local-data:

networks:
  dynamodb-local-network:
    driver: bridge

そしてコンテナを立ち上げます。

$ docker-compose up -d

すると、DynamoDB Local(8000ポート)とdynamodb-admin(8001ポート)が立ち上がります。

以下にアクセスするとGUIでDynamoDBを操作できます。
http://localhost:8001/

テーブルの作成

今回は「どのユーザにどの通知が紐付いているか」を管理するテーブルを作ってみます。
以下のコマンドで作成できますが、dynamodb-adminから作成しても良いと思います。

$ aws dynamodb create-table \
    --table-name notifications \
    --key-schema '[{ "AttributeName": "user_id", "KeyType": "HASH" }, { "AttributeName": "notification_id", "KeyType": "RANGE" }]' \
    --attribute-definitions '[{ "AttributeName": "user_id", "AttributeType": "N" }, { "AttributeName": "notification_id", "AttributeType": "N" }]' \
    --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 \
    --endpoint-url http://localhost:8000

このコマンドを実行すると、「notifications」というテーブルが作成されます。
主キー構造としてuser_id(パーティションキー)とnotification_id(ソートキー)が設定されます。
また、プロビジョニングされたスループットが1の読み取りキャパシティユニットと1の書き込みキャパシティユニットで設定されます。

各パラメータの説明は以下のようになります。

  1. --table-name notifications: 作成するテーブルの名前を指定します。
  2. --key-schema: テーブルの主キー構造を定義します。この場合、以下の2つの属性が含まれています。
    • user_idはパーティションキー(ハッシュキー)として指定されており、DynamoDBはこれを使ってデータを分散させます。
    • notification_idはソートキー(レンジキー)として指定されており、パーティション内でデータをソートするために使用されます。
  3. --attribute-definitions: 属性の型を定義します。このコマンドでは、user_id と notification_id の両方の属性が「N」(数値型)であることを指定しています。
  4. --provisioned-throughput: テーブルのプロビジョニングされたスループット(読み取りと書き込みのキャパシティユニット)を指定します。
  5. --endpoint-url: コマンドを実行するDynamoDBのエンドポイントURLを指定しています。

パーティションキーとソートキー

パーティションキー(ハッシュキー)

パーティションキーは、DynamoDBテーブル内のデータを分散させるために使用されます。
DynamoDBは、パーティションキーの値に基づいて、データを複数のパーティションに分割します。

ソートキー(レンジキー)

ソートキーは、パーティション内のアイテムをソートするために使用されます。
同じパーティションキーを持つ複数のアイテムが存在する場合、ソートキーによってそれらのアイテムが順序付けられます。

データの登録

以下のようにコマンドでデータを登録できます。
dynamodb-adminを使用すると、GUI上からJSON形式でデータを登録できます。

$ aws dynamodb put-item --table-name notifications --item '{ "user_id": { "N": "1" }, "notification_id": { "N": "1" } }' --endpoint-url http://localhost:8000
$ aws dynamodb put-item --table-name notifications --item '{ "user_id": { "N": "1" }, "notification_id": { "N": "2" } }' --endpoint-url http://localhost:8000
$ aws dynamodb put-item --table-name notifications --item '{ "user_id": { "N": "1" }, "notification_id": { "N": "3" } }' --endpoint-url http://localhost:8000

user_id, notification_id以外の値も登録したい場合は追加で指定してあげるだけで大丈夫です。
テーブル定義を更新する必要はないです。

$ aws dynamodb put-item --table-name notifications --item '{ "user_id": { "N": "1" }, "notification_id": { "N": "1" }, "type": { "S": "PUSH" } }' --endpoint-url http://localhost:8000

データの取得

データを登録したので今度は取得してみます。
データを取得するにはget-itemコマンドかqueryコマンドを使用します。

get-itemコマンド

user_id=1, notification_id=1のアイテムを取得します。

$ aws dynamodb get-item \
    --table-name notifications \
    --key '{ "user_id": { "N": "1" }, "notification_id": { "N": "1" } }' \
    --endpoint-url http://localhost:8000

queryコマンド

user_id=1のアイテムをすべて取得します。

$ aws dynamodb query \
    --table-name notifications \
    --key-condition-expression "user_id = :user_id_val" \
    --expression-attribute-values '{":user_id_val": {"N": "1"}}' \
    --endpoint-url http://localhost:8000

GSI

GSI(Global Secondary Index)は、DynamoDBテーブルに対して追加の索引を作成する機能です。
GSIを使用すると、テーブルのプライマリキー以外の属性でデータを効率的にクエリできるようになります。
GSIには独自のパーティションキーとソートキーがあり、元のテーブルとは異なるキー構造を持つことができます。

例として、type属性を追加してデータを取得してみます。
typeは通知の種類(例: EMAIL, PUSH, SMS)を示す属性とします。

まず、GSIを作成するために、AWS CLIの update-table コマンドを使用します。

$ aws dynamodb update-table \
    --table-name notifications \
    --attribute-definitions AttributeName=type,AttributeType=S \
    --global-secondary-index-updates '[
    {
        "Create": {
            "IndexName": "TypeIndex",
            "KeySchema": [{
                "AttributeName": "type",
                "KeyType": "HASH"
            }, {
                "AttributeName": "notification_id",
                "KeyType": "RANGE"
            }],
            "Projection": {
                "ProjectionType": "ALL"
            },
            "ProvisionedThroughput": {
                "ReadCapacityUnits": 1,
                "WriteCapacityUnits": 1
            }
        }
    }
    ]
    ' \
    --endpoint-url http://localhost:8000
  1. --attribute-definitions: GSIに使用される新しい属性(この場合はtype)の定義を指定しています。type は文字列型(S)であることを示しています。
  2. --global-secondary-index-updates: GSIの作成情報を指定しています。
    • IndexName: GSIの名前をTypeIndexとして指定しています。
    • KeySchema: GSIのキースキーマを定義しています。ここでは、type をパーティションキー(HASH)とし、notification_id をソートキー(RANGE)として設定しています。
    • Projection: GSIに含める属性を指定しています。ProjectionType が ALL なので、すべての属性がGSIに含まれます。
    • ProvisionedThroughput: GSIのプロビジョニングされたスループットを指定しています。読み取りキャパシティユニットと書き込みキャパシティユニットをそれぞれ1に設定しています。

GSIが作成されると、type属性でデータを取得できます。
typeが"PUSH"のアイテムを取得するには以下のコマンドを実行します。

$ aws dynamodb query \
    --table-name notifications \
    --index-name TypeIndex \
    --key-condition-expression "#t = :type_val" \
    --expression-attribute-names '{"#t": "type"}' \
    --expression-attribute-values '{":type_val": {"S": "PUSH"}}' \
    --endpoint-url http://localhost:8000

あとがき

これまでなんとなく使っていたパーティションキーとソートキーをそれなりに理解できた気がします。
GSIを知るキッカケにもなったので実際に触ってみてよかったです。
DynamoDBの得意分野を活かして、より効率的にデータを取得できるようにしていきたいですね!

Rond Tech Blog

Discussion