AWS DynamoDB(4)項目の読み取りと書き込み

項目の読み取り

単一項目の読み取り

単一項目の読み取りには、GetItemアクションを使用する。一部の属性のみを取得する場合には、ProjectionExpressionを使用する。

{
    TableName: "Music",
    Key: {
        "Artist": "No One You Know",
        "SongTitle": "Call Me Today"
    },
    "ProjectionExpression": "AlbumTitle, Year, Price"
}

複数項目の読み取り

複数項目の読み取りには、Queryアクションを使用する。Queryアクションは、複合プライマリキー (PartitionKeyとSortKey) のあるテーブルで使用できる。KeyConditionExpressionFilterExpressionの中で、ExpressionAttributeValuesの値をプレースホルダーとして使用する必要がある。

{
    TableName: "Music",
    KeyConditionExpression: "Artist = :a and SongTitle = :t",
    ExpressionAttributeValues: {
        ":a": "No One You Know",
        ":t": "Call Me Today"
    }
}

全ての項目の読み取り

全ての項目の読み取りには、Scanアクションを使用する。FilterExpressionを用いて不要な項目を除外することもできるが、Scan後に実行されることに留意が必要である。

// Return all of the values for Artist and Title
{
    TableName:  "Music",
    ProjectionExpression: "Artist, Title"
}

項目の書き込み

新たな項目の書き込み

新たな項目の書き込みには、PutItemアクションを使用する。

{
    TableName: "Music",
    Item: {
        "Artist":"No One You Know",
        "SongTitle":"Call Me Today",
        "AlbumTitle":"Somewhat Famous",
        "Year": 2015,
        "Price": 2.14,
        "Genre": "Country",
        "Tags": {
            "Composers": [
                  "Smith",
                  "Jones",
                  "Davis"
            ],
            "LengthInSeconds": 214
        }
    }
}

既存の項目の変更

既存の項目の変更には、UpdateItemアクションを使用する。既存の項目が存在する場合には更新され、存在しない場合には項目が追加される。条件付き書き込みをサポートしており、ConditionExpressionがtrueと評価されたときのみ実行される。

{
    TableName: "Music",
    Key: {
        "Artist":"No One You Know",
        "SongTitle":"Call Me Today"
    },
    UpdateExpression: "SET RecordLabel = :label",
    ConditionExpression: "Price >= :p",
    ExpressionAttributeValues: { 
        ":label": "Global Records",
        ":p": 2.00
    }
}

項目の削除

項目の削除には、DeleteItemアクションを使用する。条件付き削除をサポートしており、ConditionExpressionがtrueと評価されたときのみ実行される。

{
    TableName: "Music",
    Key: {
        Artist: "The Acme Band", 
        SongTitle: "Look Out, World"
    },
   ConditionExpression: "attribute_exists(RecordLabel)"
}

AWS DynamoDB(3)使用する上で注意すること

適切なスキーマ設定

DynamoDBは限られた範囲のクエリを効率よく処理できるがそれ以外はコストが掛かる。重要なクエリを安価に効率よく実行できるようなスキーマ設計が需要である。アプリケーションのユースケースを注意深く解析して、単一のテーブルで処理可能な構造とする。

スキーマを構成する上で確認すべき点は、データサイズデータ形式クエリ負荷である。

  • 関連するデータをまとめる
  • ソート順を決定する
  • クエリを分散する
  • グローバルセカンダリインデックスを使用する

パーティションキー

効率的なパーティションキーの設計とし、クエリを分散させる。ただし、一部のパーティションに負荷が偏った場合でも、アダプティブキャパシティが自動的に適用され、ある程度は影響を緩和することができる。

アダプティブキャパシティ

負荷を分散させるために、格納された値を元にパーティションキーにサフィックスを付加する手法も存在する。

キャパシティを上げた状態でOnDemand課金に切り替える

OnDemandを使用する場合は、バーストトラフィックに耐えれるように、まずキャパシティを上げた状態にしておき、それからOnDemand課金に変更する。ただし、課金体系への変更には制限があることから頻繁な変更はできないことに留意が必要。

大きな項目データは、圧縮するかS3に保存する

圧縮したバイナリデータを保存したり、S3に保存してこのオブジェクト識別子をDynamoDBに保存することができる。

時系列データは複数テーブルによる構成も検討する

時系列データの場合、当日データに書き込みや読み込みが偏り、ホットスポットが発生する。これを回避するために期間ごとにテーブルを分割して、バッチ処理で新しいテーブルの作成と新しいテーブルへの切り替えを行う。

クエリの最適化

Scanクエリはなるべく使わない

