Java 開発 2.0: Amazon の SimpleDB によるクラウド・ストレージ、第 1 回

SimpleDB と Amazon SDK を導入する

Amazon Web サービス・ファミリーの 1 つである Amazon の SimpleDB は、スケーラビリティーと信頼性に極めて優れたキー・バリュー型のデータ・ストアです。SimpleDB は Web インターフェースを介して公開されるため、Java 言語を使ってアクセスすることができます。この連載ではこれから 2 回にわたり、Amazon SimpleDB の独特なスキーマレス・データ・ストレージ手法を探っていきます。1 回目となるこの記事では、SimpleDB を導入し、このデータ・ストアの極めて独特な機能の 1 つ、辞書式検索を具体的な例で説明します。

Andrew Glover, Author and developer, Beacon50

Andrew GloverAndrew Glover は、ビヘイビア駆動開発、継続的インテグレーション、アジャイル・ソフトウェア開発に情熱を持つ開発者であるとともに、著者、講演者、起業家でもあります。また、easyb BDD (Behavior-Driven Development) フレームワークの創始者、そして「継続的インテグレーション入門 開発プロセスを自動化する47の作法」、「Groovy in Action」、「Java Testing Patterns」の 3 冊の本の共著者でもあります。詳細は彼のブログにアクセスしてください。



2010年 6月 15日

この連載について

Java 技術が初めて登場してから現在に至るまでに、Java 開発の様相は劇的に変化しました。成熟したオープンソースのフレームワーク、そしてサービスとして提供される信頼性の高いデプロイメント・インフラストラクチャーを利用できる (借りられる) おかげで、今では Java アプリケーションを短時間かつ低コストでアセンブルし、テスト、実行、保守することが可能になっています。この連載では Andrew Glover が、この新たな Java 開発パラダイムを可能にする多種多様な技術とツールを詳しく探ります。

この連載をとおして、NoSQL と総称されている 非リレーショナル・データ・ストアの数々を話題にしてきました。最近の記事で説明したように、ドキュメント指向のデータ・ストア (CouchDB) はスキーマ指向のリレーショナル・データベースとはかなりの違いがあります。さらに、CouchDB の API は完全に RESTful であり、サポートするクエリー手段も異なります。その手段とは、JavaScript で定義された MapReduce 関数です。これは明らかに、従来の JDBC 世界からの脱却を表します。

Google の Bigtable についても最近取り上げました。このデータ・ソリューションは、リレーショナルでもなければ、ドキュメント指向でもありません (ついでに言うと、どんな形であれ JDBC をサポートしません)。Bigtable はキー・バリュー型のデータ・ストアとして知られています。つまり、スキーマレスであり、保管したいインスタンスが駐車違反切符であろうと、レースのリストであろうと、あるいはある特定のレースに参加するランナーのリストであろうと、基本的に何でも保管できるということです。スキーマがないことから生まれる Bigtable の際立った柔軟性は、迅速な開発をサポートします。

キー・バリュー型データ・ストアの選択肢は Bigtable だけではありません。Amazon にもクラウドをベースとした独自のキー・バリュー型ストア、Amazon SimpleDB があります。Bigtable は Google App Engine による抽象化を通じて Java 開発者に公開される一方、Amazon SimpleDB は Web サービス・インターフェースを介して公開されます。したがって、SimpleDB データ・ストアは Web と HTTP を使用して操作することができます。Amazon Web サービス・インフラストラクチャーをベースとしたバインディングにより、PHP、Ruby、C#、そして Java 言語のなかから好みの言語を選んで SimpleDB を利用できるようになっています。

今月の記事では、Amazon の公式 SDK を使用して SimpleDB を紹介します。以前に使用したレースへの参加アプリケーションの別バージョンを例に、この強力なクラウド・ベースのデータ・ストアが持つ特異な一面、すなわち辞書式検索のデモンストレーションを行います。

SimpleDB の概要

