ニクニクドットミー

カッコいいおっさんを目指すエンジニアの厳かなブログ

【AWS】今更ながらDynamoDB入門

f:id:maaaato:20151223215351p:plain
NoSQLデータベースであるDynamoDBに今更ながら入門してみようと思います。
こちらの記事を参考に手を動かしながら、感覚をつかもうと思います。
AWS再入門 Amazon DynamoDB 編 | Developers.IO

また、DynamoDBにはハッシュキー、レンジキーといった概念があるので、それらを理解する為にこちらを参考にさせて頂きます。

Amazon DynamoDBはマネージドなNoSQLデータベースです。RDBMSとは違い、リレーションやトランザクションといった概念がありません。しかし、キャパシティが自動でスケールするなど管理する項目が少ないのが特徴です。また、RDSとは以下の点が違います。

RDS DynamoDB
一貫性 強力 結果整合なので基本的に弱い(強整合性指定も可能)
原子性 ある ない(同じアイテム内の更新であれば可能)
検索条件 SQLのWHERE句で自由自在 事前指定のキーまたはインデックスのみ
可用性 メンテナンスウィンドウあり 基本的に常に利用可
拡張性 スケールアップのみで天井が低い シャーディングによるスケールアウトが可能

結果整合性はS3と同じですね。データ更新直後はデータが取得できなかったり、古いものが取得される可能性があります。

Black Belt Techをぺたっと張っておきます。

DynamoDBの特徴

  • データは3箇所のAZに保存されるので信頼性が高い
  • ストレージは必要に応じて自動的にパーティショニングされる
  • テーブルごとにReadとWriteそれぞれに対し、必要な分だけのスループットキャパシティを割り当てることができる
  • 例えば下記のようにプロビジョンする

    • Read : 1,000 – Write : 100
    • 書き込みワークロードが上がってきたら
    • Read : 500 – Write : 1,000
    • この値はDB運用中にオンラインで変更可能 – ただし、スケールダウンに関しては日に4回までしかできないので注意
  • 使った分だけの従量課金制のストレージ

  • データ容量の増加に応じたディスクやノードの増設作業は一切不要

DynamoDBの整合性モデル

  • Write
    • 少なくとも2つのAZでの書き込み完了が確認とれた時点でAck
  • Read
    • デフォルト
      • 結果整合性のある読み込み
      • 最新の書き込み結果が反映されない可能性がある
    • Consistent Readオプション
      • GetItem、Query、Scanでは強力な整合性のある読み込みオプ ションが指定可能
      • Readリクエストを受け取る前までのWriteがすべて反映されたレスポンスを保証
      • Capacity Unitを2倍消費する

結果整合性ですが、Consistent Readを設定することで、Writeがすべて反映されたレスポンスが取得出来るようです。これで古いデータを取得する事が無くなると思います。

DynamoDBの料金体系

  • Read・Writeそれぞれ25キャパシティユニットまでは無料
  • 書き込み $0.00742 :10 ユニットの書き込み容量あたり/1 時間
  • 読み込み $0.00742 : 50 ユニットの読み込み容量あたり/1 時間
  • キャパシティユニット 上記で「ユニット」と呼ばれている単位のこと
    • 書き込み
      • 1ユニット:最大1KBのデータを1秒に1回書き込み可能
    • 読み込み
      • 1ユニット:最大4KBのデータを1秒に1回読み込み可能 (強い一貫性を持たない読み込みであれば、1秒辺り2回)

1秒間に100KBのデータを書き込むには100ユニット必要で、読み込みには25ユニット必要となるわけですね。

テーブル操作

HTTPベースのAPIで操作するようです。APIは上の通り。

テーブル設計

キーについて

DynamoDBには主に「table」「item(項目)」「attribute(属性)」という3つの概念が現れます。それに従属する概念として「キー」「インデックス」が出てきます。まずはこの辺りを整理していきましょう。 tableというのはみなさん分かりやすいでしょう。概ねRDBで言うところのテーブルに相当し、itemの集合体です。itemについても、概ねRDBで言うところのrow(行)に相当し、attributeの集合体です。そしてattributeといのも、概ねRDBで言うところのcolumn(列)に相当します。

Hashテーブル