Scanは非常に低速でデータ数が大きくなると非効率となるので避ける。

条件付き書き込みやアトミックカウンターを使用する

複数が同時に更新する場合は、条件付き書き込みを使用する。

AWS CloudWatch(2)システム運用における監視項目

AWSでシステム構築を行う場合、基本的にはこのCloudWawtchのみの監視で十分なことが多い。様々な監視項目が存在するが、システム運用を行う際に特に監視する必要のある項目は以下の通り。

DynamoDB

多くの項目がデフォルトで 1 分ごとにデータを送信するが、5 分間隔で送信されるデータも存在する。事前に設定した容量(スループット)が十分であるかを監視しておくと良い。

  • ProvisionedReadCapacityUnits
  • ConsumedReadCapacityUnits
  • ProvisionedWriteCapacityUnits
  • ConsumedWriteCapacityUnits

EC2

Amazon EC2 はデフォルトで 5 分ごとに CloudWatch にデータを送信する。インスタンスを作成する際に、「詳細モニタリングを有効」にチェックを入れることでこれを 1 分ごとに変更することができるCPU使用率を監視しておくと良い。メモリやディスクのメトリクスを取得するためには、CloudWatch Agentをインストールする必要がある

  • CPUUtilization

Kinesis

事前に設定した容量(シャード数)が十分であるかを監視しておくと良い。

  • PutRecords.Records
  • IncomingRecords
  • GetRecords.Bytes
  • IteratorAge

AWS DynamoDB(2)AWS SDK for Javaとアトミックカウンター

JavaによるDynamoDBへのアクセス

AWS SDK for Javaを用いることで、JavaプログラムからDynamoDBにアクセスすることができる。DynamoDBはHashKeyでインデックス化されており、Itemの追加や更新には当該テーブルのHashKeyの情報が必要となる。

DynamoDBインスタンスを取得

Credentialsやテーブル名を指定してDynamoDBインスタンスを指定する。リージョンを明示的に指定しないとデフォルトではバージニア州が指定されてしまう。通常credentialsは、ホームディレクトリ直下の「.aws」ディレクトリ(~/.aws/credentials)に置かれている。

import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Table;

// ProfileCredentialsProvider
credentialsProvider = new ProfileCredentialsProvider();
try {
    credentialsProvider.getCredentials();
} catch (Exception e) {
    throw new AmazonClientException("Cannot load the credentials from the credential profiles file. "
                    + "Please make sure that your credentials file is at the correct "
                    + "location (/Users/hoge/.aws/credentials), and is in valid format.", e);
}

// AmazonDynamoDBClient
AmazonDynamoDBClient dynamoDBClient = new AmazonDynamoDBClient(credentialsProvider);
// リージョンの設定
dynamoDBClient.setRegion(Region.getRegion(Regions.fromName("ap-northeast-1")));
// DynamoDB
DynamoDB dynamoDB = new DynamoDB(dynamoDBClient);
// DynamoDB Table
Table dynamodbTable = dynamoDB.getTable(TABLE_NAME);

データの取得

getItemメソッドで、HashKeyを指定することでItemの取得が可能となる。HashKeyの「名称」と「値」を指定することで、Itemを取得できる。

dynamodbTable.getItem(HASH_KEY_NAME, HASH_KEY_VAL);

データの書き込み

DynamoDBは空文字を格納することができない。データを格納する前に空文字チェックを行っておく

データの挿入

putItemメソッドで、Itemの書き込みが可能となる。書き込みを行う際、HashKeyの「名称」と「値」の指定が必須である。なお、同じキーを持つ項目が存在している場合は、全ての値が更新(上書き)される。

Item item = new Item()
		.withPrimaryKey(HASH_KEY_NAME, HASH_KEY_VAL)
		.withString(key, val);
dynamodbTable.putItem(item);

データの更新

updateItemメソッドで、Itemを更新する。HashKeyの「名称」と「値」を指定することで、どのItemを更新するかが決定される。また更新内容は、SETやADD, REMOVEで始まる更新式によって指定する。なお、同じキーを持つ項目が存在している場合は、指定したAttributeのみ置換される。指定したキーが存在しない場合は、新規の項目が作成される。

// 更新するAttributeの名称(Key)
Map<String,String> resultExpressionAttributeNames = new HashMap<String,String>();
resultExpressionAttributeNames.put("#key", KEY_NAME);
// 更新するAttributeの値(Value)
Map<String,Object> resultExpressionAttributeValues = new HashMap<String,Object>();
resultExpressionAttributeValues.put(":val", VALUE);
// Attributeの更新
dynamodbTable.updateItem(
	HASH_KEY_NAME, HASH_KEY_VALUE, // 更新対象のItem(HASH_KEY, VALUE)
	"set #key = :val",
resultExpressionAttributeNames,
resultExpressionAttributeValues);