Erlang で作成された SimpleDB は、スケーラビリティーにも可用性にも非常に優れたデータ・ストアです。概念としては Amazon の S3 に似ていて、S3 のオブジェクトはバケットに配置される一方、SimpleDB はアイテムで構成されたドメインとして論理的に定義されます。SimpleDB ではアイテムに属性を含めることもできます。ドメインとは、S3 におけるバケット、あるいはリレーショナルで言うテーブルのようなものです (より適切な表現を使うと、Bigtable で使用されている「kind (種類)」の概念と同様です)。しかし、SimpleDB の概念にリレーショナル性の考え方を投影しないように注意してください。SimpleDB は結局のところ、Bigtable とまったく同じくスキーマレスです。ドメインが多数のアイテム (リレーショナルな世界での行に相当) を持つことも、アイテムが多数の属性 (リレーショナルな世界での列に相当) を持つこともできます。

SimpleDB の「結果整合性」

CAP 定理 (「参考文献」を参照) では、分散システムにおいては、可用性とスケーラビリティーが極めて優れているうえに一貫性が保証されているといった、3 つの品質すべてを同時に満たした状態を実現することは不可能であり、任意の時点で同時に満たせるのは、このうちの 2 つだけであると述べています。そのため、SimpleDB は即時の一貫性をサポートしない、可用性とスケーラビリティーに優れたデータ・ストアを保証しています。SimpleDB がサポートするのは結果整合性ですが、これは皆さんが想像するほど悪い考えではありません。

Amazon で言う結果整合性とは、数秒以内に (ある領域内の) すべてのノードで一貫性が保たれるようになることを意味します。このほんの数秒の間に、2 つの並行プロセスが同じデータの 2 つの異なるインスタンスを (単なる可能性として) 読み取るかもしれない代わりに、際立った信頼性を、しかも極めて手頃な価格で実現できます (同じような信頼性を提供している営利組織に値段を聞いてみるだけで、価格の違いがわかるはずです)。

属性は、実際には単なる名前と値のペア (Bigtable のようだと思いませんか?) であり、「ペア」という点に関しては 1 つの値に制限されません。つまり、属性名を関連する値の集合 (リスト) にできるため、例えば 1 つの単語のアイテムが多重定義された属性の値を持つこともできます。さらに、SimpleDB に保管されたデータはすべて String として表現されます。この点は、無数のデータ型をサポートする Bigtable や、さらには標準の RDBMS とも明らかに異なります。

属性値を単一のデータ型に限定するという SimpleDB の手法は、見方によってはメリットにも、制約にもなります。いずれにしても、この手法がクエリーの実行方法に影響することは確かです (この後、その詳細を説明します)。SimpleDB ではドメイン間の結合という概念もサポートされないため、複数のドメインでアイテムに対してクエリーを実行することはできません。ただしこの制約は、複数の SimpleDB クエリーを実行し、開発者が自ら結合を行うことで克服することができます。

アイテムには、(Bigtable とは異なり) キーそのものがありません。アイテムのキーすなわち固有の ID は、アイテムの名前です。重複する作成リクエストが発行された場合でも、アイテムの属性が変更されたとしたら、SimpleDB はそのアイテムを更新するだけの賢さを持ち合わせています。

他の Amazon Web サービスと同じく、SimpleDB では何もかもが HTTP で公開されるので、SimpleDB とインターフェースを取る手段は無数にあります。Java を使用する場合、インターフェースを取る手段としては Amazon 独自の SDK (この後の例で使用) を使用することも、Topica という名前のよく使われているプロジェクトや本格的な JPA 実装 (第 2 回で取り上げます) を使用することもできます。


クラウド内でのレース

この連載ではこれまでレースと駐車違反切符の例を用いて、さまざまな Java 2.0 技術が持つ機能のデモンストレーションを行ってきました。馴染みのある問題領域を使用すれば、システム間の違いと共通点を理解しやすくなるからです。今回もマラソンの例を引用して、Amazon SimpleDB でランナーとレースを表現する方法を見ていくことにします。

