DynamoDB でのセッション管理のスキーマ設計
セッション管理のビジネスユースケース
このユースケースでは、DynamoDB をセッションストアとして使用する方法について説明します。セッションストアを使用すると、ウェブブラウジングセッション中にデータを保存できます。DynamoDB では、高速かつスケーラブルな方法でセッション管理を分散できます。セットアップが簡単であり、個別のストアをローカルに管理せずに、シングルサインオンプラットフォームで共有できます。セッションストアの一般的なアクセスパターンは以下のとおりです。
-
お客様が開始したセッションのエントリの作成
-
お客様が開始したセッションの検索
-
セッションのすべての子セッションの一覧表示
-
お客様の最終ログイン時刻の取得
セッションデータの一般的な項目サイズは 1 KB 以内で、スループット管理戦略は以下のようなさまざまな要因によって異なります。
-
ユーザー数
-
1 秒あたりに作成されるセッションの数
-
ワークロードの季節性
新しいプラットフォームの場合、導入状況とユーザーベースは不明であるため、オンデマンドスループットモードから始めます。
セッション管理エンティティ関係図
次に示すのは、セッション管理のスキーマ設計に使用するエンティティ関係図 (ERD) です。

セッション管理アクセスパターン
これらは、セッション管理のスキーマ設計のために検討するアクセスパターンです。
-
createSession
-
getSessionBySessionId
-
expireSession
-
getChildSessionsBySessionId
-
getSessionByChildSessionId
-
getLastLoginTimeByCustomerId
-
getSessionIdByCustomerId
-
getSessionsByCustomerId
セッション管理のスキーマ設計
まず、キー属性に PK
や SK
などの汎用名を使用し、さまざまなタイプのエンティティを同じテーブルに保存して、モデルを将来にわたって使用できるようにします。読みやすくするために、データのタイプを示すプレフィックスを含めたり、entity_type
という任意の属性を含めたりできます。例えば、セッション UUID を c342etj3
として保存する代わりに、suuid#c342etj3
として保存したり、この項目に entity_type
という任意の属性を追加して session
を値としたりします。
ステップ 1: アクセスパターン 1 (createSession
)、2 (getSessionBySessionId
)、3 (expireSession
) に対処する
セッションエンティティは session_id
で識別し、各 session_id
が一意であるため、現時点ではソート基準の定義はありません。つまり、session_id
をパーティションキーとする基本スキーマにより、これら 3 つのアクセスパターンが実現されます。セッションの新規作成時は、親セッションも子セッションもありません。DynamoDB スキーマは柔軟性があるため、テーブルを作成した後でもキー以外の属性を追加、削除、変更できます。PutItem
の呼び出しを使用してセッションを作成できます。session_id
でセッションの詳細を取得するには、GetItem
操作で PK=suuid#c342etj3
を使用できます。同様に、DeleteItem
操作でキー条件 PK=suuid#c342etj3
を使用して、セッションを削除できます。これらの各操作では、キースキーマ全体を指定する必要があることに注意してください。ソートキーがある場合は、これらの各操作に正確なパーティションキーとソートキーの値を指定する必要があります。

ステップ 2: アクセスパターン 4 (getChildSessionsBySessionId
) に対処する
現在、セッションはフラットな非正規化構造になっています。このセッションで 3 つの新しい子セッションを作成する場合は、どうなるでしょうか。1 つのオプションとしては、次のように、子セッションのマップを親セッション項目に非キー属性として追加します。

アクセスパターン 4 (getChildSessionsBySessionId
) は、この設計により、GetItem
操作で PK=suuid#c342etj3
を使用することで簡単に対処できます。ただし、子セッションの UUID を使用して親セッションの詳細を検索するアクセスパターン 5 (getSessionByChildSessionId
) では、子セッションのマップをインデックス付けできないため、現在の状態ではテーブル全体の Scan 操作が必要になります。インデックスは、スカラー型の属性 (文字列、数値、バイナリ) に対してのみ作成できます。では、両方のアクセスパターンに対応できるようにスキーマを再設計するにはどうすればよいでしょうか。
ステップ 3: アクセスパターン 4 (getChildSessionsBySessionId
) と 5 (getSessionByChildSessionId
) に対処する
これを行うには、汎用属性を SK
とするソートキー列を設計に追加します。これで、SK
の値を子セッションの UUID にできるようになりました。1 対多マッピングにより、次に示すように、DynamoDB テーブルに項目コレクションが作成されます。

ステップ 4: 子セッションの UUID にサフィックスを追加して親と区別する
customer_id
、parent_session_state
、parent_access_token
、last_login_time
などの一部の属性は、親のすべての子セッションで変わらないことに注意してください。複数の項目ごとに情報を複製するよりも、これらの共通の属性を保存する別の項目を作成するほうが効率的です。また、access_token
属性と session_state
属性を使用して、親セッションの詳細と子セッションの詳細を保存できるようになります。これを行うには、子セッションの UUID 値ごとに「child」というサフィックスを追加して、親セッションやルートセッションと区別します。以降のアクセスパターンを考慮して、この別の項目のソートキー値として customer_id
を選択します。この決定については、後のステップで詳しく説明します。これで、データは次のようになります。

