初めに
この記事で DynamoDB について調べたので、せっかくなら触ってみる。
ローカル環境構築
version: '3.8'
services:
dynamodb-local:
command: "-jar DynamoDBLocal.jar -sharedDb -dbPath ./data"
image: "amazon/dynamodb-local:latest"
container_name: dynamodb-local
ports:
- "8000:8111" # 公式では 8000 ですが、port が被ってたので 8111
volumes:
- "./docker/dynamodb:/home/dynamodblocal/data"
working_dir: /home/dynamodblocal
aws cli でアクセスできる
aws dynamodb list-tables --endpoint-url http://localhost:8111
{
"TableNames": []
}
テーブル作成
X のような SNS を想定した次のような機能があるシステムを想定。
1. ユーザーのプロフィール管理
ユーザー名、メールアドレス、登録日などの基本情報を保存。
ユーザー間の友達リスト
2. 各ユーザーが持つ友達リストを保存。
投稿(ポスト)機能
3. ユーザーが投稿を作成する。
各投稿にはタイトル、本文、作成日時、いいね数が含まれる。
いいね(Likes)機能
4. 各投稿に対して他のユーザーが「いいね」できる。
テーブル定義の json を記載する
{
"TableName": "ExampleSns",
"AttributeDefinitions": [
{
"AttributeName": "PK",
"AttributeType": "S"
},
{
"AttributeName": "SK",
"AttributeType": "S"
}
],
"KeySchema": [
{
"AttributeName": "PK",
"KeyType": "HASH"
},
{
"AttributeName": "SK",
"KeyType": "RANGE"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5
},
"GlobalSecondaryIndexes": [
{
"IndexName": "GSI1",
"KeySchema": [
{
"AttributeName": "SK",
"KeyType": "HASH"
},
{
"AttributeName": "PK",
"KeyType": "RANGE"
}
],
"Projection": {
"ProjectionType": "ALL"
},
"ProvisionedThroughput": {
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5
}
}
]
}
- TableName
- テーブル名
- AttributeDefinitions
- テーブルとインデックスのキースキーマを記述する属性のリスト
- AttributeType
-
N
は数値、S
は文字列、B
はバイナリ
-
- KeySchema
- "KeyType": "HASH" ... パーティションキー
- "KeyType": "RANGE" ... ソートキー
- ProvisionedThroughput
- RCU
- WCU
- GlobalSecondaryIndex
- グローバルセカンダリーインデックスの定義
- IndexName
- インデックス名
- KeySchema
- インデックスが使用するキーを定義
- GSI は同じデータでもう1つ別のテーブルを作成するようなイメージ
- SK に GSI を設定することは必須でもなんでもないが、設定するケースは多そうかも?
- 例えば、投稿日時(SK)でソートされた投稿を取得するようなクエリが実行可能になったりするらしい
- ProjectionType
- ProvisionedThroughput
- GSI の読み取り・書き込みキャパシティユニットを設定
- キャパシティユニットはテーブル、インデックスごとに設定する必要があるらしい
テーブル作成
aws dynamodb create-table \
--cli-input-json file://table-definition.json \
--endpoint-url http://localhost:8111
作成したテーブルの確認
aws dynamodb list-tables --endpoint-url http://localhost:8111
結果
{
"TableNames": [
"ExampleSns"
]
}
データの新規追加
aws dynamodb put-item \
--table-name ExampleSns \
--item '{
"PK": {"S": "user#123"},
"SK": {"S": "profile"},
"name": {"S": "A-san"},
"email": {"S": "a-san@example.com"},
"joined_at": {"S": "2024-01-01"}
}' \
--endpoint-url http://localhost:8111
バッチ
一度に複数のクエリをまとめて投げることも可能。
ネットワークコールの回数を減らし効率化できる。
バッチを使った更新 batch-write-item
batch-write-item
BatchWriteItem操作は、最大25個のPutItemまたはDeleteItem要求を一度に処理できる。
aws dynamodb batch-write-item \
--request-items '{
"ExampleSns": [
{
"PutRequest": {
"Item": {
"PK": {"S": "user#123"},
"SK": {"S": "post#2024-12-25T10:00"},
"content": {"S": "Merry Christmas!"},
"likes": {"N": "0"},
"comments": {"N": "0"}
}
}
},
{
"PutRequest": {
"Item": {
"PK": {"S": "user#123"},
"SK": {"S": "following#789"},
"followed_user_id": {"S": "user#789"},
"followed_at": {"S": "2024-12-24T09:00"}
}
}
},
{
"PutRequest": {
"Item": {
"PK": {"S": "user#789"},
"SK": {"S": "profile"},
"name": {"S": "B-san"},
"email": {"S": "b-san@example.com"},
"joined_at": {"S": "2023-01-01"}
}
}
}
]
}' \
--endpoint-url http://localhost:8111
結果
{
"UnprocessedItems": {}
}
バッチ処理はトランザクション処理ではないので、1つのクエリだけ失敗する場合もある。
UnprocessedItems
に失敗したデータが入るらしいので、アプリケーション側での制御が必要。
データを読み込む
query コマンド
ここではユーザーの profile 情報を取得する。
aws dynamodb query \
--table-name ExampleSns \
--key-condition-expression "PK = :userId AND SK = :sortKeyValue" \
--expression-attribute-values '{":userId": {"S": "user#123"}, ":sortKeyValue": {"S": "profile"}}' \
--endpoint-url http://localhost:8111
(AttributeName を PK
, SK
にしたせいでちょっとわかりにくくなってしまった...)
-
--table-name
- ExampleSns テーブルが検索対象
-
--key-condition-expression
- 検索条件
-
PK
(パーティションキーにつけている名前)の値が:userId
- かつ
-
SK
(ソートキーにつけている名前)の値がsortKeyValue
-
- 検索条件
-
--expression-attribute-values
- 検索する値
-
:userId
に文字列user#123
を代入 -
:sortKeyValue
に文字列profile
を代入
-
- 検索する値
-
--endpoint-url
- エンドポイント URL
結果
{
"Items": [
{
"SK": {
"S": "profile"
},
"name": {
"S": "A-san"
},
"joined_at": {
"S": "2024-01-01"
},
"PK": {
"S": "user#123"
},
"email": {
"S": "a-san@example.com"
}
}
],
"Count": 1,
"ScannedCount": 1,
"ConsumedCapacity": null
}
scan コマンド
特定のパーティションキーに依存しない全件取得。
取得が遅くなる。RCU の消費が大きくなるため基本的には使用しないのが無難。
aws dynamodb scan \
--table-name ExampleSns \
--endpoint-url http://localhost:8111
結果
{
"Items": [
{
"SK": {
"S": "profile"
},
"name": {
"S": "A-san"
},
"joined_at": {
"S": "2024-01-01"
},
"PK": {
"S": "user#123"
},
"email": {
"S": "a-san@example.com"
}
}
],
"Count": 1,
"ScannedCount": 1,
"ConsumedCapacity": null
}
バッチを使った取得処理 batch-get-item
aws dynamodb batch-get-item \
--request-items '{
"ExampleSns": {
"Keys": [
{"PK": {"S": "user#123"}, "SK": {"S": "post#2024-12-25T10:00"}},
{"PK": {"S": "user#123"}, "SK": {"S": "following#789"}},
{"PK": {"S": "user#789"}, "SK": {"S": "profile"}}
]
}
}' \
--endpoint-url http://localhost:8111
GSIを利用した検索も可能
aws dynamodb query \
--table-name ExampleSns \
--index-name GSI1 \
--key-condition-expression "SK = :sk" \
--expression-attribute-values '{
":sk": {"S": "post#2024-12-25T10:00"}
}' \
--endpoint-url http://localhost:8111
データの更新
UpdateItem
投稿のいいね
を増やす
aws dynamodb update-item \
--table-name ExampleSns \
--key '{"PK": {"S": "user#123"}, "SK": {"S": "post#2024-12-25T10:00"}}' \
--update-expression "SET likes = likes + :inc" \
--expression-attribute-values '{":inc": {"N": "1"}}' \
--endpoint-url http://localhost:8111
- 対象キー (PK, SK): user#123 の投稿 post#2024-12-25T10:00
- 更新内容: likes 属性を +1
- 単一アイテムに対しての簡単な更新操作
トランザクションを利用した更新処理 TransactWriteItems
aws dynamodb transact-write-items \
--transact-items '[
{
"Update": {
"TableName": "ExampleSns",
"Key": {
"PK": {"S": "user#123"},
"SK": {"S": "profile"}
},
"UpdateExpression": "SET email = :email",
"ExpressionAttributeValues": {":email": {"S": "hoge@example.com"}}
}
},
{
"Update": {
"TableName": "ExampleSns",
"Key": {
"PK": {"S": "user#123"},
"SK": {"S": "post#2024-12-25T10:00"}
},
"UpdateExpression": "SET likes = likes + :inc",
"ExpressionAttributeValues": {":inc": {"N": "1"}}
}
}
]' \
--endpoint-url http://localhost:8111
データの削除
aws dynamodb delete-item \
--table-name ExampleSns \
--key '{"PK": {"S": "user#123"}, "SK": {"S": "post#2024-12-25T10:00"}}' \
--endpoint-url http://localhost:8111
条件つきの削除
いいね
が 0 件の場合のみ削除する
aws dynamodb delete-item \
--table-name ExampleSns \
--key '{"PK": {"S": "user#123"}, "SK": {"S": "post#2024-12-25T10:00"}}' \
--condition-expression "likes = :zero" \
--expression-attribute-values '{":zero": {"N": "0"}}' \
--endpoint-url http://localhost:8111