📑

DynamoDB の設計とクエリ

2023/03/06に公開

DynamoDB がとても難しかったので軽いまとめと備忘録

大事なこと

テーブル設計をするとき、まずは目的や用途から考える

スキャンではなくクエリを使う

実際にやったもの

各メンバーの SR の ID とその情報を DynamoDB に保存

使用用途

  • 特定期間(最大1か月)以内に各メンバーにアサインされた SR 情報の取得
  • SR アサイン時にその SR がリアサインかの確認(リアサインの場合は前アサイン者の名前を取得)

各設定(キー/インデックス)

プライマリキー

パーテーションキー:メンバー名
ソートキー:アサイン時間

インデックス

グローバルセカンダリインデックス:SR の ID
ローカルセカンダリインデックス:なし

はまりポイント

DynamoDB のキーは Datetime 型に対応していないので String にする必要がある
数値型にしてエポック時間で登録することでも対応可能だがコード側で処理を変えなければいけなかったので今回は Datetime 型を使用しました
※ただ特定フォーマットであればソートすることが可能

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html

良くまとめてくださっている記事

https://mseeeen.msen.jp/dynamo-datetime/

自分がはまった個所

ソートキーに使用するアサイン時間にタイムゾーンを含めてしまっていた
DynamoDB 的には問題なくソートできたのですが、コード側でタイムゾーンが入っていると処理がめんどくさくなることに気づいてませんでした。。。

既存ではすでに 2000 エントリくらい登録されていたので手動で更新するのは現実的ではなかったため、SDK を使用し対応しました。
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html

※最初はアップデートアイテムで修正しようとしたのですが、ソートキーで使用していたためうまくいかなかったです(私のコードが悪かったかもですが)
 なので一度スキャンを行いすべてのアイテムを取得し、タイムゾーンを削除した新しい値でアイテムの再作成し終わった後に既存の誤った値をすべてDeleteするという対応をしました。。。。
 もっとスマートな対応をしたかったですが一度だけでいいので Lambda でサクッとやりました(Lambdaはほんとに便利)

2023/03/07:追記
"%z"付ければよかっただけ説が濃厚に。。。
%Y-%m-%dT%H:%M:%S.%f%z

キーワードのおさらい

機能系

プライマリキー

データを一意に識別するためのキーで、「パーティションキー」または「パーティションキーとソートキーの複合キー」のこと

パーテーションキー

データをどのパーティションに配置するか決定する。
各パーティションへのアクセスがなるべく均一になるようパーティションキーを設計すると良い

ソートキー

ソートキーによってデータはパーティション内で並べ替えられて物理的に近くなるように配置される
QueryAPIではソートキーを指定して取り出すデータの範囲をフィルタ可能
ソートキーの設定は任意

GSI (グローバルセカンダリインデックス)

ベースのテーブル上で異なるパーティションキー・ソートキーのテーブルを作成する仕組み
保持する情報も選択可能
キャパシティユニットも GSI 用に確保

LSI (ローカルセカンダリインデックス)

ベースのテーブル上でパーテーションキーは同じもののままで異なるソートキーのテーブルを作成する仕組み
保持する情報も選択可能
テーブル作成時のみ作成可能

GSI vs LSI

GSI LSI
新しく設定するキー パーティションキー、ソートキー(任意) ソートキー
キーの重複
インデックス数の上限 5 5
項目コレクションのサイズ制限 なし 10GB以下
テーブル作成後の追加 不可
読み込み整合性 結果整合性 結果整合性、強い整合性
キャパシティユニットの消費 GSIから消費 ベーステーブルから消費

関連ワード

クエリ

パーテンションキーを使用して特定のデータを取得すること
ソートキーを使用することで取得範囲のフィルターが可能

スキャン

テーブルのすべてのデータを取得すること

キャパシティユニット

読み込み・書き込み処理が発生した際に使用されるものの
GSI は独自のものを持ち、LSI はベースのテーブルとキャパシティユニットを共有している

オンデマンドモードとプロビジョニングモードがある
※モードは切り替え可能
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html

オンデマンドモード

従量課金制(使用した分のみの課金)
新しくテーブルを作る際や、アプリケーションのトラフィックが予測できない場合に使用が勧められる

使用されるキャパシティユニットの詳細

読み込み
  • 4 KB 以下の項目の強力な整合性のある読み込みリクエストには、1 つの読み込みリクエストユニット
  • 4 KB 以下の項目の結果整合性のある読み込みリクエストには、2 分の 1 の読み込みリクエストユニット
  • 4 KB 以下の項目のトランザクション読み込みリクエストには、2 つの読み込みリクエストユニット
書き込み

