中級読了 2 分

変化する(ミュータブルな)属性でDynamoDBをソートする

ある属性を中心にソートキーを設計し、その順序でアイテムをクエリできるようにする — そして その属性が変化します。チケットのステータス、注文の状態、タスクの優先度。ここでDynamoDBが 突きつける落とし穴があります:キー属性をその場で更新することはできません。 プライマリキーはアイテムの生涯を通じて不変です。キーの一部である値を変更することは、 アイテムを編集することではなく、移動することであり、DynamoDBはそれを明示的に行わせます。

DynamoDBのソートキーは変更できますか?

いいえ。ソートキーはプライマリキーの一部であり、DynamoDBのキー属性は不変です — UpdateItemでパーティションキーやソートキーの値を編集することはできず、「アイテムを移動する」操作も存在しません。変更するには、古いアイテムを削除して新しいアイテムをputするか、変動する値をベーステーブルのキーではなくGSIのソートキーに置く方法を検討してください。

  • キー属性は不変。 パーティションキーやソートキーの値をUpdateItemすることはできません — DynamoDBに「アイテムを移動する」操作はありません。
  • キーの値を変更するには、古いアイテムを削除して新しいものをputする — アトミックに なるよう、理想的にはトランザクションの中で。
  • より良い方法:変動する値をベーステーブルのキーから外し、代わりに GSIのソートキーに置く — GSIのキーは変更できます。ベースの アイテムを更新すれば、インデックスのエントリが再伝播されるだけだからです。
  • 変化しないソートキーを選ぶ(タイムスタンプ、不変のid)。アクセスパターンが許す限り。

問題:ソートに使いたいのに変わり続けるステータス

サポートデスクを運用していて、チームのチケットをステータス順に一覧したいので、ステータスを ソートキーに入れるとします:

PK: TEAM#7   SK: STATUS#open#TICKET#8842

ここでチケットがpendingに移ります。ソートキーをSTATUS#pending#TICKET#8842UpdateItemしたいところですが — DynamoDBはキー属性を変更するあらゆる書き込みを拒否 します。キーはアイテムのアドレスです。アドレスをその場で編集することはできません。 ソートに使うために選んだステータスが、まさにじっとしていないものなのです。

選択肢1:削除して作り直す(アトミックに)

値がベーステーブルのキーに存在しなければならない場合、それを変更するということは、古い アイテムを削除して新しいものを書き込むことを意味します:

1. DeleteItem  PK=TEAM#7  SK=STATUS#open#TICKET#8842
2. PutItem     PK=TEAM#7  SK=STATUS#pending#TICKET#8842  (same attributes)

削除とputが両方成功するか両方失敗するように、 TransactWriteItemsの中で行いましょう — さもないと、 その間にクラッシュが起きるとチケットを失うか重複させてしまいます。これは機能しますが、 ステータス変更ごとに2回の書き込みとトランザクションが必要になります。たまの変更なら 問題ありませんが、頻繁なものにはコストがかかります。

選択肢2:ミュータブルな値をベースキーから外す(推奨)

よりすっきりした設計:ベーステーブルのキーを不変なもの(チケットのid)にし、変動する ソート可能な値をGSIのソートキーに置きます。

Base:  PK: TICKET#8842   status: "open"   teamId: TEAM#7
GSI:   GSI1PK: TEAM#7    GSI1SK: STATUS#open#TICKET#8842

これでステータスの変更は、ベースアイテムのstatus属性に対する単純なUpdateItemに なります — statusはベーステーブルのキーではないので、DynamoDBはこれを許可します。 するとDynamoDBはGSIのエントリを新しいソート位置へ自動的に再伝播します。書き込みは 1回、トランザクションも削除の手間もありません。

はいいいえ、GSIのソートキーステータスが open から pendingに変わる値はベーステーブルのキーにあるか?トランザクションで削除して作り直す単純なUpdateItem。GSIが再伝播する

トレードオフ:GSIは結果整合性であり、追加の ストレージ/書き込みコストがかかります — しかし頻繁に変わる値については、変更のたびに削除 して作り直すよりはるかに安上がりです。

DynoTableでのキー設計

ベースの読み取りとGSIの読み取りの両方について、 DynamoDB式ビルダーでキー条件を構築してプレビューします。

DynoTableでは、クエリがどのインデックスを通るかを選び、変動する値がGSIでソートされる様子を 確認しながら、ベースアイテムが不変のキーを保つ — 両方の読み取りを実データ上で並べて確認できます。

DynoTableで、ベースアイテムが不変のキーを保ちつつ、ステータスでソートされたGSIをクエリしているところ。
DynoTableで、ベースアイテムが不変のキーを保ちつつ、ステータスでソートされたGSIをクエリしているところ。

落とし穴と次のステップ

  • キー属性をUpdateItemしようとしてはいけない — 拒否されます。キーの値はアイテムの 生涯にわたり固定です。
  • どうしても移動が必要なら、トランザクションで削除+putを行う — ガードのない2回の 書き込みとして行ってはいけません。
  • ソートしかつ変更する属性には、不変のベースキー + GSIを優先する。
  • GSIの結果整合性を忘れない — 並べ替えられたエントリは、わずかな伝播遅延のあとに 現れます。
  • 関連: ソートキー戦略GSIとLSIトランザクション

ミュータブルな属性がGSIとベーステーブルでどうソートされるかを見てみたいですか? DynoTableをダウンロードして、インデックスを直接探索してください。

更新日