Amazon Web Services ブログ

ソートキーを使用して、Amazon DynamoDB でデータを編成する



Amazon DynamoDB テーブルにデータを入力することは難しくありません。さらにデータの整理に頭を悩ませることなく、データ検索オプションを後で制限することができます。

データ検索のためのデータの編成とプランニングは、テーブルを設計する際に大切な手順のうちの一つです。適切なデータ編成がない場合、データを取得するための唯一のオプションは、パーティションキーによる検索テーブルのフルスキャンしかありません。これらの検索方法では、テーブルは高価となり、パフォーマンスは限られたものになります。

この記事では、ソートキーを使用した、データを取得するための最適化テーブルの実際の例について検討します。ソートキーを使用すると、データをグループ化し編成するだけでなく、テーブル内の項目に対してクエリを実行する追加手段にもなります。

パーティションキーの簡単な紹介

データの編成方法についてよりよく理解するためには、データが DynamoDB に格納される方法を知っておくことが大切です。DynamoDB テーブルの各項目には、DynamoDB のドキュメントに記載されているように、テーブルのプライマリーキーを作成する必要があります。プライマリーキーは、パーティションキー、またはパーティションキーとソートキーの組み合わせです。プライマリーキーは、テーブル全体で固有のものでなければなりません。

パーティションキーのみをプライマリキーとして使用する場合、最適とはいえないパーティションキーを選択し、その結果、テーブルの全体的なパフォーマンスに影響する可能性があります。例えば、同じプライマリキーを頻繁に取得すると、スループットが超過した例外が発生し、テーブルへのリクエストを抑制する可能性があります。

パーティションキーとそのベストプラクティスの詳細については、AWS データベースブログの「正しい DynamoDB パーティションキーの選択」の記事を参照してください。

ソートキーを使用して、データ検索オプションを拡張する

場合によっては、テーブルを作成する時に、プライマリキーとしてパーティションキーのみを提供する場合があります。このような場合、パーティションキーによるデータの取得、またはスキャン操作でテーブル内のすべての項目の返送に限定されます。このようにテーブルを作成するのは簡単で、場合によってはとてもシンプルです。

ただし、データセットが増加するにつれて、テーブルスキャンは価格とパフォーマンスの面で大きな負担となります。テーブルスキャンは、読み取り容量をすぐに使い果たしてしまうため、請求額が増加します。読み書きの容量単位、およびこれらの単位が DynamoDB 請求書に与える影響の詳細については、DynamoDB ドキュメントの「Amazon DynamoDB 料金表」と「読み書きの処理能力」を参照してください。さらに、パーティションキーだけで、項目の取得ができるとは限りません。特定の日付の前に顧客の注文を検索し、カテゴリーに一致するすべての商品を取得する例を考えてみましょう。ソートキーをテーブルに追加すると、スキャンやパーティションキーを利用する以上に、より多くのデータ検索機能が利用可能となります。

ソートキーは、クエリによって返される項目のソート順を決定するためだけのものではないことに留意しましょう。この記事が示すように、ソートキーは検索のために項目を直接ソートするのではなく、データ検索オプションを拡張します。

パーティションキーとソートキーを組み合わせると、コンポジットキーが作成され、それらのコンポジットキーはテーブル内の個々の項目のプライマリキーになります。コンポジットキーを使用すると、ソートキーに対して KeyConditionExpression でクエリを使用することが可能となります。クエリでは、KeyConditionExpression を用いて、キーに対して評価して返される項目を制限する比較演算子を利用し、条件文を記述できます。つまり、特殊演算子を使用して、項目をソートキー値でインクルード、除外、および照合することができるわけです。

ソートキーを使ってクエリを実行する例と、時間によってクエリを実行する例を、簡単に見てみましょう。特定のテーブルのソートキーが、数値として格納された Unix タイムスタンプであるとします。この場合、特定の時刻の前に < 比較演算子を使用して、すべての項目を返すというキー条件でクエリを発行できます。時間を限定した検索の例を、後ほど考察しましょう。

範囲を使う

ソートキーに関連した範囲を使用して、クエリを作成します。KeyConditionExpressions クエリでは、次の演算子を使用できます。

  • =
  • <
  • >
  • <=
  • >=
  • between
  • begins_with

between 演算子は範囲を決定するために 2 つの値を受け取り、begins_with 演算子は文字列が部分文字列で始まるかどうかを調べます。これらの演算子のほとんどは数値を使用できますが、beginning_with 演算子では面白い方法でデータをクエリできます。

例えば、場所をグループ化する方法を検討するとしましょう。米国では、場所は通常、都市、州、郡でグループ分けします。city-state-country のテンプレートを使用して、項目の場所を文字列に格納するソートキーを作成したとします。