最大サイズが 1 KB の項目について 1 回の書き込みを表す。
※1 KB より大きい項目を書き込む必要がある場合、追加の書き込みリクエストユニットを消費する
トランザクション書き込みリクエストでは、1 KB までの項目を 1 回書き込むのに書き込みリクエストユニットが 2 個必要

プロビジョニングモード

事前に 1 秒あたりの読み込みと書き込みの回数を指定する
使用され方が予測出来る場合に推奨
※Auto Scaling を使用することでトラフィックの変更に応じて自動調整可能

使用されるキャパシティユニットの詳細

読み込み

1 つの読み込み容量単位は、最大サイズ 4 KB の項目について、1 秒あたり 1 回の強力な整合性のある読み込み、あるいは 1 秒あたり 2 回の結果整合性のある読み込みを表す。
トランザクション読み込みリクエストでは、4 KB までの項目を 1 秒あたりに 1 回読み込むのに読み込み容量単位が 2 個必要。4 KB より大きい項目を読み込む必要がある場合、DynamoDB は追加の読み込み容量単位を消費する必要があります。

例:
項目のサイズが 8 KB の場合、1 秒あたり 1 回の強力な整合性のある読み込みを維持するには読み込みキャパシティーユニットが 2 個、結果整合性のある読み込みを選択した場合は読み込みキャパシティーユニットが 1 個、またはトランザクション読み込みリクエストには読み込みキャパシティーユニットが 4 個必要

書き込み

最大サイズが 1 KB の項目について 1 回の書き込みを表す。
※1 KB より大きい項目を書き込む必要がある場合、追加の書き込みリクエストユニットを消費する
トランザクション書き込みリクエストでは、1 KB までの項目を 1 回書き込むのに書き込みリクエストユニットが 2 個必要

DynamoDB Auto Scaling

ベーステーブルと GSI のスループット容量を自動管理する
※コンソール上から GSI を作成するとデフォルトで有効になる
読み込みおよび書き込みのキャパシティーユニットの範囲 (上限と下限) と、その範囲内での目標使用率を定義し、アプリケーションのワークロードが増減しても、ターゲットの使用率が維持されるようにする

リザーブドキャパシティ

前払いすることで標準テーブルクラスの場合割引を受けられるもの
※書き込み、読み込み両方が対象

注意点:

  • レプリケーションされた書き込みキャパシティユニットでは使用できない
  • 購入したリージョン限定
  • 標準-IAテーブルクラスやオンデマンドモードでは使用できない

読み込み整合性

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html

結果整合性

最新の書き込みが反映されていなかったり、古いデータを読み取ることがある
時間がたってからリクエストすることで最新のデータが返される
デフォルトの動作
→ConsistentRead を True にすることで強い整合性のある読み込みとなる

強い整合性

成功した以前のすべての書き込みオペレーションからの更新が反映された最新データの応答を返す

注意点:

  • GSI ではサポートされていない
  • 多くのスループット容量が使用される

DAX (DynamoDB Accelerator)

DynamoDB で使用できるキャッシュサービス
以下が使用用途(マイクロ秒の応答時間が必要な場合)

  • インメモリキャッシュとしての DAX は、1 桁台のミリ秒単位からマイクロ秒単位まで、結果整合性のある読み込みワークロードの応答時間を短縮する
  • DAX は、DynamoDB と API の互換性のあるマネージドサービスを提供することにより、オペレーションとアプリケーションの複雑さを軽減します。したがって、既存のアプリケーションで使用するために必要なのは最小限の機能変更だけとなる
  • 読み込みの多いワークロードや急激に増大するワークロードにおいて、DAX はスループットを強化することや、読み込み容量ユニットを必要以上にプロビジョニングしないようにすることで運用コストの節約を可能にします。個々のキーで繰り返し読み込みが必要なアプリケーションにおいては特にメリットがある

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html

今後検討したいもの

キャパシティユニットの最適化

初期はどれくらいのキャパシティが必要になるかわかっていなかったためオンデマンドモードにしていたのですが、ある程度データが取れた後はコストの削減のためにプロビジョンドモードに変更したいと考えています
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/switching.capacitymode.html

アイテムの S3 へのバックアップ

現在はバックアップや、古いデータの処理が設定できていないので特定日数が過ぎたアイテムについては S3 へバックアップされるようにしたいと考えています
https://aws.amazon.com/jp/premiumsupport/knowledge-center/back-up-dynamodb-s3/

クエリの高速化

現状は必要ないですが今後もしクエリスピードに問題が出てきてしまったり、今以上のスピードが必要になった場合は DAX を追加設定するのもありかなと思います(コストとの兼ね合いでむずかしいかもですが)
https://aws.amazon.com/jp/dynamodb/dax/#:~:text=Amazon DynamoDB Accelerator (DAX) は,向上を実現します。

Discussion