the provided key element does not match the schema javaというエラーが発生した場合は、誤ったHashKeyを指定している可能性があるので確認する。

アトミックカウンター

DynamoDBは、高い可用性やスケーラビリティ(BASE属性)を確保している一方で、厳密な一貫性や即時反映性(ACID属性)を諦めている。しかし、アトミックカウンター(一貫性のあるカウンタ)をサポートしているため、他の処理を妨げることなく並列に、一貫性のあるカウンタ処理を実施することは可能である。

アトミックカウンターをサポートとあるが、アトミックカウンターという特別に用意された機能が存在するわけでもない。updateItemメソッドを利用して以下のような記述をすると、アトミック性のあるカウンタを実装することができる。

    	Map<String,String> expressionAttributeNames = new HashMap<String,String>();
		expressionAttributeNames.put("#key", kEY_NAME);
		Map<String,Object> expressionAttributeValues = new HashMap<String,Object>();
		expressionAttributeValues.put(":val", 1);

		try {
			dynamodbTable.updateItem(
				    HASH_KEY_NAME, HASH_KEY_VAL,  
				    "ADD #key :val",
				    expressionAttributeNames, 
				    expressionAttributeValues);
		} catch (Exception e) {

		}

更新式にて単純に1を加算する処理を行っているだけであるので、プログラムを書き換えることで加算もできれば減算もできる。2を足す、3を引くといったことも可能である。

    	dynamodbTable.updateItem(
				    HASH_KEY_NAME, HASH_KEY_VAL,  
				    "SET #key = #key + :val",
				    expressionAttributeNames, 
				    expressionAttributeValues);

としてもよいが、SETアクションの場合、アイテムが事前に存在しない場合にエラーとなるので、ADDアクションで実装したほうがよい。

DynamoDB操作の違い

なお、DynamoDBのそれぞれの操作の差異は以下の通り。

操作 アクション 既にアイテムが存在している場合の挙動
putItem 全ての属性を消去した上で新たに属性を追加する
updateItem SET 対象の属性のみ更新される
updateItem REMOVE 対象の属性のみ削除される
updateItem ADD **対象の属性が数値の場合は値に追加される、セットデータの場合は要素が追加される**
updateItem DELETE 対象の属性のセットデータから要素が削除される

AWS DynamoDB(1)DynamoDBの概要

DynamoDBとは

DynamoDBは高速かつフレキシブルな、完全マネージド型のNoSQLデータベース。データはKey, Value型で保存される。高速なレスポンススケーラビリティドキュメントデータモデル(JSON)をサポートしていることなどが特徴的。同一リージョン内の3つの設備で同期レプリケーションされるため障害にも強い。

管理不要で信頼性が高い

  • 単一障害点が存在しない構造
  • 同一リージョン内の3つの設備にデータが保存される
    • 設定と構成、レプリケーション、ソフトウェアのパッチ適用などが不要
  • ストレージの自動パーティショニング

プロビジョンドスループット

  • Read/Writeの速度を指定できる
    • ダウンタイムやパフォーマンスの低下が生じない

1キャパシティユニットは、最大4KBの読み込み項目に対して、1秒あたり1回の強力な整合性のある読み込み、もしくは1秒あたり2回の結果整合性のある読み込み最大1KBの書き込み項目に対して、1秒あたり1回の書き込みを表す単位。読み込みや書き込みのリクエストがプロビジョニングしたスループットを超えた場合は、HTTP 400 コード (Bad Request) : ProvisionedThroughputExceededExceptionのエラーレスポンスが返る。DynamoDB Auto Scalingを用いることで、キャパシティを自動的に変更することが可能であるが、バーストトラフィックには対応できないので注意が必要である。

2018年からプロビジョニングモードに加えて、前に到達したトラフィックレベルまで拡張または縮小(前のピークトラフィックの最大 2 倍まで瞬時に対応)して、ワークロードを即座に受け入れることができるオンデマンドモードを指定することが可能となった。プロビジョニングモードとオンデマンドモードは、24時間に1回切り替えることができる。プロビジョニングモードからオンデマンドモードに切り替えた場合、プロビジョニングされたキャパシティーが維持されるため、大量のトラフィックが予想される場合は、プロビジョニングモードで想定されるキャパシティを設定しておき、その後オンデマンドモードに切り替えると良い。

その他の特徴

  • 容量制限が存在しない
  • 結果整合性
    • 書き込みに関しては、2箇所への書き込みが完了した時点でACK
    • 読み込みに関しては、最新の結果が反映されていない可能性がある