SimpleDB では、例えばレースをドメインとしてモデル化することができます。その場合、レースのインスタンスは SimpleDB 内のアイテムとなり、レースの名前と日付を (値を持つ) 属性として表現することになります。ここで重要な点は、レースの名前は属性であり、アイテム自体の名前ではないことです。アイテムのインスタンスに指定する名前は、アイテムのキーとなります。この例でのアイテムの場合、キーはマラソン・レースの名前にすることができます。あるいは、レースのインスタンスをある特定の時点に制限する代わりに (レースは毎年のイベントであることが多いため)、アイテムに (タイムスタンプのような) 固有の名前を指定することもできます。そうすれば、例えば年に 2 回開催されるレースのそれぞれを SimpleDB に保管できるようになります。

同様に、runner もドメインとなります。その場合には個々のランナーがアイテムとなり、ランナーの名前と年齢が属性になります。レースの場合と同じく、runner アイテムのインスタンスごとに固有の名前が必要です (例えば、Pete Smith とMarty Howard を区別するため)。Bigtable とは異なり、SimpleDB では各アイテムにどのような名前を付けるかは重要ではなく、実際、キー・ジェネレーターも提供していません。おそらくこの例の場合、タイムスタンプを使用するか、あるいは runner_1runner_2 といった具合にランナーごとのカウンターをインクリメントしていくだけで十分です。

スキーマがないことから、個々のアイテムの属性はそれぞれに異なっていてもまったく構いません。それと同じく、ドメインにどのアイテムを保管するかも自由です。それでも、この可変性は制限しなければなりません。可変性はデータの編成を無秩序にしがちです。そして組織化されていないデータは、簡単に検索、または管理することができません。私からの助言として言っておきますが、秩序もスキーマもなく、整理されていないデータは大失敗につながります。

Amazon SDK での作業開始

Amazon では最近、すべての Amazon Web サービス (SimpleDBを含む) を操作するためのコードが含まれるライブラリーを標準化しました。このライブラリーは他のほとんどのライブラリーと同様に、Amazon Web サービスにアクセスしてサービスを利用するために必要となる基本的な通信を抽象化し、クライアントが Amazon Web サービスをネイティブに操作することを可能にします。例えば、SimpleDB を対象とした Amazon の Java ライブラリーでは、ドメインとアイテムの作成およびクエリー、そしてもちろんドメインとアイテムの更新、ストレージからの削除といった操作を、いずれの操作も実は HTTP でクラウドに送られているということにまったく気付かずに実行することができます。

リスト 1 に、ごく単純な Java コードを使って定義された AmazonSimpleDBClient と、Races ドメインを記載します (この演習をご自分のワークステーションで行うには、Amazon のアカウントを作成する必要があります)。

リスト 1. AmazonSimpleDBClient のインスタンスの作成
AmazonSimpleDB sdb = new AmazonSimpleDBClient(new PropertiesCredentials(
                new File("etc/AwsCredentials.properties")));
String domain = "Races";
sdb.createDomain(new CreateDomainRequest(domain));

Amazon SDK の Request オブジェクトのパターンは、すべての SimpleDB アクティビティーに引き継がれることに注意してください。この例での場合、CreateDomainRequest を作成すると、ドメインが作成されます。アイテムを追加する場合には、クライアントの batchPutAttributes メソッドを使用することができます。このメソッドは基本的に、リスト 2 に記載するようなアイテムの List を引数に取ります。

リスト 2. Race_01
List<ReplaceableItem> data = new ArrayList<ReplaceableItem>();

data.add(new ReplaceableItem().withName("Race_01").withAttributes(
   new ReplaceableAttribute().withName("Name").withValue("Charlottesville Marathon"),
   new ReplaceableAttribute().withName("Distance").withValue("26.2")));

Amazon SDKでは、ItemReplaceableItem 型として表現されます。各インスタンスに名前 (つまり、キー) を指定した後、(ReplaceableAttribute 型の) 属性を追加することができます。リスト 2 で作成したレースは、「Race_01」という単純なキーを持つマラソンです。このインスタンスを Races ドメインに追加するには、BatchPutAttributesRequset を作成して AmazonSimpleDBClient に送信します (リスト 3 を参照)。

リスト 3. SimpleDB 内のアイテムの作成
sdb.batchPutAttributes(new BatchPutAttributesRequest(domain, data));

