Amazon DynamoDBがBatchWriteItemに対応しました

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

BatchWriteItem

Amazon DynamoDBが複数アイテム同時処理に対応しました。1つのアイテムを追加/削除したいならば、PutItem/DeleteItemを用いれば事足りるのですが、例えばElasticMapReduceによって大量のデータをDynamoDBにアップロードする等の場合に効果を発揮します。クライアント側でJavaを使っているならばスレッド処理によって並行的に操作を行う事ができますが、コードが複雑になり管理の面とリソース使用の面で劣ります。その他の言語を用いる時はスレッド処理自体に対応していないかもしれません。ということで、BatchWriteItemを使う事によって多くの恩恵を得る事が出来ます。以下に特徴を示します。

  • 1回のリクエストにまとめて追加/削除/置換を指示するため、スループットが向上する。
  • サーバー側で同時に処理を行ってくれるため、クライアント側でスレッド処理を組む必要がない。
  • PutItem/DeleteItemに比べて、追加/削除/置換の平均遅延が低い。

ただし、BatchWriteItemには以下の制約があります。

  • 追加/削除/置換処理を1回に25個まで同時に処理できる。サイズ制限は全体で1MB。
  • アイテムの追加/削除で用いることができるが、既存のアイテムの更新に用いることはできない。
  • BatchWriteItem内で命令される個々の操作はアトミックであるが、BatchWriteItem自体はアトミックではない。要するに追加/削除など個々の操作の一部が失敗したとしても全体が取り消されることはない。もし、ネットワーク遅延やスループット制限により一部の操作が失敗した場合には、UnprocessedItemsとして戻り値が返されるので再処理などをすれば良い。
  • アイテムを返さない。BatchWriteItemは大量のデータを効率的にアップロードするための仕組みである。DeleteItemはReturnValueを返すが、BatchWriteItemはPutItemやDeleteItemのように洗練されたAPIを持っていない。
  • PutItemやDeleteItemとは異なり、BatchWriteItemは個々の操作内で書き込み時条件を指定することはできません。

さらに、以下のような条件に1つでも当てはまる場合、DynamoDBはBatchWriteItem命令を拒否します。

  • BatchWriteItem内で指定されたテーブルが存在しないとき。
  • 指定された主キーが、対応するテーブルの主キーのスキーマに合致しないとき。
  • 同じアイテムに対して追加や削除を行うとき。
  • リクエストの合計サイズが1MBを超えているとき。

ということでサンプルプログラムを作って動かしてみましょう。

AWS SDK for Javaを使ってDynamoDBを操作する

AWS SDK for Javaを使ってDynamoDBへBatchWriteItem命令を行って動作を確認します。既にmy-favorite-movies-tableという名前のテーブルを作成済みということで進めます。

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.dynamodb.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodb.model.AttributeValue;
import com.amazonaws.services.dynamodb.model.BatchWriteItemRequest;
import com.amazonaws.services.dynamodb.model.PutRequest;
import com.amazonaws.services.dynamodb.model.WriteRequest;

public class AmazonDynamoDBSample {
    static AmazonDynamoDBClient dynamoDB;
    
    private static void init() throws Exception {
        AWSCredentials credentials = new PropertiesCredentials(
                AmazonDynamoDBSample.class.getResourceAsStream("AwsCredentials.properties"));

        dynamoDB = new AmazonDynamoDBClient(credentials);
    }

    public static void main(String[] args) throws Exception {
        init();

        try {
            String tableName = "my-favorite-movies-table";
            
            //追加操作1
            PutRequest putRequest1 = new PutRequest();
            Map<String, AttributeValue> item1 = newItem("Satoshi & Ted's Excellent Adventure", 1989, "****", "James", "Sara");
            putRequest1.setItem(item1);
            WriteRequest write1 = new WriteRequest();
            write1.setPutRequest(putRequest1);
            
            //追加操作2
            PutRequest putRequest2 = new PutRequest();            
            Map<String, AttributeValue> item2 = newItem("Airplane", 1980, "*****", "James", "Billy Bob");
            putRequest2.setItem(item2);            
            WriteRequest write2 = new WriteRequest();
            write2.setPutRequest(putRequest2);

            //リスト化
            List<WriteRequest> list = new ArrayList<WriteRequest>();
            list.add(write1);
            list.add(write2);
            
            //テーブル指定
            Map<String, List<WriteRequest>> requestItems = new HashMap<String, List<WriteRequest>>();
            requestItems.put(tableName, list);
            
            //バッチ操作追加
            BatchWriteItemRequest batchWriteItemRequest = new BatchWriteItemRequest();
            batchWriteItemRequest.setRequestItems(requestItems);
			
            //バッチ処理実行
            dynamoDB.batchWriteItem(batchWriteItemRequest);

        } catch (AmazonServiceException ase) {
            System.out.println(ase);
        } catch (AmazonClientException ace) {
            System.out.println(ace);
        }
    }

    private static Map<String, AttributeValue> newItem(String name, int year, String rating, String... fans) {
        Map<String, AttributeValue> item = new HashMap<String, AttributeValue>();
        item.put("name", new AttributeValue(name));
        item.put("year", new AttributeValue().withN(Integer.toString(year)));
        item.put("rating", new AttributeValue(rating));
        item.put("fans", new AttributeValue().withSS(fans));

        return item;
    }

}

まとめ

BatchWriteItemの利用によって、1回の処理で複数の操作を行うことができることが分かりました。繰り返しリクエスト・レスポンスする処理が無くなり、アプリケーションの大幅な速度向上に繋がると思います。これらを活用してパフォーマンスアップとコストダウンを実現しましょう。今日から君もダイナモンバッチをゲットだぜ!

参考資料

Amazon DynamoDB Announces BatchWriteItem Feature