DynamoDBを初めて触ってハマったこと
AWSのDynamoDBを初めて触ったときの感想というか、つまづいた点を記録しておきます。
その1
CloudFormationでDynamoDBのテーブルを作成しようとしたら
The number of attributes in key schema must match the number of attributesdefined in attribute definitions.
というエラーになりました。 AttributeDefinitions
にカラムを全部書くものと思い込んでいたので、すべてのカラムをキーに指定しないといけない、、、、え?そんななんてわけはないよね、となり混乱しました。
AttributeDefinitions
に書くのはパーティションキーとソートキーのみでよく、ほかは書いてはいけないというのが正解でした。テーブル作成時ではなく、レコード挿入時に自由にカラムを増やせます。
その2
その1と関連しますが、
CloudFormationでDyanomoDBのテーブルを作成しようと、データ型の指定方法を調べると、文字列と数字とバイナリの3種類しかありません。
AWS::DynamoDB::Table AttributeDefinition - AWS CloudFormation
CloudFormationから作成するときはこの3種類のデータ型しか使えないのかと混乱しました。しかし、そんなことはありません。これはプライマリキーとソートキーに使えるデータ型がこの3種類というだけです。プライマリキーとソートキー以外のカラムはCloudFormationに書かなくてよいので、レコード挿入時に自由なデータ型を使えばよいようです。
サポートされているデータの種類 - Amazon DynamoDB
その3
CloudFormationから作成するときに BillingMode
と ProvisionedThroughput
という2つの項目があります。どちらも必須ではないのですが、 BillingMode
を省略すると ProvisionedThroughput
が必須に変わります。したがって少なくともどちらかを書かないといけません。
BillingMode
は以下の2種類から選択できます。
- オンデマンド(従量課金)
PAY_PER_REQUEST
- プロビジョニング
PROVISIONED
(デフォルト)
CloudFormationから作成するときに BillingMode
を省略すると、 PROVISIONED
とみなされます。 PROVISIONED
の場合は ProvisionedThroughput
が必須に変わるのです。
開発環境で自分しか触らないのであればオンデマンドのほうが安上がりのようです。
BillingMode
は必須ではないけど常に書くことにしておいたほうがよさそうです。
CloudFormationのテンプレートのサンプル
AWSTemplateFormatVersion: '2010-09-09' Resources: SampleTable: Type: AWS::DynamoDB::Table Properties: TableName: sample AttributeDefinitions: - AttributeName: UserId AttributeType: S - AttributeName: TransactionId AttributeType: S KeySchema: - AttributeName: UserId KeyType: HASH # パーティションキー - AttributeName: TransactionId KeyType: RANGE # ソートキー TimeToLiveSpecification: AttributeName: Expiration Enabled: true BillingMode: PAY_PER_REQUEST
DynamoDBにPythonからアクセスするサンプルコード
Pythonのboto3を使ったサンプルコードです。
session = boto3.session.Session() dynamodb_resource = session.resource("dynamodb") table = dynamodb_resource.Table("sampletable") # 全レコード取得 # 全レコードがrecordsに入る records = [] response = table.scan() while True: for item in response["Items"]: records.append(item) if not "LastEvaluatedKey" in response: break key = response["LastEvaluatedKey"] response = table.scan(ExclusiveStartKey = key) # 1レコード取得 response = table.get_item( Key = { "UserId": userId, "TransactionId": transId, }, ) if not "Item" in response: "Not Found!" record = response["Item"] # レコード挿入または上書き table.put_item( Item = { "UserId": userId, "TransactionId": transId, "Foo": foo, "Bar": bar, }, )