2011年6月17日金曜日

Amazon SimpleDBってなんじゃ?(その1 Scratchpad編)

いまさらですが、さいきんAWSで遊んでます。AmazonWebServiceというAmazonが貸し出すインフラをAPIを使って構築できるダイナミックなサービスです。

その中でSimpleDBというサービスがあります。普段ぼくらが使っているMySQLやOracleなどのリレーショナルデータベースとはちがって、結合や複雑なトランザクションがなく、単体の表がたくさんあるような、エクセルに似たデータの持ち方をしています。

つまり複雑さを捨てた代わりに、スピードや拡張性を追求しているということができます。

SimpleDBはエクセルやRDBMSに該当する以下の概念があります。
  • ドメイン:エクセルではシート、DBでいうテーブル
  • アイテム:エクセルでは行、DBではレコード
  • アイテムネーム:プライマリキー
  • 属性:エクセルでは列、DBではカラム
そのほかいろいろ制限やクセはあるようですが、とりあえず遊んでみました。


既にAWSのアカウントを保持していることが前提ですが、
SimpleDBのページで利用申し込みをします。




 申し込むと登録完了メールが届いて、すぐつかえるようになります。

 普通はプログラムから使用するんですが、その前にまず簡単なクライアントツールが用意されているので、それで触ってみます。

Javascript Scratchpad for Amazon SimpleDBというツールをダウンロードして、解凍した中にあるindex.htmlをブラウザで開きます。

ヘッダにアクセスキーとシークレットアクセスキーを入力して、右側のリストから仕様したいAPIを選びます。



ドメイン(テーブル)の作成

まずは表(ドメイン)を作りたいので、"CreateDomain"を選択します。これはDBでいうところのcreate tableに該当します。

Domain Name(テーブル名)を入力し、"Display Signed URL"を押すと、APIリクエストのURLが表示され、"Invoke Request"を押すと実際にそのURLで別窓をターゲットにドメイン生成のAPIアクセスが行われ、XMLのレスポンスが返ります。
ここではDomain Nameに"member"と入力しました。

 すると、
<CreateDomainResponse>
  <ResponseMetadata>
    <RequestId>813d1434-dc52-b4ae-2616-0f3e57dbfcec</RequestId>
    <BoxUsage>0.0055590278</BoxUsage>
  </ResponseMetadata>
</CreateDomainResponse>
というレスポンスが返ってきました。

まだ慣れていないので、このレスポンスで正常なのかわかりません。
ドメインの一覧を見て"member"ドメインが作成されたか調べてみましょう。



ドメイン(テーブル)の一覧

今度はヘッダ右のリストから"ListDomains"というAPIを選択します。これは現在登録中のドメインの一覧を取得するものです。mysql でいうところのshow tablesでしょうか。


ここで、2つの入力項目"Max Number Of Domains" と "Next Tokens"があります。
"Max Number Of Domains"はそのまま取得したい最大件数の制限をかけるものです。
"Next Token"に関しては後ほどアイテムの一覧で説明します。

ここではMax Number Of Domainsに10と入れて"Invode Request"を押します。
すると以下のようなXMLレスポンスが返ります。
<ListDomainsResponse>
  <ListDomainsResult>
    <DomainName>member</DomainName>
  </ListDomainsResult>
  <ResponseMetadata>
    <RequestId>1af6eec5-5ab5-2b84-c4f1-7a731f874dab</RequestId>
    <BoxUsage>0.0000071759</BoxUsage>
  </ResponseMetadata>
</ListDomainsResponse>
ここで、さっきのCreateDomainのレスポンスとの違いを見ると、レスポンスの意味がわかってきました。ListDomainsResultという要素がDomainNameという要素を1つ含みDomainNameはmemberというテキストを持っています。これが実際に知りたかったドメイン一覧の部分で、残りのResponseMetadataの部分は、それ以外のメタデータのようです。つまりさっきのCreateDomainは返却値のあるクエリではなく更新のクエリだったため、メタデータのみが返ってきたということがわかります。この感じはDBのSELECTとDDLの関係にとても似ています。



アイテム(行)の追加

 "member"というドメインが正常に作成されたようなので、次は行(アイテム)を追加していきましょう。APIリストから"PutAttribute"を選びます。


 上部にDomain NameとItem Nameという項目があります。memberドメインに行(アイテム)を追加するので、"member"と入力します。Item Nameですが、これはmemberドメインのプライマリキーにあたるものです。member内で一意ならばなんでもいいので、メールアドレスなどでもいいですが、ここではIDっぽく1と入れておきます。

そして、以下Attributeというブロックがあり、Name、Value、Replaceという項目があり、右上の+と-のマークがあります。ここで属性(カラム)を好きなだけ追加します。
ここでは、nameとcountryの属性を設定します。

まず最初のAttributeのボックスにname属性を追加します。
Name:name
Value:memorycraft
Replace:
そして、右上の+ボタンを押し、次のAttributeボックスにcountry属性を追加します。
Name:country
Value:Japan
Replace:
このように属性名とその値をセットで登録して、"Invoke Request"を押します。するとCreateDomainのときと同じようにResponseMetadataだけのレスポンスが返ってきます。更新系のクエリのためです。