SimpleDB でのクエリー

これでレースは保存できたので、この後はもちろん、SimpleDB の問い合わせ言語を使ってレースを検索することができます。この問い合わせ言語は SQL とよく似ていますが、ここには落とし穴があります。私が前に、すべての属性値は String として保管されると言ったことを覚えていますか?これはデータの比較が辞書式順序で行われることを意味するため、データを検索するとなると、この順序が影響してきます。

数値を基準にしたクエリーを実行すると、SimpleDB は実際の整数値ではなく、文字を基準にして検索を行います。目下、SimpleDB に保管しているレースのインスタンスは 1 つしかありません。このインスタンスは、SQL のような SimpleDB の文を使って簡単に検索することができます (リスト 4 を参照)。

リスト 4. Race_01 の検索
String qry = "select * from `" + domain + "` where Name = 'Charlottesville Marathon'";
SelectRequest selectRequest = new SelectRequest(qry);
for (Item item : sdb.select(selectRequest).getItems()) {
 System.out.println("Race Name: " + item.getName());
}

リスト 4 のクエリーは通常の SQL のように見えます。この例では単純に、Race のインスタンスのうち、Name が「Charlottesville Marathon」と等しいすべてのインスタンスを要求しているだけです。SelectRequestAmazonSimpleDBClient に送信すると、結果としてアイテムの集合が返されます。したがって、これらのアイテムを繰り返し処理して、それぞれの名前を出力することができます。この例の場合、返されるアイテムは 1 つだけです。

一方、今度は距離の属性が異なる別のレースを追加すると、どのような結果になるでしょうか (リスト 5 を参照)。

リスト 5. 距離の短いレース
List<ReplaceableItem> data2 = new ArrayList<ReplaceableItem>();

data2.add(new ReplaceableItem().withName("Race_02").withAttributes(
   new ReplaceableAttribute().withName("Name").withValue("Charlottesville 1/2 Marathon"),
   new ReplaceableAttribute().withName("Distance").withValue("13.1")));

sdb.batchPutAttributes(new BatchPutAttributesRequest(domain, data2));

距離の異なる 2 つのレースがあるとしたら、当然、距離を基準に検索するのが道理です (リスト 6 を参照)。

リスト 6. 距離を基準にした検索
String disQry = "select * from `" + domain + "` where Distance > '13.1'";
SelectRequest selectRequest = new SelectRequest(disQry);
for (Item item : sdb.select(selectRequest).getItems()) {
 System.out.println("Race Name: " + item.getName());
}

案の上、上記の検索で返されるレースは Race_01 です。26.2 は 13.1 より大きいので、数学的にも、さらに辞書式順序という点からも、この結果は誤っていません。けれども、かなり長距離のレースをさらに追加すると結果がどうなるかを見てください。

リスト 7. Leesburg Ultra Marathon
List<ReplaceableItem> data3 = new ArrayList<ReplaceableItem>();

data3.add(new ReplaceableItem().withName("Race_03").withAttributes(
   new ReplaceableAttribute().withName("Name").withValue("Leesburg Ultra Marathon"),
   new ReplaceableAttribute().withName("Distance").withValue("103.1")));

sdb.batchPutAttributes(new BatchPutAttributesRequest(domain, data3));

リスト 7 で追加したのは、距離が 103.1 のレースです。リスト 6 のクエリーをもう一度実行すると、結果がどうなるかを想像してみてください。ご想像のとおり、辞書式順序からすると 103.1 は 13.1 より小さく、13.1 より大きくはありません。これがまさに、(自宅でこの演習に従っている場合) Leesburg Ultra Marathon が出力に記載されない理由です。

今度は、距離の短いレースを検索するクエリーを実行した場合に、どのような結果になるかを見てみましょう (リスト 8 を参照)。

リスト 8. 出力結果を見てください
String disQry = "select * from `" + domain + "` where Distance < '13.1'";
SelectRequest selectRequest = new SelectRequest(disQry);
for (Item item : sdb.select(selectRequest).getItems()) {
 System.out.println("Race Name: " + item.getName());
}