city-state-country の順序は、場所データの最も具体的な部分から開始します。結果として、beginning_with 演算子を使ったクエリは、都市に限定されます。場所文字列の順序を country-state-city に切り替えると、beginning_with 演算子は代わりに 3 つのレベルのクエリを提供します。

  • starts_with(USA) – 米国にあるすべての項目を返します。
  • begins_with(USA-TX) – テキサスにある項目のみを返します。
  • begins_with(USA-TX-Houston) – ヒューストンにある項目のみを返します。

このように場所をグループ化すると、あとは創意工夫で様々に活用できます。

データの編成および検索に、ソートキーを使用する方法

実際の例にソートキーの知識を当てはめると、将来作成するテーブルでのデータ編成と検索に使用できる、再現性のあるテクニックとパターンが見えてきます。

イベント監視、pub/sub イベント、サービスログなどのログデータを考えてみましょう。データソースの例としては、クリックストリーム、systemd ログ、イベントログ、または Salesforce などのビジネス生産性と SaaS (Software-as-a-Service) プラットフォームから API をストリーミングする、などがあります。

例 1: ログデータを使う

単純な例から始め、Unix のタイムスタンプについて議論する際に、先ほど触れた時間で制限する検索についての概念を実行しましょう。複数の機器からテレメトリデータを収集しているとします。各デバイスには、デバイス ID、固有の各イベントのイベント ID、およびタイムスタンプがあります。ログには、ログに記録されたイベントに関連付けられた時間データが含まれていることがよくあります。特定のタイムスパンをクエリすることでログを取得するのが一般的です。

DynamoDB コンソールに移動し、新しいテーブルを作成します。ログデータを編成するには、次のスクリーンショットに示すように、ログデータを作成するサービスに固有のパーティションキーを指定します。例えば、パーティションキーは、hostname または deviceID の値になります。ソートキーには、個々のイベントの timestamp 値を指定します。

ログデータを編成するには、ログデータを作成するサービスに固有のパーティションキーを指定する

すると、between 演算子と 2 つのタイムスタンプ、> または < を使用して、クエリを発行できます。次のスクリーンショットに示すように、 deviceID123 というデバイスに対して 3 つのレコードを保存します。Unix のタイムスタンプとして保存された日付は、2018 年 8 月 29 日の午後 12 時 UTC になります。2018 年 9 月 4 日午前 1 時UTC; 2011 年 7 月 9 日午後 1 時UTC。タイムスタンプを人が読める形式に変換するには、Epoch Unix Time Stamp Converter のウェブサイトを参照してください。

deviceID が 123 のデバイスに対して 3 つのレコードが格納される

2018 年 9 月 4 日午前 12 時 UTC より前に、< 演算子とその時間の Unix タイムスタンプを使用して、タイムスタンプ付きのログのクエリを発行します : 1536019200

2018 年 9 月 4 日午前 12 時 UTC より前のタイムスタンプを持つログのクエリ結果

例 2: チャットメッセージを使う

この例は、ソートキーにテンプレート文字列を使用するという概念に基づいています。前に説明した位置データの例を覚えておいてください。この新しいケースでは、チャットアプリケーションからのデータを検討します。参考のために、ユーザーにはユーザー名があり、チャットルームには固有の ID があり、各チャットメッセージを保存したいとします。ユーザーと時間帯によって、メッセージを取得できるようにしたいとします。

chatroomID はパーティションキーとして使用できます。とても賑わっているチャットルームがある場合、chatroomID に大きくなっていく数値の接尾辞を追加して (例えば seattle-1seattle-2)、データアクセスをパーティション中に分散させることができます。この方法で、ホットパーティションの作成に関連する問題を回避できます。ホットパーティションとテーブルの最適なパーティションキーの選択についての詳細は、AWS データベースブログの「正しい DynamoDB パーティションキーの選択」を参照してください。

複合ソートキーは、次の形式にする必要があります :user_name#datetimedatetime 値の場合、yyyy-MM-dd:HH:mm:ss のような文字列表現を用いると、ほとんどのクエリオプションが使えます。

ログデータを編成するには、ログデータを作成するサービスに固有のパーティションキーを指定する

この例では、次のスクリーンショットに示すように、2 人のユーザーのメッセージをさまざまな時間に保存します。

2 人のユーザーのメッセージをさまざまな時間に保存する

複合ソートキーを作成すると、begins_with 演算子でクエリを実行するためのいくつかのオプションが与えられます。ルームと特定のユーザーによって制限されたメッセージをクエリすることができます。

複合ソートキーを作成すると、begins_with 演算子でクエリを実行するためのいくつかのオプションが与えられる