定数を複製するか、別の属性として保存するかは、以下の特定の要因によって決まります。
-
定数情報は変数と共にどのくらいの頻度で読み取られるか。頻度が高い場合は、読み取り効率を高めるために、項目間で属性を複製することをお勧めします。
-
共通の属性はどのくらいの頻度で更新されるか。頻度が高い場合は、書き込み効率を高めるために、これらの属性を別の項目として保存することをお勧めします。
1 つの項目を複数の論理的なサブ項目に分割するこの手法は、垂直パーティショニングと呼ばれます。これで、この設計により、query 操作で PK=suuid#c342etj3 and SK
beings_with “child#”
を使用してアクセスパターン 4 (getChildSessionsBySessionId
) を実行できるようになりました。以下のターゲット項目が返されます。

このスキーマを使用して、テーブルにさらにサンプルデータを追加してみましょう。

ステップ 5: アクセスパターン 6 (getLastLoginTimeByCustomerId
)、7 (getSessionIdByCustomerId
)、8 (getSessionsByCustomerId
) に対処する
これらのアクセスパターンでは、child_session_id
や customer_id
などの異なる検索条件に基づいて異なる属性を取得します。ステップ 3 のスキーマでは、2 つの異なるグローバルセカンダリインデックス (GSI) を作成する必要がありました。検索属性の child_session_id
と customer_id
ごとに 1 つずつです。ただし、検索条件ごとに 1 つの GSI というこの決定は、テーブルごとの GSI の数に制限があるため、将来にわたって保証されるものではありません。時間の経過とともにアクセスパターンを追加すると、そのたびに GSI の数が増えていきます。それでは、より良い設計手法は何でしょうか。
テーブルのキースキーマを入れ替えて GSI を作成するとどうなるか見てみましょう。つまり、テーブルのソートキーを GSI のパーティションキーにして、テーブルのパーティションキーを GSI のソートキーにします。

同じ GSI を使用して異なる条件で検索するこの手法は、GSI オーバーローディングと呼ばれます。このオーバーロードされた GSI が、以下のアクセスパターンにどのように対応するのか見てみましょう。
getSessionByChildSessionId: Query GSI with
SK=child#suuid#kljhfytf23

getLastLoginTimeByCustomerId: Query GSI with SK=c#ABC, limit 1
注記
GSI は重複した値を持つ可能性があるため、ここでは「Limit 1」を追加します。GSI は、ベーステーブルのようにキー属性値の一意性を強制しません。

getSessionIdByCustomerId: Query GSI with SK=c#ABC,
PK=suuid#d0004tj2

getSessionsByCustomerId: Query GSI with SK=c#ABC

複数の検索条件があるにもかかわらず、1つの GSI で 1 つのテーブルを使用することにより、すべてのアクセスパターンに対応できました。以下により、これを達成しました。
-
GSI オーバーローディングにより、特に条件が相互に排他的である場合に、異なる条件に基づいて同じインデックスを使用して検索できます。
-
垂直パーティショニングにより、ターゲットを絞った読み取りと更新が可能になります。
-
垂直パーティショニングの結果としてオーバーロードされたソートキーがある場合、オーバーロードされた GSI を作成しやすくなります。
アクセスパターン | ベーステーブル/GSI/LSI | 操作 | パーティションキー値 | ソートキー値 | その他の条件/フィルター |
---|---|---|---|---|---|
createSession | ベーステーブル | PutItem | PK=session_id | SK=customer_id | |
getSessionBySessionId | ベーステーブル | GetItem | PK=session_id | SK=customer_id | |
expireSession | ベーステーブル | DeleteItem | PK=session_id | SK=customer_id | |
getChildSessionsBySessionId | ベーステーブル | Query | PK=session_id | SK begins_with "child#" | |
getSessionByChildSessionId | GSI | Query | SK=child_session_id | ||
getLastLoginTimeByCustomerId | GSI | Query | SK=customer_id | Limit 1 | |
getSessionIdByCustomerId | GSI | Query | SK=customer_id | PK=session_id | |
getSessionsByCustomerId | GSI | Query | SK=customer_id |
セッション管理の最終スキーマ
ベーステーブルと GSI の最終的なスキーマ設計は以下のとおりです。このスキーマ設計を JSON ファイルとしてダウンロードするには、GitHub の DynamoDB の例
ベーステーブル:

GSI:

このスキーマ設計での NoSQL Workbench の使用
この最終スキーマを、DynamoDB のデータモデリング、データ視覚化、クエリ開発機能を提供するビジュアルツールである NoSQL Workbench にインポートして、新しいプロジェクトを詳しく調べたり編集したりできます。使用を開始するには、次の手順に従います。
-
NoSQL Workbench をダウンロードします。詳細については、「DynamoDB 用の NoSQL Workbench のダウンロード」を参照してください。
-
上記の JSON スキーマファイルをダウンロードします。このファイルは既に NoSQL Workbench モデル形式になっています。
-
JSON スキーマファイルを NoSQL Workbench にインポートします。詳細については、「既存のデータモデルのインポート」を参照してください。
-
NOSQL Workbench にインポートしたら、データモデルを編集できます。詳細については、「既存のデータモデルの編集」を参照してください。
-
データモデルの視覚化、サンプルデータの追加、CSV ファイルからのサンプルデータのインポートを行うには、NoSQL Workbench のデータビジュアライザー機能を使用します。