見たままの内容をそのまま信じる人の目には、リスト 8 のクエリーの実行結果は意外なものとして映ることでしょう。けれども検索が辞書式順序で行われていることを知っていれば、これはもっともな結果です。とは言え、距離の短いレースを検索しているとしたら、この (架空の) Leesburg Ultra Marathon は興味のないレースであることも確かです。


辞書式検索

辞書式検索は、数値のデータ (日付を含め) を検索する場合には問題の原因になりかねませんが、その解決方法がないわけではありません。距離を基準とした検索の問題を解決する 1 つの方法は、距離の属性に使用する数値にパディング処理を施すことです。

現在のところ、最も距離の長いレースは (個人的に走ったことのある距離には程遠いマイル数ですが) 103.1 マイルです。辞書式順序では、この数値は小数点左側の 3 桁で判断されます。そこで、他のレースの数値の前にゼロを追加し、すべてのレースが同じ文字数になるようにすれば、距離を基準とした検索でも上手く機能するというわけです。

図 1 は、SDB Tool を使用した場合のスクリーン・ショットです。この Firefox プラグインを使用すると、SimpleDB データベース・ドメインに対し、視覚的にクエリーや更新を行うことができます (「参考文献」を参照)。

図 1. 距離値のパディング処理
SDB Tool に表示されたレースと距離値のスクリーン・ショット

ご覧のように、Race_01Race_02 両方の距離の値にゼロを追加しました。経験がないとあまりよくわからないかもしれませんが、このパディング処理によって、かなり簡単に検索できるようになります。このようにして 020.0 マイル (あるいは単に 20 マイル) より短い距離のレースを検索すると (図 2 を参照)、最終的に検索結果が (適切に) 表示されます。

図 2. 問題が解決されたパディング処理後の検索
SDB Tool に表示された、正常なクエリー実行結果のスクリーン・ショット

多少の先見の明があれば、辞書式検索の制限要素と思われるような問題でも難なく解決することができます。パディングは得意でないというのであれば、アプリケーション側でフィルタリングするという方法もあります。この方法では、整数を通常の整数のままにし、Amazon からフィルタリングされていないアイテムの集合を受け取った時点でフィルタリングします。つまり、すべてのアイテムに対して select * を実行するということです。ただし、大量のデータがある場合、この方法にはコストがかかります。


SimpleDB における関係

関係を SimpleDB で設定するのは難しいことではありません。概念的には、ランナー・アイテムに race という属性を作成して、そこにレースの名前 (Race_01 など) を含めるという簡単な方法を使うことができます。さらに都合のよいことに、この値にレースの名前の集合を保持してはいけない理由は何もありません。その逆も然りで、ランナーの名前の集合を race ドメインに保持するのは簡単です (リスト 9 を参照)。ただし、Amazon の問い合わせ言語を使って 2 つのドメインを実際に結合することはできません。そのため、このセットアップは開発者自らが行う必要があります。

リスト 9. Runners ドメインと 2 人のランナーの作成
sdb.createDomain(new CreateDomainRequest("Runners"));

List<ReplaceableItem> runners = new ArrayList<ReplaceableItem>();

runners.add(new ReplaceableItem().withName("Runner_01").withAttributes(
   new ReplaceableAttribute().withName("Name").withValue("Sally Smith")));

runners.add(new ReplaceableItem().withName("Runner_02").withAttributes(
	new ReplaceableAttribute().withName("Name").withValue("Richard Bean")));

sdb.batchPutAttributes(new BatchPutAttributesRequest("Runners", runners));

Runners ドメインを作成して、そこにランナーを追加した後は、既存のレースを更新すれば、これらのランナーをレースに追加することができます (リスト 10 を参照)。

リスト 10. レースの更新による 2 人のランナーの追加
races.add(new ReplaceableItem().withName("Race_01").withAttributes(
  new ReplaceableAttribute().withName("Name").withValue("Charlottesville Marathon"),
  new ReplaceableAttribute().withName("Distance").withValue("026.2"),
  new ReplaceableAttribute().withName("Runners").withValue("Runner_01"),
  new ReplaceableAttribute().withName("Runners").withValue("Runner_02")));