このようにして以下のように3アイテム追加しました。

ItemName, name, country
1, memorycraft, Japan
2, David, USA
3, Pierre, France

これらが正常に登録されているか見てみましょう。



アイテムの検索

さきほどはドメインの一覧をListDomainsで調べました。これは、create table したものをshow tablesで調べるようなものです。今度は行を調べるのでselect文にあたるクエリを使います。
SimpleDBには、SQLと似たSelect構文があり、それを使います。

APIリストからSelectを選びます。

 ここには
"Select Expression"、"Next Token"、"Consistent Read"という項目があります。
ここでは"Select Expression"に単純に"select * from member"と入力して結果を見てみます。
<SelectResponse>
  <SelectResult>
    <Item>
      <Name>1</Name>
      <Attribute><Name>name</Name><Value>memorycraft</Value></Attribute>
      <Attribute><Name>country</Name><Value>Japan</Value></Attribute>
    </Item>
    <Item>
      <Name>2</Name>
      <Attribute><Name>name</Name><Value>David</Value></Attribute>
      <Attribute><Name>country</Name><Value>USA</Value></Attribute>
     </Item>
     <Item>
       <Name>3</Name>
       <Attribute><Name>name</Name><Value>Pierre</Value></Attribute>
       <Attribute><Name>country</Name><Value>France</Value></Attribute>
      </Item>
    </SelectResult>
  <ResponseMetadata>
    <RequestId>694e492f-feec-e860-258e-29086b644585</RequestId>
    <BoxUsage>0.0000411449</BoxUsage>
  </ResponseMetadata>
</SelectResponse>
このように3件登録されているのがわかります。

ここで、"NextToken"とはなんでしょうか。
調べてみると、次の結果セットへのポインタのようなものだとわかりました。
SimpleDBには、スピードや冗長性を保つために他のものを犠牲にしており、たとえば1度のクエリ実行によりフェッチできる件数は最大2500件までです。
そのため、SimpleDBでは結果の一部だけを取得した場合、APIレスポンスの中にそれに続く結果セットへのポインタとしてNextTokenという文字列を含めて返却します。

たとえば、この場合select * from member limit 2というクエリを発行した場合、レスポンスには2件分の結果セットのほかに以下のような要素が入っています。
<NextToken>rO0ABXNyACdjb20uYW1hem9uLnNkcy5RdWVyeVByb2Nlc3Nvci5Nb3JlVG9rZW7racXLnINNqwMA
C0kAFGluaXRpYWxDb2ddGVudExTTnQAEkxqYXZhL2xhbmcvU3Ry
aW5nO0wAEmxhc3RBdHRyaWJ1dGVWYWx1ZXEAfgABTAAJc29ydE9yZGVydAAvTGNvbS9hbWF6b24v
c2RzL1F1ZXJ5UHJvY2Vzc29yL1F1ZXJ5JFNvcnRPcmRlcjt4cAAAAAAAAAAAAAAAAAIAAAAAAAAA
AAAAAAAAAAAAAABwcHB4</NextToken>
そして、次の結果セットを取得する場合は、このNextTokenをセットして同じクエリで、再度クエリ実行します。

すると、残りの1件が以下のように取得することができます。
<SelectResponse>
  <SelectResult>
    <Item>
      <Name>3</Name>
      <Attribute><Name>name</Name><Value>Pierre</Value></Attribute>
      <Attribute><Name>country</Name><Value>France</Value></Attribute>
    </Item>
  </SelectResult>
  <ResponseMetadata>
    <RequestId>ac269ea7-4733-8da2-6705-41a069a88d0f</RequestId>
    <BoxUsage>0.0000228616</BoxUsage>
  </ResponseMetadata>
</SelectResponse>
複数に分割された結果セットにおいて、NextToken要素は、最後の結果セットになるまでレスポンスに含まれ、常にその次の結果セットへのポインタを示します。

つまり、APIをプログラム内で仕様する際には、すべての結果を一度に表示したい場合、NextTokenがなくなるまでループしながら結果の取得をするようなロジックを組む必要があります。
また、ページネーションを明示的に行う場合も、limit句とNextTokenを組み合わせて使用します。

また、"Consistent Read"という項目も、同様にスケーラビリティを優先したためあえて捨てられたデータの一貫性に関する項目です。分散されたデータストレージへ更新が伝播しきるまえに取得されると最新のデータではないものが取得されるようになっており、それでスピードを出しているのですが、それではまずい場合もあります。
その場合はこの"Consistent Read"をtrueに設定することにより、取得スピードは遅くなるけれども最新のデータを取得できるようになっています。
 あまり使いすぎるとSimpleDBのよさがなくなってしまうので、要所で使うのが好ましいようです。
また、結局全部trueになってしまった、という場合はそもそもSimpleDBではなくRDBMSを使用するこを検討したほうがいいかもしれません。


今日はここまでー。
いきなりがんばりすぎて続く気がしない。。。