chatMessageIdentifier テンプレート文字列の日付部分は、最も具体的でないものから最も具体的なものへと配列されています。例えば、ユーザーのメッセージをクエリし、1 年 (amsg#2018) から、または 1 年と特定の月 (amsg#2018-08) からすべてのメッセージを返すことができます。

chatMessageIdentifier テンプレート文字列の日付部分は、最も具体的でないものから最も具体的なものへと配列されている

クエリにはトレードオフがあります。例えば、user_name でテンプレート文字列を開始することを選択しているため、ここでは時間に基づいてデータを照会することはできません。時間がより重要な検索要因である場合は、テンプレート文字列の順序を変更するか、元のテンプレート文字列を反転する追加のソートキーを使用して、2 次インデックスを追加できます : datetime#user_name

例 3: ドキュメントの追跡

最後に、ソートキーが任意の値であり、同じオブジェクトに関する異なるデータセットを取得するように変更するといった、より高度なユースケースを考えてみましょう。ここでは、ドキュメント追跡システムの実際の例を使用します。ドキュメント追跡システムでは、ドキュメントに対するアクセス許可を持つユーザー、ドキュメントのバージョン、ドキュメントの保存場所、ドキュメントに関するメタデータを保持することが多いです。パーティションキーをドキュメントに関連付けられた一意の ID に設定し、ソートキーを使用してドキュメント追跡システムでこれらの変数をそれぞれ表します。

ログデータを編成するには、ログデータを作成するサービスに固有のパーティションキーを指定する

包括的であるけれども特定の状況に依存するこのソートキーを使用して、metadata のソートキー値を追加することで、任意のメタデータを保存および検索できるようになります。または、このフィールドを使用して、permissions のソートキー値でこのドキュメントにアクセスできるユーザーを指定するためのアクセス識別子を保管することができます。データベーススキーマを前もって定義するのではなく、アプリケーションでドキュメントに関してソートするデータの種類のコンテキストを提供できるようにすることが可能です。こうした柔軟性により、アプリケーションはデータベースに代わって、データを定義することができるのです。

データの構造は項目ごとに異なり、任意の各ソートキー値に関連する情報を保存する

前出のスクリーンショットで分かるように、データの構造はアイテムごとに異なり、任意の各ソートキー値に関連する情報を保存します。これらのアイテムの 1 つを取得するために、必要なドキュメント情報の種類の = 演算子を使用してクエリを発行します。例えば、ドキュメントのメタデータを取得するには、クエリに = 演算子と値 metadata を使用します。

必要なドキュメント情報の種類の = 演算子を使用してクエリを発行する

このドキュメント追跡の例に基づいて、ソートキーの高度なユースケースについて解説しましょう。ドキュメントのバージョン管理を実装するのに、同じ documentInfo ソートキーを利用できます。もう一度、テンプレート文字列で starts_with 演算子を使用して、ドキュメントの各バージョンのテーブルに項目を作成します。ソートキーの値は、次のテンプレートに従います : V_# ここで、# はバージョン ID またはドキュメントのバージョン番号です。ソートキー値 v_0 は、ドキュメントの最新バージョンを保存するために予約されており、この値は常に最後に追加されたドキュメントバージョンの重複行です。この例では、v_0v_2 と同じデータを保存します。v_2 が最新のドキュメントであるためです。

このパターンでは、次のスクリーンショットが示すように、begins_with(v_) で完全なドキュメント履歴を取得するのにクエリを実行できます。

このパターンでは、begins_with(v_) で完全なドキュメント履歴を取得するのにクエリを実行できる

まとめ

ソートキーを使用すると、DynamoDB のクエリ機能をより強力にすることが可能です。ただし、いくつか考慮する点があります。begins_with 演算子を活用する文字列テンプレートを使用し、最も具体的でないものから最も具体的なものへ配列することを忘れないでください。また、ソートキーには、検索を助けるために、metadatapermissions などの一意の任意の文字列値を保存できます。

より集中的なデータのクエリ、検索、および分析といったニーズがある場合は、他のサービスでも DynamoDB データをぜひ活用してください。例えば、Amazon DynamoDB Streams を使用して、ストリーミングデータAmazon Elasticsearch Service にロードできます。DynamoDB の強みは、Auto Scaling機能と、そのデータを格納できるシンプルなインターフェイスにあります。他にも、Amazon DynamoDB Accelerator (DAX) などの機能を備えた要求の厳しいアプリケーションに対する迅速な応答時間、さらにサーバーレスのアーキテクチャでのペアリングがあります。適切な方法で検討し計画するのであれば、データのクエリと取得は難しいことではありません。

 


今回のブログ投稿者について

AM Grobelny の写真AM Grobelny はワシントン州シアトルに拠点を置くシニアテクニカルエバンジェリストであり、あらゆる種類のソフトウェア開発者が AWS で成功できるようサポートを行っています。どの言語も好きとのことですが、C# と TypeScript が特に彼女の心をつかんで離しません。