##DynamoDBとは
Amazon DynamoDBは、フルマネージド型のNoSQLデータベースサービスで、
高速で予測可能なパフォーマンスとシームレスなスケーラビリティを特長としています。
大切なことは全部公式ドキュメントが言ってるので、
簡単にまとめると管理不要のNoSQLデータベースサービスのこと。
##そもそもNoSQLって?
リレーショナルモデルの代わりに、キーと値のペアやドキュメントストレージなど、データ管理のための代替モデルを使用します。
RDB(リレーショナルデータベース)はデータを保持するテーブル間に関係性があり、
それぞれのトランザクションがデータの一貫性(ACID)を保つように設計されています。
具体的には物理的なレコードのロック(他のトランザクションからの取得・更新などを一時的に待機させる)やロールバック・コミットなどのトランザクション制御指示などによりACIDが実現されます。
しかし、実行速度や拡張性に難が有ります。
実行速度や拡張性に関わる要件が、需要を増して生まれたのがNoSQLです。
NoSQLには様々な要件ごとにソフトが開発され、それはRDBでできた機能を実現したりしなかったり、それまでになかった機能を実現したりと、
多岐にわたるバリエーションのソフトがあります。(当たり前ですが、全てのNoSQLサービスが同一の機能を有してません)
ユースケースとしては、堅実な一貫性を取るならRDB、実行速度や拡張性(フェーズによるデータ増加や、ACIDを犠牲にしたデータ保持)を重視するならNoSQLと考えれば良いでしょう。
##DynamoDBの特徴
キー・バリュー形式の単純なデータを表形式で保持し、高速で拡張性(スケールインなど)の高いDBです。
メインの構成は、データを保持するテーブル・テーブル内に定義された項目・項目に定義する属性でできており、RDBと似たテーブル構造になります。
※RDBの構造化データに対し、DynamoDBは半構造化データのテーブルを作成できます。
そのため、主キーやインデックス以外はテーブル作成時に定義不要です
しかし、検索に制限が多く、基本的には後述するプライマリキー(主キー)を使用した検索になると考えてください。
###DynamoDB用語解説
####「プライマリキー」
テーブルのレコードを一意に識別するもので、テーブル作成時に以下の2種類から選択する。
・パーティションキー
→1つのキーで主キーとなる場合に採用
・パーティションキーとソートキー(別名:複合プライマリキー)
→2つのキーで主キーとなる場合に採用
※ソートキーに指定した項目は範囲検索が可能になる。
####「セカンダリインデックス」
プライマリキー以外での検索に対応したい場合に設定する。
以下の2種類から選択する。
・グローバルセカンダリーインデックス(GSI)
→プライマリキーとは異なるキーで検索したい場合に採用
※設定したソートキーをパーティションキーにするようなことも可能
・ローカルセカンダリインデックス(LSI)
パーティションキーとLSI(任意の項目)の組み合わせで検索したい場合に採用
※複合プライマリキー設定時のみ設定可能
※テーブル作成時にのみ設定可能
##データ取得パターン
以下の3パターンが存在する
・getitem(主キー検索)
・query(複合プライマリキー設定時の範囲検索・セカンダリインデックスでの検索)
・scan(全レコード取得)
##Pythonからの検索
PythonからDynamoDBにアクセスする際は、AWS SDK for Python (Boto 3)を使用する。
以下のサンプルはlambdaを使用したサンプル例
###準備
#boto3のimport
import boto3
#lambdaのためのハンドラ(今回の解説対象外)
def lambda_handler(event, context):
#boto3からDynamoDBアクセスのためのオブジェクト取得
dynamodb = boto3.resource('dynamodb')
#事前に作成したテーブル(今回は"sample1")へのアクセスオブジェクト取得
table = dynamodb.Table('sample1')
###getitem(主キー検索)
複合プライマリキー設定時の例
ちなみに、ソートキーを検索パラメータに設定しなかった場合、エラーになる。
import boto3
def lambda_handler(event, context):
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('sample1')
#getItemメソッドの呼び出し(主キー検索)
response = table.get_item(
#パラメーターとして主キー情報(辞書型)を渡す
#Keyという変数名?は固定(違う名前だとエラーになる)
Key={
#主キー情報を設定
#今回はテーブルにid(プライマリーキー)・sex(ソートキー)を定義した
'id': '001',
'sex': 'man'
}
)
#responseの正体は、Itemなどのキーが定義された辞書型オブジェクト
print(response)
#結果の取得
item = response['Item']
#辞書型オブジェクトとして取得できる(テーブルのカラムが定義されている)
#キーに一致するものがない場合、エラーとなる
print(item)
###query
import boto3
#Keyオブジェクトを使用できるようにする(これがないとエラーになる)
#謎エラーで詰まったら、importの有無を確認した方が良い
from boto3.dynamodb.conditions import Key
def lambda_handler(event, context):
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('sample1')
#複合プライマリキー設定時、プライマリーキーのみの検索
#以下の記載でプライマリーキー以外を指定した場合はエラーとなる
response = table.query(
KeyConditionExpression=Key('id').eq('001')
)
#getItem同様、辞書型の情報が取得される
print(response)
#取得件数も取れる
print(response['Count'])
#取得レコードは"item"ではなく"items"!
items = response['Items']
#複数件取れるインターフェイスのためlistオブジェクトが格納されている
#取得結果なしの場合は、0件のlistが取得される
print(type(items))
#その他検索パターン
#ソートキーの範囲検索
#条件が複数ある場合は、"&"or"|"を使用する
response = table.query(
KeyConditionExpression=Key('id').eq('001') & Key('sex').begins_with('m')
)
#LSIを使用した検索(基本的なやり方は複合プライマリキー時の検索と同じ)
#LSIはでの検索は1件に絞れる保証がないため、getItemは使用不可
response = table.query(
#設定したLSIのインデックスNameを指定する
IndexName='id-name-index',
KeyConditionExpression=Key('id').eq('001') & Key('name').eq('mama')
)
#GSIを使用した検索(基本的なやり方はLSIの検索と同じ)
response = table.query(
#設定したGSIのインデックスNameを指定する
IndexName='location-index',
KeyConditionExpression=Key('location').eq('Tokyo')
)
###Scan(全件検索)
import boto3
def lambda_handler(event, context):
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('sample1')
#シンプルにscanメソッドの呼び出しをする
response = table.scan()
#getItem同様、辞書型の情報が取得される
print(response)
#取得件数も取れる
print(response['Count'])
#取得レコードは"item"ではなく"items"!
items = response['Items']
#複数件取れるインターフェイスのためlistオブジェクトが格納されている
#取得結果なしの場合は、0件のlistが取得される
print(type(items))
また、scanでの全件取得結果をもとにフィルターをかけ、該当データのみ抽出することも可能です。
※キー設定でどうしようもなくなった場合の最後の手段としましょう。
##まとめ
テーブルのキー設定のバリエーション
・プライマリキー × GSI
・複合プライマリキー
・複合プライマリキー × LSI
・複合プライマリキー × GSI
・複合プライマリキー × LSI × GSI
それぞれのキー設定で行える検索パターン
キー設定 | scan | getItem | query |
---|---|---|---|
プライマリキー | ○ | ○ | × |
複合プライマリキー | ○ | ○ | ○ (PK(必須)+SK(任意・範囲指定化)) |
LSI | - | - | ○(複合プライマリキーのquery検索パターンのソートキーを切り替えた検索) |
GSI | - | - | ○(任意の項目を用いた検索) |
上記を意識して、適切なキー設定を行えるとRDB脳からの切り替えがうまくいくと思います。