'{ "id": {"N": "1"}, "first_name": {"S": "Jun"}, "last_name": { "S": "Chiba"}, "Score": {"N": "50"}}'

このようなデータがあるとします。Hashキーは単体でプライマリーキーとして利用出来るので、idをHashキーにします。 Hashキーのみ使用している場合はで検索にidしか使えないので、GetItemもしくはScanで検索する事になります。

aws> dynamodb --profile dynamodb_user get-item --table-name test --key '{ "id": {"N": "1"} }'
{
"Item": {
"first_name": {
"S": "Jun"
},
"last_name": {
"S": "Chiba"
},
"Score": {
"N": "50"
},
"id": {
"N": "1"
}
}
}

aws> dynamodb --profile dynamodb_user scan  --table-name test
{
"Count": 1,
"Items": [
{
"first_name": {
"S": "Jun"
},
"last_name": {
"S": "Chiba"
},
"Score": {
"N": "50"
},
"id": {
"N": "1"
}
}
],
"ScannedCount": 1,
"ConsumedCapacity": null
}

Hashキーの重複は出来ませんので、id:1を追加する事は出来ません。

Hash+Rangeテーブル

ハッシュキーテーブルでは、table作成時に1つのattributeを選び、それをハッシュキーとして宣言しました。 そうではなく、table作成時に2つのattributeを選び、1つをハッシュキーとして、もう一つをレンジキーと呼ばれるキーとして宣言する、という方法があります。

ダミーデータを投入しました。tableのキーはこのようになっています。
f:id:maaaato:20151223215431p:plain

aws> dynamodb --profile dynamodb_user describe-table --table-name hash-range-test
{
"Table": {
"TableArn": "arn:aws:dynamodb:ap-northeast-1:123456789:table/hash-range-test",
"AttributeDefinitions": [
{
"AttributeName": "Score",
"AttributeType": "N"
},
{
"AttributeName": "id",
"AttributeType": "N"
}
],
"ProvisionedThroughput": {
"NumberOfDecreasesToday": 0,
"WriteCapacityUnits": 5,
"ReadCapacityUnits": 5
},
"TableSizeBytes": 0,
"TableName": "hash-range-test",
"TableStatus": "ACTIVE",
"KeySchema": [
{
"KeyType": "HASH",
"AttributeName": "id"
},
{
"KeyType": "RANGE",
"AttributeName": "Score"
}
],
"ItemCount": 0,
"CreationDateTime": 1450872773.111
}
}

では、id=2でScoreが10から90の間にあるitemを取得します。 f:id:maaaato:20151223215504p:plain

ちゃんと取得出来ましたね!Between検索が出来ています。
Hashキーでパーティショニングされ、さらにそのパーティションのRangeキーで検索がされるみたいです。

ハッシュキーテーブル 複合キーテーブル
Scan 全件取得 全件取得
GetItem hash-keyに対するequal-to条件値を1つ指定して、0〜1件取得 hash-keyとrange-key両者に対するequal-to条件値を指定して、0〜1件取得
Query (無意味) hash-keyに対するequal-to条件値を1つ、range-keyに対する範囲条件(optional)を指定して、0〜複数件取得

DynamoDBではホットパーティションに気をつける必要があります。ホットパーティションは一部のパーティションのみに負荷が掛かる事を指します。

例えば。前述の「レンジキーの範囲だけで検索(つまりrange BETWEEN x AND yによる検索)」の例を考えてみましょう。裏側のイメージが出来ていない人は、表面的なことだけを考えてこんなことを考えるかもしれません。 「あ! ハッシュキー内でしか範囲検索できないのならば、ハッシュキーは固定で1とかにしておいて、そして使わなければいいんだ。そうすれば全件に対する範囲検索ができるぞー!」

この例だとHashキーが1で固定されているので、全て同じパーティションにデータが入ってしまい、せっかくのパーティション機能が活用されていません。データ件数が少ないと問題は出てきにくいですが、件数が増えると負荷が掛かってしまいますので、パーティション設計は大切です。

まとめ

簡単な操作しかしていませんが、他にローカルセカンダリインデックスやグローバルセカンダリインデックスなど設定があります。高速、高可用性を備えたDynamoDBはとても魅力的だと思います。

参考サイト