AWS を操作するための Python ライブラリ boto3 を使って DynamoDB へデータを書き込む方法について調べていたところ、どうやらその方法が 3 パターンあるようです。今回は、それらについてのメモです。

目次

概要

boto3 を使って DynamoDB のデータを読み書きする方法については前にも書きましたが、今回は書き込みに絞って調べてみた内容です。

ちなみにこれを書いている時点 (201909) 時点での boto3 のバージョンは 1.9.236 です。

boto3 を使った DynamoDB への書き込み方法

前の記事 ではざっくりと put_item() と書いてましたが、その put_item() にも 2 パターンあります。さらにその put_item() をバッチ処理的に実行できる batch_writer() というメソッドがあるので、全部で 3 パターンあるということになります。
具体的には以下のメソッドです。

  • DynamoDB.Client.put_item()
  • DynamoDB.Table.put_item()
  • DynamoDB.Table.batch_writer()

それぞれどういう使い方をするのか見ていきます。 ​

DynamoDB.Client.put_item()

これは 前の記事 でやった方法で、実装としては次のようになります。

table_name = 'hinatazaka-blog'
item = {
  "Author": {"S": "富田鈴花"},
  "Authorcode": {"S": "15"},
  "Blogkey": {"N": "30775"},
  "Entrydate": {"N": "201909062231"},
  "Images": {"L": [
      {"S":"https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/201909/mobzVcYf5.jpg"},
      {"S": "https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/201909/mobbmdhGD.jpg"},
      {"S": "https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/201909/mobNBUyrd.jpg"}
    ]
  },
  "Title": {"S": "東北自動車道"},
  "Url": {"S": "https://www.hinatazaka46.com/s/official/diary/detail/30775?ima=0000&cd=member"}
}

dynamo = boto3.client('dynamodb')
dynamo.put_item(TableName=table_name, Item=item)

TableName : stringItem : dict が必須のパラメータとなります。ちょっとややこしいのが、 Item で指定する dict の value は更に dict になっていて、そこでデータの型を指定する必要があります。 S とか L とかがそれです。
この方法だと Item として指定する dict の構成がちょっと繁雑に見えます。

また、任意のパラメータである ConditionExpression に色々と条件を指定することで、同一キー項目の上書きを防止したり、条件を満たす場合のみ更新したりすることができます。これはちょっと奥が深そうなので、別で書きたいと思います。

DynamoDB.Table.put_item()

これは DynamoDB.Client.put_item() よりもシンプルというか、直感的な使い方になります。

table_name = 'hinatazaka-blog'
item = {
  "Author": "富田鈴花",
  "Authorcode": "15",
  "Blogkey": 30775,
  "Entrydate": 201909062231,
  "Images": [
    "https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/201909/mobzVcYf5.jpg",
    "https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/201909/mobbmdhGD.jpg",
    "https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/201909/mobNBUyrd.jpg"
  ],
  "Title": "東北自動車道",
  "Url": "https://www.hinatazaka46.com/s/official/diary/detail/30775?ima=0000&cd=member"
}

dynamo = boto3.resource('dynamodb')
dynamo_table = dynamo.Table(table_name)
dynamo_table.put_item(Item=item)

必須パラメータは Item: dict のみで、指定する dict も内部で型の指定をする必要がありません。

また、DynamoDB.Client.put_item() と同様に任意のパラメータである ConditionExpression に色々と条件を指定することで、同一キー項目の上書きを防止したり、条件を満たす場合のみ更新したりすることができます。

DynamoDB.Table.batch_writer()

まずは使い方を見てみます。