複数のクライアントが同時にアクセスし値を更新するようなシナリオの場合は、「現在この値であれば値を更新する」といったような条件付き書き込みを行うことで、想定外のデータ上書きを防止することが可能である。

条件付き書き込み

料金

  • 指定したスループットによる時間課金
  • 保存データ量による課金

DynamoDBへのアクセスにはHTTPおよびHTTPSが用いられる。HTTP/HTTPSによる通信はオーバヘッドが大きいため、ゲームアプリ等の早い応答速度が求められるサービスでは、DyamoDBではなくRDSなど他のデータベースを選択することが多い。シリアライズフォーマットとしては、JSON形式が使用される。

データモデル

DynamoDBのテーブルは複数のItemから構成され、Item内には、KeyValue型のデータ(Attributes)が格納されている。各Itemに格納されているAttributesの数は不ぞろいでも良く(スキーマレス)、あるアイテムにはuser-id, name, e-mail, ageの4つのAttributesが、別のItemにはuser-idのみが格納されているとことも可能である。格納するAttributesのうち1つにPartitionKeyを指定する。PartitionKeyはプライマリーキーとして利用でき、ハッシュインデックスを構築されるときのキーとなる。この場合同じ項目を持つPartitionKeyは持つことができない。また、SortKeyを指定することもでき、この場合はPartitionKey + SortKeyでプライマリーキーとなる。

DynamoDBは、数値や文字列、バイナリ(圧縮ファイルや画像)などのスカラー型、これらのスカラー型をセットにしたセット型、リストやマップ(JSON)などのドキュメント型の3つの形式をサポートしている。格納することのできる値の範囲や制限事項などは、DynamoDB での制限 – Amazon DynamoDBを参照のこと。文字列や多値データセット型は空白や空白セットはサポートされていない。スカラー型の場合、NULL値を格納することは可能である。ドキュメント型を用いることで入れ子構造のデータを保存することも可能である。

DynamoDBには予約語が存在し、予約語を属性名として定義する式を実行することはできない。予約語と競合する属性名を式の中で使用したい場合には、代替の属性名を定義するなどし使用する。

パーティショニング

DynamoDBは、スループット性能を維持するためテーブルをパーティショニングする。PartitionKeyが同じでSortKeyが異なる項目は、SortKeyによって並べ替えられて物理的に近いところに配置される。パーティション間のデータ分散にはPartitionKeyを用いられ、またスループットは各パーティションに均等に配分されるために、特定のPartitionKeyにアクセスが偏ると性能が出ない場合がある

セカンダリインデックス(Secondary Index)

セカンダリインデックスは、SortKey以外に使用することのできる絞込み検索用のAttributesである。ローカルセカンダリインデックス(local Secondary Index)はSortKeyの代替となる検索用インデックスで、同一PartitionKeyのItem検索に利用できる。グローバルセカンダリインデックス(Glocal Secondary Index)は、PartitionKeyの代替となる検索用インデックスで、異なるPartitionKeyのItem検索にも利用できる。セカンダリインデックスは便利ではあるが、追加のスループットやストレージを必要とするためRDSで代替できないか検討すべきである。

テーブルあたり20個のグローバルセカンダリインデックスと5つのローカルセカンダリインデックスを定義することができる。

項目の操作

DynamoDB は、作成、読み込み、更新、削除 (CRUD) の 4 つの基本的なオペレーション機能を提供する。また、以下に加えて、複数のGet/Put処理を1回の呼び出しで行うBatchGetItem/BatchWriteItemという関数も用意されている。

  • PutItem – 項目の作成
  • UpdateItem – 項目の更新
  • GetItem – 項目の読み取り
  • Query – 特定のPartitionKeyがある全ての項目の読み取り
  • Scan – 指定されたテーブルまたはインデックスの全ての項目の読み取り
  • DeleteItem – 項目の削除

また、Time to Live(TTL)を用いてテーブルの項目の有効期限を設定することが可能で、データベースから項目を自動的に削除することができる。指定する時刻はUnixtimeで、DynamoDBはこの期限から48時間以内に項目を削除する。有効期限すぐに削除されるとは限らないために、これを考慮したアプリケーション実装とすることが必要。

DynamoDB ストリーム

DynamoDBストリームは、DynamoDBテーブルのデータ変更イベントをキャプチャする。ストリームには24時間の有効期限があり、Lambdaや等を用いてこれらのストリームデータの処理を行うことができる。

DynamoDBストリームは、変更前のイメージと変更後のイメージの両方を含むことができるため、これらを比較することで変更の差分を取得することができる。