要するに、関係を設定することは可能ですが、関係は SimpleDB の外部で管理しなければならないということです。例えば Race_01 に参加するすべてのランナーのフルネームを取得するには、1 つのクエリーですべてのランナーの名前を取得してから、runner ドメインに対して複数のクエリーを実行しないと (この例の場合、Race_01 には 2 つの属性値しかないため、実行するクエリーは 2 つです)、フルネームを得られません。


クリーンアップ操作

自分で後片付けをすることは重要です。したがって、この記事は Amazon SDK を使って簡単なクリーンアップを行うことで締めくくります。クリーンアップ操作は、データを作成して、そのデータに対するクエリーを実行する操作とそれ程変わりません。つまり、Request 型を作成し、削除リクエストを発行するだけでよいのです。

リスト 11 に示すように、Race_01 はごく簡単に削除することができます。

リスト 11. SimpleDB での削除リクエストの発行
sdb.deleteAttributes(new DeleteAttributesRequest(domain, "Race_01"));

アイテムの削除に DeleteAttributesRequest を使用するのであれば、ドメインの削除には何が必要だと思いますか?ご想像のとおり、DeleteDomainRequest です!

リスト 12. SimpleDB でのドメインの削除
sdb.deleteDomain(new DeleteDomainRequest(domain));

クラウドのツアーはまだ終わっていません!

Amazon の SimpleDB によるクラウドのツアーはまだ終わったわけではありませんが、このツアーでの Amazon SDK の役目は終わりました。Amazon SDK は機能的で、ある程度は役立つものの、レースやランナーのようなものをモデル化する場合には、例えば JPA を利用するとよいかもしれません。そこで、来月は SimpleDB に JPA を組み合わせた場合を調べます。次回の記事でお会いするまで、辞書式検索を楽しんでください!

参考文献

学ぶために

  • Java 開発 2.0」: この developerWorks 連載では Java 開発全体を定義し直している技術とツールを探っています。これまで、CouchDB (2009年11月)、Bigtable (2010年5月) などを取り上げました。
  • Amazon Web サービスを利用したクラウド・コンピューティング: 第 5 回 SimpleDB によるクラウド内でのデータセット処理」(Prabhakar Chaganti 著、developerWorks、2009年10月): Amazon SimpleDB の概念を学び、SimpleDB とのインターフェースに使われるオープンソースの Python ライブラリー boto に用意された関数について調べてください。
  • Eventually Consistent - Revisited」(Werner Vogels 著、All Things Distributed、2008年12月): Amazon の CTO が、Eric Brewer の CAP 定理と、その Amazon Web サービス・インフラストラクチャーへの影響について説明しています。
  • NoSQL Patterns」(Ricky Ho 著、Pragmatic Programming Techniques、2009年11月): NoSQL データベースの概要と具体的な例を紹介した後、NoSQL データ・ストアの共通アーキテクチャーを詳細に検討しています。
  • Bigtable: A Distributed Storage System for Structured Data」(Fay Chang 他による共著、Google、2006年11年): Bigtable は構造化データを管理するための分散ストレージ・システムです。極めて大規模にスケーリングされるように設計された Bigtable は、何千もの商品サーバーにまたがる数ペタバイトのデータに対応できます。
  • Technology bookstore で、この記事で取り上げた技術やその他の技術に関する本を探してください。
  • developerWorks Java technology ゾーン: Java プログラミングのあらゆる側面を網羅した記事が豊富に用意されています。

製品や技術を入手するために

  • Amazon SDK: Amazon の SDK をダウンロードして、Amazon Web サービス・インフラストラクチャーを利用し始めてください (Amazon のアカウントをまだ持っていない場合は作成する必要があります)。
  • SDB Tool: Amazon SimpleDB を簡単に使えるようにする Firefox GUI プラグインです。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology, Cloud computing
ArticleID=499996
ArticleTitle=Java 開発 2.0: Amazon の SimpleDB によるクラウド・ストレージ、第 1 回
publish-date=06152010