table_name = 'hinatazaka-blog'
items = [
  {
    "Author": "富田鈴花",
    "Authorcode": "15",
    "Blogkey": 30775,
    "Entrydate": 201909062231,
    "Images": [
      "https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/201909/mobzVcYf5.jpg",
      "https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/201909/mobbmdhGD.jpg",
      "https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/201909/mobNBUyrd.jpg"
    ],
    "Title": "東北自動車道",
    "Url": "https://www.hinatazaka46.com/s/official/diary/detail/30775?ima=0000&cd=member"
  },
  {
    "Author": "富田鈴花",
    "Authorcode": "15",
    "Blogkey": 30820,
    "Entrydate": 201909100044,
    "Images": [
      "https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/201909/mob12dNLt.jpg",
      "https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/201909/mobLZTiXa.jpg"
    ],
    "Title": "甘口と中辛のハイブリット、甘中辛",
    "Url": "https://www.hinatazaka46.com/s/official/diary/detail/30820?ima=0000&cd=member"
  },
  {
    "Author": "富田鈴花",
    "Authorcode": "15",
    "Blogkey": 30855,
    "Entrydate": 201909130035,
    "Images": [
      "https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/201909/mobSSIX7d.jpg"
    ],
    "Title": "現国よりも数学が",
    "Url": "https://www.hinatazaka46.com/s/official/diary/detail/30855?ima=0000&cd=member"
  }
]

dynamo = boto3.resource('dynamodb')
dynamo_table = dynamo.Table(table_name)
with dynamo_table.batch_writer() as batch:
    for item in items:
        batch.put_item(Item=item)

こんな感じで batch_writer() の中で put_item() を呼ぶことで、一度に複数のデータを書き込むことができます。
普通に DynamoDB.Table.put_item() を複数回実行するのと何が違うのかというと、 batch_writer() を使った場合はよしなに再送とかをやってくれるんです。

The batch writer will automatically handle buffering and sending items in batches. In addition, the batch writer will also automatically handle any unprocessed items and resend them as needed. All you need to do is call put_item for any items you want to add, and delete_item for any items you want to delete.

また、ドキュメントにもあるように、 delete_item() も同時に batch writer に登録できるので、登録と削除を一括で実行することができます。

DynamoDB.Client と DynamoDB.Table の違い

ここまでで 3 パターンをみてきましたが、実質的には DynamoDB.Client.put_item()DynamoDB.Table.put_item() の 2 パターンという味方ができます。
どちらもオプションで詳細な書き込み条件を指定することができるという特徴がありますが、必須パラメータが違ったり Item で指定する dict の構成が違ったりします。じゃあこれどっちを使えばいいのかっていう疑問が出てきます。

あらためてそれぞれの put_item() を使う実装方法をみてみると、 DynamoDB.Client.put_item() ではまず DynamoDB の Client オブジェクトを生成しています。

dynamo = boto3.client('dynamodb')

DynamoDB.Table.put_item() では、 DynamoDB の ServiceResource オブジェクトを生成して、そこから Table オブジェクトを生成しています。

dynamo = boto3.resource('dynamodb')
dynamo_table = dynamo.Table(table_name)

つまり DynamoDB.Client.put_item() のほうが、より低レベルな層からデータの書き込みを実行していることになります。実際、 DynamoDB.Client には create_table()describe_endpoints() といった、 DynamoDB 全体にかかるメソッドが用意されています。 一方で DynamoDB.Table には、 delete()scan() といった、特定のテーブルに対する操作を行うメソッドしかありません。

なので使い分けとしては、基本的には DynamoDB.Table.put_item() のほうを使えばいいのかなという感じです。 put_item() のたびにテーブル名を指定する必要もないですし、書き込むデータの dict も自然なので扱いやすいです。

一方で、一つのプロセス内で複数のテーブルに対して処理を実行するような場合には DynamoDB.Client.put_item() を使うことでオブジェクトの生成回数を減らすことができます。結果的にこれがパフォーマンスの向上に繋がるかもしれませんが、そこまでシビアな実装をすることってあるのかな?という気もします。

まとめ

Python (boto3) を使って DynamoDB にデータを書き込む方法について調べてみた話でした。
同じ名前のメソッドでも必須パラメータが違ったり指定するデータの構成が違ったりするので、初めて boto3 でデータ書き込みしようと思ったときは戸惑いました。今回書いた 3 パターンについては、その違いがわかったかと思います。

途中でも書いていますが、 put_item() に指定できるオプションパラメータについては またあらためて調べて試してみます 下記の記事で書いたので、よかったら参考にしてみてください。


以上、よっしー (michimani) でした。

Share to ...

Follow on Feedly

comments powered by Disqus