Amazon S3で複数のオブジェクトをまとめて消せるようになりました

オブジェクトをまとめて消すことができます

Amazon S3において、オブジェクトの削除はひとつずつ命令する必要がありました。これは、HTTPのオーバーヘッドが発生するため、効率的ではありませんでした。今度の機能改善によって、オブジェクトの削除命令をまとめて1回で行うことができます。ちなみに、バージョン管理されたオブジェクトに対しても行う事ができます。

1つずつオブジェクトを消す場合

速度比較を行うために1つずつオブジェクトを消してみましょう。実験のために、AWS SDK for Java(1.2.15)とEclipseプラグインであるAWS Toolkitを用いました。以下の例では連続して10個のオブジェクトを消した場合に掛かった時間をカウントして表示しています。

import java.io.*;
import java.util.*;

import com.amazonaws.*;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.s3.*;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.services.s3.model.DeleteObjectsRequest.KeyVersion;

public class S3SingleDeleteSample {

	public static void main(String[] args) throws IOException {
		AmazonS3 s3 = new AmazonS3Client(
				new PropertiesCredentials(S3SingleDeleteSample.class
						.getResourceAsStream("AwsCredentials.properties")));

		String bucketName = "my-first-s3-bucket-" + UUID.randomUUID();
		String keyName1 = "MyObjectKey" + UUID.randomUUID();
		String keyName2 = "MyObjectKey" + UUID.randomUUID();
		String keyName3 = "MyObjectKey" + UUID.randomUUID();
		String keyName4 = "MyObjectKey" + UUID.randomUUID();
		String keyName5 = "MyObjectKey" + UUID.randomUUID();
		String keyName6 = "MyObjectKey" + UUID.randomUUID();
		String keyName7 = "MyObjectKey" + UUID.randomUUID();
		String keyName8 = "MyObjectKey" + UUID.randomUUID();
		String keyName9 = "MyObjectKey" + UUID.randomUUID();
		String keyName10 = "MyObjectKey" + UUID.randomUUID();

		try {
			System.out.println("Creating bucket " + bucketName + "\n");
			s3.createBucket(bucketName);

			s3.putObject(new PutObjectRequest(bucketName, keyName1,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName2,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName3,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName4,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName5,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName6,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName7,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName8,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName9,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName10,
					createSampleFile()));

			long startSingle = System.currentTimeMillis();

			s3.deleteObject(bucketName, keyName1);
			s3.deleteObject(bucketName, keyName2);
			s3.deleteObject(bucketName, keyName3);
			s3.deleteObject(bucketName, keyName4);
			s3.deleteObject(bucketName, keyName5);
			s3.deleteObject(bucketName, keyName6);
			s3.deleteObject(bucketName, keyName7);
			s3.deleteObject(bucketName, keyName8);
			s3.deleteObject(bucketName, keyName9);
			s3.deleteObject(bucketName, keyName10);

			long endSingle = System.currentTimeMillis();
			System.out.format("Single Delete : %d ms\n",
					(endSingle - startSingle));

		} catch (AmazonClientException ace) {
			System.out.println("Error Message: " + ace.getMessage());
		}
	}

	private static File createSampleFile() throws IOException {
		File file = File.createTempFile("aws-java-sdk-", ".txt");
		file.deleteOnExit();

		Writer writer = new OutputStreamWriter(new FileOutputStream(file));
		writer.write("abcdefghijklmnopqrstuvwxyz\n");
		writer.close();

		return file;
	}

}

実行結果は以下です。

Single Delete : 2420 ms

まとめてオブジェクトを消す場合

続きまして、まとめてオブジェクトを消す場合のサンプルです。以下の例では、10個のオブジェクトをまとめて消した場合に掛かった時間をカウントしています。

import java.io.*;
import java.util.*;

import com.amazonaws.*;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.s3.*;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.services.s3.model.DeleteObjectsRequest.KeyVersion;

public class S3MultiDeleteSample {

	public static void main(String[] args) throws IOException {
		AmazonS3 s3 = new AmazonS3Client(new PropertiesCredentials(
				S3MultiDeleteSample.class
						.getResourceAsStream("AwsCredentials.properties")));

		String bucketName = "my-first-s3-bucket-" + UUID.randomUUID();
		String keyName1 = "MyObjectKey" + UUID.randomUUID();
		String keyName2 = "MyObjectKey" + UUID.randomUUID();
		String keyName3 = "MyObjectKey" + UUID.randomUUID();
		String keyName4 = "MyObjectKey" + UUID.randomUUID();
		String keyName5 = "MyObjectKey" + UUID.randomUUID();
		String keyName6 = "MyObjectKey" + UUID.randomUUID();
		String keyName7 = "MyObjectKey" + UUID.randomUUID();
		String keyName8 = "MyObjectKey" + UUID.randomUUID();
		String keyName9 = "MyObjectKey" + UUID.randomUUID();
		String keyName10 = "MyObjectKey" + UUID.randomUUID();

		try {
			System.out.println("Creating bucket " + bucketName + "\n");
			s3.createBucket(bucketName);

			s3.putObject(new PutObjectRequest(bucketName, keyName1,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName2,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName3,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName4,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName5,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName6,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName7,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName8,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName9,
					createSampleFile()));
			s3.putObject(new PutObjectRequest(bucketName, keyName10,
					createSampleFile()));

			long startMultiple = System.currentTimeMillis();

			DeleteObjectsRequest multiObjectDeleteRequest = new DeleteObjectsRequest(
					bucketName);

			List<KeyVersion> keys = new ArrayList<KeyVersion>();
			keys.add(new KeyVersion(keyName1));
			keys.add(new KeyVersion(keyName2));
			keys.add(new KeyVersion(keyName3));
			keys.add(new KeyVersion(keyName4));
			keys.add(new KeyVersion(keyName5));
			keys.add(new KeyVersion(keyName6));
			keys.add(new KeyVersion(keyName7));
			keys.add(new KeyVersion(keyName8));
			keys.add(new KeyVersion(keyName9));
			keys.add(new KeyVersion(keyName10));

			multiObjectDeleteRequest.setKeys(keys);

			s3.deleteObjects(multiObjectDeleteRequest);

			long endMultiple = System.currentTimeMillis();
			System.out.format("Multi Delete : %d ms\n",
					(endMultiple - startMultiple));

		} catch (AmazonClientException ace) {
			System.out.println("Error Message: " + ace.getMessage());
		}
	}

	private static File createSampleFile() throws IOException {
		File file = File.createTempFile("aws-java-sdk-", ".txt");
		file.deleteOnExit();

		Writer writer = new OutputStreamWriter(new FileOutputStream(file));
		writer.write("abcdefghijklmnopqrstuvwxyz\n");
		writer.close();

		return file;
	}

}

実行結果は以下です。

Multi Delete : 373 ms

予想通り、APIをコールするためのHTTP回数が10分の1になっていますので早くなっていますね。

まとめ

まとめて削除できるようになったことで、今後はアップロードのボトルネックが気になり始めましたね。実際に10個の小さなファイルをアップロードするだけで2秒近く掛かっていました。これは、Javaのプログラムがアップロードを順次処理で実行しているからですね。これをスレッド化すれば早くなりそうです。また、とても大きなファイルをアップロードする際には、Multipartアップロードという機能を使って、細切れにしたファイルをスレッド化して同時にアップロードすることで解決できそうです。次回は、スレッド化してどの程度早くなるか実験してみたいと思います。Amazon S3のまとめて削除をマスターしてAPIマエストロになろう!

参考資料

Deleting Multiple Objects Using the AWS SDK for Java