dynamodb について
Amazon DynamoDBを扱う開発者向けの実用的なリファレンススキルです。データモデリング、AWS CLIとboto3の操作、性能・コスト管理、トラブルシューティングを扱います。
主な機能
- 01キーとインデックスの設計
- 02テーブルと項目の操作
- 03クエリと一括処理のパターン
- 04容量とコストの指針
- 05スロットリングの調査
活用シーン
- 01パーティションキーとソートキーの設計
- 02CLIでのテーブル作成や更新
- 03遅いクエリやスロットリングの診断
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ itsmostafa/ aws-agent-skills --skill dynamodbインストール後、Claude Code で/dynamodbを入力して読み込まれたか確認します。
同梱ファイルquery-patterns.md
詳細
AWS DynamoDB
Amazon DynamoDB は、あらゆる規模で高速かつ予測可能なパフォーマンスを提供する、フルマネージド型の NoSQL データベースサービスです。キーバリュー型とドキュメント型のデータ構造をサポートしています。
目次
コアコンセプト
キー
| キーの種類 | 説明 |
|---|---|
| パーティションキー(PK) | 必須です。データの分散方法を決定します |
| ソートキー(SK) | 任意です。パーティション内で範囲クエリを実行できます |
| 複合キー | PK と SK の組み合わせ |
セカンダリインデックス
| インデックスの種類 | 説明 |
|---|---|
| GSI(グローバルセカンダリインデックス) | PK/SK が異なり、スループットは個別に設定され、結果整合性があります |
| LSI(ローカルセカンダリインデックス) | PK は同じで SK は異なり、テーブルのスループットを共有し、強い整合性も選択できます |
キャパシティモード
| モード | 用途 |
|---|---|
| オンデマンド | トラフィックが予測できない場合。リクエストごとの従量課金です |
| プロビジョンド | トラフィックが予測できる場合。コストを抑えられ、自動スケーリングを利用できます |
一般的なパターン
テーブルの作成
AWS CLI:
aws dynamodb create-table \
--table-name Users \
--attribute-definitions \
AttributeName=PK,AttributeType=S \
AttributeName=SK,AttributeType=S \
--key-schema \
AttributeName=PK,KeyType=HASH \
AttributeName=SK,KeyType=RANGE \
--billing-mode PAY_PER_REQUEST
boto3:
import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.create_table(
TableName='Users',
KeySchema=[
{'AttributeName': 'PK', 'KeyType': 'HASH'},
{'AttributeName': 'SK', 'KeyType': 'RANGE'}
],
AttributeDefinitions=[
{'AttributeName': 'PK', 'AttributeType': 'S'},
{'AttributeName': 'SK', 'AttributeType': 'S'}
],
BillingMode='PAY_PER_REQUEST'
)
table.wait_until_exists()
基本的な CRUD 操作
import boto3
from boto3.dynamodb.conditions import Key, Attr
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Users')
# Put item
table.put_item(
Item={
'PK': 'USER#123',
'SK': 'PROFILE',
'name': 'John Doe',
'email': '[email protected]',
'created_at': '2024-01-15T10:30:00Z'
}
)
# Get item
response = table.get_item(
Key={'PK': 'USER#123', 'SK': 'PROFILE'}
)
item = response.get('Item')
# Update item
table.update_item(
Key={'PK': 'USER#123', 'SK': 'PROFILE'},
UpdateExpression='SET #name = :name, updated_at = :updated',
ExpressionAttributeNames={'#name': 'name'},
ExpressionAttributeValues={
':name': 'John Smith',
':updated': '2024-01-16T10:30:00Z'
}
)
# Delete item
table.delete_item(
Key={'PK': 'USER#123', 'SK': 'PROFILE'}
)
クエリ操作
# Query by partition key
response = table.query(
KeyConditionExpression=Key('PK').eq('USER#123')
)
# Query with sort key condition
response = table.query(
KeyConditionExpression=Key('PK').eq('USER#123') & Key('SK').begins_with('ORDER#')
)
# Query with filter
response = table.query(
KeyConditionExpression=Key('PK').eq('USER#123'),
FilterExpression=Attr('status').eq('active')
)
# Query with projection
response = table.query(
KeyConditionExpression=Key('PK').eq('USER#123'),
ProjectionExpression='PK, SK, #name, email',
ExpressionAttributeNames={'#name': 'name'}
)
# Paginated query
paginator = dynamodb.meta.client.get_paginator('query')
for page in paginator.paginate(
TableName='Users',
KeyConditionExpression='PK = :pk',
ExpressionAttributeValues={':pk': {'S': 'USER#123'}}
):
for item in page['Items']:
print(item)
バッチ操作
# Batch write (up to 25 items)
with table.batch_writer() as batch:
for i in range(100):
batch.put_item(Item={
'PK': f'USER#{i}',
'SK': 'PROFILE',
'name': f'User {i}'
})
# Batch get (up to 100 items)
dynamodb = boto3.resource('dynamodb')
response = dynamodb.batch_get_item(
RequestItems={
'Users': {
'Keys': [
{'PK': 'USER#1', 'SK': 'PROFILE'},
{'PK': 'USER#2', 'SK': 'PROFILE'}
]
}
}
)
GSI の作成
aws dynamodb update-table \
--table-name Users \
--attribute-definitions AttributeName=email,AttributeType=S \
--global-secondary-index-updates '[
{
"Create": {
"IndexName": "email-index",
"KeySchema": [{"AttributeName": "email", "KeyType": "HASH"}],
"Projection": {"ProjectionType": "ALL"}
}
}
]'
条件付き書き込み
from botocore.exceptions import ClientError
# Only put if item doesn't exist
try:
table.put_item(
Item={'PK': 'USER#123', 'SK': 'PROFILE', 'name': 'John'},
ConditionExpression='attribute_not_exists(PK)'
)
except ClientError as e:
if e.response['Error']['Code'] == 'ConditionalCheckFailedException':
print("Item already exists")
# Optimistic locking with version
table.update_item(
Key={'PK': 'USER#123', 'SK': 'PROFILE'},
UpdateExpression='SET #name = :name, version = version + :inc',
ConditionExpression='version = :current_version',
ExpressionAttributeNames={'#name': 'name'},
ExpressionAttributeValues={
':name': 'New Name',
':inc': 1,
':current_version': 5
}
)
CLI リファレンス
テーブル操作
| コマンド | 説明 |
|---|---|
aws dynamodb create-table | テーブルを作成 |
aws dynamodb describe-table | テーブル情報を取得 |
aws dynamodb update-table | テーブル/インデックスを変更 |
aws dynamodb delete-table | テーブルを削除 |
aws dynamodb list-tables | すべてのテーブルを一覧表示 |
アイテム操作
| コマンド | 説明 |
|---|---|
aws dynamodb put-item | アイテムを作成/置換 |
aws dynamodb get-item | 単一のアイテムを読み取り |
aws dynamodb update-item | アイテムの属性を更新 |
aws dynamodb delete-item | アイテムを削除 |
aws dynamodb query | キーでクエリ |
aws dynamodb scan | テーブル全体をスキャン |
バッチ操作
| コマンド | 説明 |
|---|---|
aws dynamodb batch-write-item | バッチ書き込み(最大 25 件) |
aws dynamodb batch-get-item | バッチ読み取り(最大 100 件) |
aws dynamodb transact-write-items | トランザクション書き込み |
aws dynamodb transact-get-items | トランザクション読み取り |
ベストプラクティス
データモデリング
- アクセスパターンに合わせて設計する — 設計の前にクエリを把握します
- 複合キーを使用する — PK はグループ化に、SK は並べ替え/フィルタリングに使用します
- スキャンよりクエリを優先する — スキャンはコストがかかります
- スパースインデックスを使用する — インデックス属性を持つアイテムのみがインデックス化されます
- 関連するエンティティにはシングルテーブル設計を検討します
パフォーマンス
- パーティションキーを均等に分散する — ホットパーティションを避けます
- バッチ操作を使用する — API 呼び出しを減らします
- 読み取り中心のワークロードでは DAX を有効にする
- プロジェクションを使用する — データ転送量を減らします
コストの最適化
- ワークロードの変動が大きい場合はオンデマンドを使用する
- ワークロードが予測可能な場合はプロビジョンドと自動スケーリングを使用する
- 有効期限があるデータには TTL を設定する
- アクセス頻度の低いデータは S3 にアーカイブする
トラブルシューティング
スロットリング
症状: ProvisionedThroughputExceededException
原因:
- ホットパーティション(キーの分散が不均等)
- バーストトラフィックがキャパシティを超過
- GSI のスロットリングがベーステーブルに影響
解決策:
# Use exponential backoff
import time
from botocore.config import Config
config = Config(
retries={
'max_attempts': 10,
'mode': 'adaptive'
}
)
dynamodb = boto3.resource('dynamodb', config=config)
ホットパーティション
デバッグ:
# Check consumed capacity by partition
aws cloudwatch get-metric-statistics \
--namespace AWS/DynamoDB \
--metric-name ConsumedReadCapacityUnits \
--dimensions Name=TableName,Value=Users \
--start-time $(date -d '1 hour ago' -u +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 60 \
--statistics Sum
解決策:
- パーティションキーにランダム性を加える
- 書き込みシャーディングを使用する
- パーティション間でアクセスを分散する
クエリでアイテムが返されない
デバッグチェックリスト:
- キーの値が完全に一致していることを確認します(大文字と小文字を区別します)
- キーの型(S、N、B)を確認します
- テーブル/インデックス名を確認します
- フィルター式を確認します(読み取り後に適用されます)
スキャンのパフォーマンス
問題: スキャンが遅く、コストがかかります
解決策:
- 大きなテーブルには並列スキャンを使用します
- アクセスパターンに合わせて GSI を作成します
- フィルター式を使用して、返されるデータを減らします
# Parallel scan
import concurrent.futures
def scan_segment(segment, total_segments):
return table.scan(
Segment=segment,
TotalSegments=total_segments
)
with concurrent.futures.ThreadPoolExecutor() as executor:
results = list(executor.map(
lambda s: scan_segment(s, 4),
range(4)
))
参考資料
コメント
Markdown 対応