DynamoDBはキー・バリューで管理するデータベースです。 MySQLのようなデータベースと比べて検索機能は強くないですが、スケールアップができる構造になっています。
1. プライマリーキー
データベーステーブル内の項目は、プライマリーキーにより一意に決まります。プライマリーキーは
- パーティションキー
- ソートキー(必須ではない)
により構成されます。これら2つの値を決めると、テーブル内項目が1つに決定されます。
1.1 パーティションキー
パーティションキーにより、テーブル内項目は大まかに分離されます。もしも、プライマリーキーとしてパーティションキーのみを使用する場合、パーティションキーを決めれば、項目が一つに決まります。
パーティションキーはアクセスがなるべく均等になるように設定します。というのも、テーブルのデータは一つのサーバーに格納されているわけではなく、パーティションキーによって細切れにして多数のサーバーで保管されています。もしも1つのパーティションが高頻度に使用されると、そのパーティションが属するサーバーの負荷が極端に増えてしまいます。そのため、均等にデータベースが使われるようにパーティションを設計するのが望ましいのです。
パーティション設定のいい例と悪い例を挙げます。ユーザーIDの場合は、ユーザーに依存して使用頻度が変わらないので均一性が高くていいです。逆に登録日をパーティションキーとすると、平日や休日などでばらつきがでるためよくない例です。以上のことから、データを一つの塊となるようにパーティションを設定するのがいいことがわかります。
キーの命名規則ですが、個人的にはcamelCaseがいいと思っています(deviceIdのような区切り位置に大文字を使い、はじめは小文字の形式)。というのも、読み込みの際にJSONで取得するケースが多く、JSONの場合はcamelCaseが推奨されるからです。
1.2 ソートキー
ソートキーを設定すると、パーティションキーによって分けられた項目をソートキーで並べて取得することができます。パーティションキーとソートキーを決めると、項目が一つに決まります。
ソートキーは
- begins_with
- between
- 比較
などの演算子により範囲を指定してクエリできるという特徴があります。これらをうまく生かすことで、効率よく検索できるので慎重に設計する必要があります。
以下のような複合ソートキーを作成すると階層レベルでクエリを実行することができます。
[country]#[region]#[state]#[county]#[city]#[neighborhood]
例えば、ある特定のcityのみの項目をqueryでとってくることができます。
2. データベースの基本操作
2.1 getItem (取得)
プライマリーキーを指定することで項目を一つ取得することができます。
2.2 検索
queryとscanで検索を行うことができます。
2.2.1 query
プライマリーキーで検索することができます。ソートキーを使用すれば、さらに項目を絞り込むことが可能です。
また、パーティションキーでは等価、ソートキーでは「=,<,<=,>,>=,BETWEEN,begins_with」で条件を制限することができます。まとめると以下の通りです。
#partitionKeyName = :partitionkeyval
#partitionKeyName = :partitionkeyval AND #sortKeyName = :sortkeyval
#partitionKeyName = :partitionkeyval AND #sortKeyName < :sortkeyval
#partitionKeyName = :partitionkeyval AND #sortKeyName <= :sortkeyval
#partitionKeyName = :partitionkeyval AND #sortKeyName > :sortkeyval
#partitionKeyName = :partitionkeyval AND #sortKeyName >= :sortkeyval
#partitionKeyName = :partitionkeyval AND #sortKeyName BETWEEN :sortkeyval1 AND :sortkeyval2
#partitionKeyName = :partitionkeyval AND begins_with ( #sortKeyName, :sortkeyval )
2.2.2 scan
全件を取得することができます。全件取得は重いので基本的に使用してはいけません。
2.2.3 filter
query, scanで取得した値に対してfilterにより余分なものを取り除くことができます。取得した後に取り除くので、あまり効率はよくない点に注意が必要です。セカンダリーインデックスを使うと効率よく取得できます。
2.3 追加と更新
putとupdateで項目の追加と更新が可能です。putの場合は更新の際に明示していない属性を削除します。updateの場合は明示していない属性をそのまま残します。
2.4 削除
deleteによりプライマリーキーにより指定した項目を削除できます。
3. セカンダリ―インデックス
プライマリーキーだけの検索では足りないことが多々あります。そのようなときはセカンダリーインデックスを追加します。セカンダリーインデックスにはグローバルセカンダリインデックス(GSI)とローカルセカンダリインデックス(LSI)があります。
3.1 グローバルセカンダリインデックス(GSI)
プライマリーキー以外に新しくインデックスを追加して検索できるようになります。GSIでは、パーティションキーとソートキー(任意)を新しく指定します。キャパシティユニットは作成したGSIから消費されます。
3.2 ローカルセカンダリインデックス(LSI)
パーティションキーは同じにして、ソートキーのみを変えたい場合に使用します。初期のテーブル設定の際にのみ使用できるので注意が必要です。キャパシティユニットはベーステーブルから消費されます。
4. 料金
料金に関しては大きく分けて、データベース操作、ストレージとデータ転送となります。データベース操作の料金が高くなると思うので、操作しやすいようにテーブルを設計するのがいいかと思います。
4.1 読み込み/書き込み要求
支払い方法にはオンデマンドとプロビジョニング済みの2つがあります。前者は、使った分のキャパシティだけ料金を払う形式で、後者は事前にキャパシティユニットを設定する方式です。ある程度大規模になり時間当たりの使用キャパシティが読める場合は、プロビジョニングを使用するとコストを抑えることができます。
キャパシティの計算方法を理解しましょう。読み込みに関しては、getItem1回に対して1キャパシティを消費します。queryなら「データ数/4KB」のキャパシティを消費します。よって、getItemで10個の項目を撮るよりも、queryで10個の項目を一回でとってきた方が有利となります。
書き込みは1KBにつき1キャパシティとなります。
4.2 ストレージ
ストレージに関しては毎月25GBは無料で、それ以降は0.285USDとなります(2021年2月東京リージョン現在)。
4.3 データ転送
データ受信や同一リージョンのAWSサービスへの送信に関しては無料ですが、その他の送信に関しては0.114USD/GBほどお金がかかります。