データベース
Amazon DynamoDB
概要
Amazon DynamoDBは、AWSが提供するフルマネージド型のNoSQLデータベースサービスです。どんな規模のデータでも保存・取得でき、あらゆるレベルのリクエストトラフィックに対応し、サーバーレスで一桁ミリ秒のパフォーマンスを提供します。
詳細
DynamoDBは2012年にAmazonによって発表され、Amazon.comの内部で使用されていたDynamoデータベースをベースに開発されました。完全マネージド型のサービスとして、サーバーの管理、パッチ適用、ソフトウェアのアップグレード、レプリケーション、バックアップを自動化し、開発者がアプリケーション開発に集中できるよう設計されています。
DynamoDBの主な特徴:
- サーバーレス・フルマネージド
- 一桁ミリ秒のレスポンス時間
- 自動スケーリング
- グローバルテーブル(マルチリージョン)
- ACID トランザクション
- 細かい粒度のアクセス制御
- DynamoDB Accelerator (DAX) によるインメモリキャッシュ
- DynamoDB Streams によるデータ変更の監視
- 暗号化(保存時・転送時)
- ポイントインタイム復旧
メリット・デメリット
メリット
- サーバーレス: サーバー管理が不要で運用負荷が軽減
- 高性能: 一桁ミリ秒の低レイテンシ
- 自動スケーリング: トラフィックに応じて自動的にスケール
- 高可用性: 99.999%のSLA保証
- セキュリティ: 暗号化やIAM統合による包括的なセキュリティ
- AWS統合: 他のAWSサービスとの密な連携
- コスト効率: 使用した分だけの従量課金
デメリット
- ベンダーロックイン: AWS に依存する設計
- SQL制限: SQLクエリが使用できない
- データモデル制約: NoSQLの制約による設計の複雑さ
- コスト: 大規模利用時の費用が高額になる場合
- 学習コスト: DynamoDB特有の概念と制限の習得が必要
主要リンク
書き方の例
セットアップ・設定
# AWS CLI インストール・設定
aws configure
# DynamoDB ローカル(開発用)
docker run -p 8000:8000 amazon/dynamodb-local
# AWS SDK インストール(Node.js)
npm install aws-sdk
# AWS SDK インストール(Python)
pip install boto3
基本操作(テーブル管理)
const AWS = require('aws-sdk')
// DynamoDB クライアント設定
const dynamodb = new AWS.DynamoDB.DocumentClient({
region: 'ap-northeast-1'
})
// テーブル作成
const createTableParams = {
TableName: 'Users',
KeySchema: [
{ AttributeName: 'userId', KeyType: 'HASH' }, // パーティションキー
{ AttributeName: 'timestamp', KeyType: 'RANGE' } // ソートキー
],
AttributeDefinitions: [
{ AttributeName: 'userId', AttributeType: 'S' },
{ AttributeName: 'timestamp', AttributeType: 'N' }
],
BillingMode: 'PAY_PER_REQUEST' // オンデマンド
}
// テーブル一覧取得
const tables = await dynamodb.listTables().promise()
console.log(tables.TableNames)
基本操作(CRUD)
// アイテム作成(Create)
const putParams = {
TableName: 'Users',
Item: {
userId: 'user123',
timestamp: Date.now(),
name: '田中太郎',
email: '[email protected]',
age: 30,
address: {
city: '東京',
prefecture: '東京都'
},
tags: ['engineer', 'javascript']
}
}
await dynamodb.put(putParams).promise()
// アイテム読み取り(Read)
const getParams = {
TableName: 'Users',
Key: {
userId: 'user123',
timestamp: 1640995200000
}
}
const result = await dynamodb.get(getParams).promise()
console.log(result.Item)
// アイテム更新(Update)
const updateParams = {
TableName: 'Users',
Key: {
userId: 'user123',
timestamp: 1640995200000
},
UpdateExpression: 'SET age = :age, email = :email',
ExpressionAttributeValues: {
':age': 31,
':email': '[email protected]'
},
ReturnValues: 'UPDATED_NEW'
}
await dynamodb.update(updateParams).promise()
// アイテム削除(Delete)
const deleteParams = {
TableName: 'Users',
Key: {
userId: 'user123',
timestamp: 1640995200000
}
}
await dynamodb.delete(deleteParams).promise()
クエリとスキャン
// クエリ(パーティションキーで検索)
const queryParams = {
TableName: 'Users',
KeyConditionExpression: 'userId = :userId',
ExpressionAttributeValues: {
':userId': 'user123'
},
ScanIndexForward: false, // 降順
Limit: 10
}
const queryResult = await dynamodb.query(queryParams).promise()
console.log(queryResult.Items)
// スキャン(全テーブル検索)
const scanParams = {
TableName: 'Users',
FilterExpression: 'age > :age',
ExpressionAttributeValues: {
':age': 25
}
}
const scanResult = await dynamodb.scan(scanParams).promise()
console.log(scanResult.Items)
// バッチ読み取り
const batchGetParams = {
RequestItems: {
'Users': {
Keys: [
{ userId: 'user123', timestamp: 1640995200000 },
{ userId: 'user456', timestamp: 1640995300000 }
]
}
}
}
const batchResult = await dynamodb.batchGet(batchGetParams).promise()
インデックス・最適化
// グローバルセカンダリインデックス(GSI)作成
const updateTableParams = {
TableName: 'Users',
GlobalSecondaryIndexUpdates: [
{
Create: {
IndexName: 'EmailIndex',
KeySchema: [
{ AttributeName: 'email', KeyType: 'HASH' }
],
Projection: { ProjectionType: 'ALL' },
BillingMode: 'PAY_PER_REQUEST'
}
}
],
AttributeDefinitions: [
{ AttributeName: 'email', AttributeType: 'S' }
]
}
// GSI を使ったクエリ
const gsiQueryParams = {
TableName: 'Users',
IndexName: 'EmailIndex',
KeyConditionExpression: 'email = :email',
ExpressionAttributeValues: {
':email': '[email protected]'
}
}
const gsiResult = await dynamodb.query(gsiQueryParams).promise()
実用例
// トランザクション
const transactParams = {
TransactItems: [
{
Put: {
TableName: 'Orders',
Item: {
orderId: 'order123',
userId: 'user123',
amount: 1000,
status: 'created'
}
}
},
{
Update: {
TableName: 'Users',
Key: { userId: 'user123' },
UpdateExpression: 'ADD totalOrders :inc',
ExpressionAttributeValues: { ':inc': 1 }
}
}
]
}
await dynamodb.transactWrite(transactParams).promise()
// 条件付き書き込み
const conditionalPutParams = {
TableName: 'Users',
Item: {
userId: 'user789',
name: '佐藤花子',
email: '[email protected]'
},
ConditionExpression: 'attribute_not_exists(userId)'
}
// DynamoDB Streams の設定
const enableStreamsParams = {
TableName: 'Users',
StreamSpecification: {
StreamEnabled: true,
StreamViewType: 'NEW_AND_OLD_IMAGES'
}
}
ベストプラクティス
// バッチ書き込み
const batchWriteParams = {
RequestItems: {
'Users': [
{
PutRequest: {
Item: { userId: 'user001', name: 'ユーザー1' }
}
},
{
DeleteRequest: {
Key: { userId: 'user002' }
}
}
]
}
}
await dynamodb.batchWrite(batchWriteParams).promise()
// ページネーション
async function scanAllItems(tableName) {
let items = []
let lastEvaluatedKey = null
do {
const params = {
TableName: tableName,
...(lastEvaluatedKey && { ExclusiveStartKey: lastEvaluatedKey })
}
const result = await dynamodb.scan(params).promise()
items = items.concat(result.Items)
lastEvaluatedKey = result.LastEvaluatedKey
} while (lastEvaluatedKey)
return items
}
// エラーハンドリング
try {
await dynamodb.put(putParams).promise()
} catch (error) {
if (error.code === 'ConditionalCheckFailedException') {
console.log('条件チェックエラー')
} else if (error.code === 'ProvisionedThroughputExceededException') {
console.log('スループット制限エラー')
}
}
AWS CLI での操作
# テーブル作成
aws dynamodb create-table \
--table-name Users \
--attribute-definitions \
AttributeName=userId,AttributeType=S \
--key-schema \
AttributeName=userId,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
# アイテム追加
aws dynamodb put-item \
--table-name Users \
--item '{"userId": {"S": "user123"}, "name": {"S": "田中太郎"}}'
# アイテム取得
aws dynamodb get-item \
--table-name Users \
--key '{"userId": {"S": "user123"}}'
# テーブル一覧
aws dynamodb list-tables