2012年1月25日水曜日

DynamoDBってなんじゃ?(SDK編)

前回は、AWSコンソールをつかってテーブルの作成まで行いました。
今回は、それらのテーブルにSDKをつかってアクセスしてみます。
ここでは、PHP SDKを使用してみます。

クラスの初期化
DynamoDBクラスの初期化は以下のように行います。
キーなどのパラメータは認証用のファイルに記載しておき省略することも可能です。
$ddb = new AmazonDynamoDB(
  array(
   'key' => 'xxxxxxxxxxxxxxx',
   'secret' => 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyy',
   'default_cache_config' => '/tmp/cache'));

データの投入
次に、前回作成した2つのテーブルにそれぞれデータを投入してみます。
値の指定は、DynamoDBの型名とstring値を紐付けた形で投入するようです。
//book
$books = array(
array('1', '978-4274066306', 'Joel on Software'),
array('2', '978-0321413093', 'Implementation Patterns'),
array('3', '978-1244865723', 'Agile Software Development'),
);
for($i=0; $i<count($books); $i++){
  $ddb->put_item(array(
    'TableName' => 'book',
    'Item' => array(
      'id'   => array(AmazonDynamoDB::TYPE_NUMBER => (string)$books[$i][0]),
      'isbn' => array(AmazonDynamoDB::TYPE_STRING => (string)$books[$i][1]),
      'name' => array(AmazonDynamoDB::TYPE_STRING => (string)$books[$i][2]),
     )
  ));
}

//comment
$comments = array(
  array('1', strtotime('-10 days'), 'memorycraft', 'i like this'),
  array('1', strtotime('-7 days'), 'hoge', 'i never like this'),
  array('2', strtotime('-9 days'), 'moge', 'this is awesome'),
);
for($i=0; $i<count($comments); $i++){
  $ddb->put_item(array(
    'TableName' => 'comment',
    'Item' => array(
      'book_id'    => array(AmazonDynamoDB::TYPE_NUMBER => (string)$comments[$i][0]),
      'posted_at'  => array(AmazonDynamoDB::TYPE_NUMBER => (string)$comments[$i][1]),
      'username'   => array(AmazonDynamoDB::TYPE_STRING => (string)$comments[$i][2]),
      'message'    => array(AmazonDynamoDB::TYPE_STRING => (string)$comments[$i][3]),
    )
  ));
}

データアクセス
続いて、先程データを投入したテーブルに対して、値の取得を行なってみます。
Hashのプライマリキーで一意に取得できる場合は、下記のようにget_itemで行います。
//Hashプライマリキーで完全一致
echo 'get_item (1) ' .
print_r(
  $ddb->get_item(
    array(
      'TableName' => 'book',
      'Key' => array('HashKeyElement' => array(AmazonDynamoDB::TYPE_NUMBER => '1')
    )
  )->body->Item->to_json(),
  true
) . "\n";
出力は以下のようになります。
get_item (1) {"id":{"N":"1"},"isbn":{"S":"978-4274066306"},"name":{"S":"Joel on Software"}}

複合プライマリキーを使い、Hashキーを指定し、Rangeキーで絞るような場合は、queryを使用します。
//複合キーで検索
echo 'query ' .
print_r(
  $ddb->query(
    array('TableName' => 'comment',
      'HashKeyValue' => array(AmazonDynamoDB::TYPE_NUMBER => '1'),
      'ConsistentRead' => true,
      'RangeKeyCondition' => array(
        'ComparisonOperator' => AmazonDynamoDB::CONDITION_GREATER_THAN_OR_EQUAL,
        'AttributeValueList' => array(array(AmazonDynamoDB::TYPE_NUMBER => (string)strtotime('-8 days')))
      )
    )
  )->body->Items->to_json(),
  true
) . "\n";
出力は以下のようになります。
query {"0":{"book_id":{"N":"1"},"message":{"S":"i never like this"},"posted_at":{"N":"1326880572"},"username":{"S":"hoge"}}}

また、キー値以外で検索を行う場合は、scan関数を利用します。
//キー以外で検索
echo 'scan ' .
print_r(
  $ddb->scan(
    array(
      'TableName' => 'comment',
      'AttributeToGet' => array('username', 'message'),
      'ScanFilter' => array(
        'username' => array(
          'ComparisonOperator' => AmazonDynamoDB::CONDITION_EQUAL,
          'AttributeValueList' => array(
            array(AmazonDynamoDB::TYPE_STRING => 'memorycraft')
          )
        )
      )
    )
  )->body->Items->to_json(),
  true
) . "\n";
出力は以下のとおりです。
scan {"0":{"book_id":{"N":"1"},"message":{"S":"i like this"},"posted_at":{"N":"1326621372"},"username":{"S":"memorycraft"}}}

テーブルの作成
ちなみにテーブルの作成は以下のように行います。
//book
$ddb->create_table(array(
        'TableName' => 'book',
        'KeySchema' => array(
                'HashKeyElement' => array(
                        'AttributeName' => 'id',
                        'AttributeType' => AmazonDynamoDB::TYPE_NUMBER
                )
        ),
        'ProvisionedThroughput' => array(
                'ReadCapacityUnits' => 50,
                'WriteCapacityUnits' => 10
        )
));

//comment
$ddb->create_table(array(
        'TableName' => 'comment',
        'KeySchema' => array(
                'HashKeyElement' => array(
                        'AttributeName' => 'book_id',
                        'AttributeType' => AmazonDynamoDB::TYPE_NUMBER
                ),
                'RangeKeyElement' => array(
                        'AttributeName' => 'posted_at',
                        'AttributeType' => AmazonDynamoDB::TYPE_NUMBER
                )
        ),
        'ProvisionedThroughput' => array(
                'ReadCapacityUnits' => 50,
                'WriteCapacityUnits' => 10
        )
));

この際、テーブルの作成には時間が多少かかるので、テーブルを作成したあとに続けてデータ追加処理など行う場合は、以下のように待機します。
foreach(array('book', 'comment') as $table_name){
  $count = 0;
  echo $table_name . " : status ";
  do {
    sleep(1);
    $count++;
    $res = $ddb->describe_table(array(
      'TableName' => $table_name
    ));
    $status = (string)$res->body->Table->TableStatus;
    echo ($status === 'ACTIVE' ? $status . "\n" : '.');
  }
  while ((string) $res->body->Table->TableStatus !== 'ACTIVE');
}
出力は以下のようになります。
book : status ...........................ACTIVE
comment : status ACTIVE


テーブルの削除
テーブルの削除は以下のように行います。
foreach(array('book', 'comment') as $table_name){
        $ddb->delete_table(
                array('TableName' => $table_name)
        );
}


RDBとは違うので、データの構成などに工夫が必要ですが、RDBとの使い分けで非常に高い効果を発揮すると思います。
東京リージョンへの展開が待ち遠しいですが、US-Eastに立てたインスタンスからはRTTがないので、ハイパフォーマンスを体験できます。

以上です。