DynamoDBでUNIQUE制約を実現する
構造化されたデータをDBで管理する場合、プライマリキーだけではなく特定のカラムの値で一意性を保証したくなるケースは存在するだろう。RDBMSではカラム単位でUNIQUE 制約を設定することが可能だが、DynamoDBでは標準的な使い方ではUNIQUE 制約を実現するのは困難である。
これまでに何度か、どうしてもDynamoDBでUNIQUE 制約を必要とするケースはあったが、アプリケーションロジックで回避していた。対象のカラムにIndexを張って、事前にQueryして値の存在確認をする、などの方法を取っていた。しかしこの方法では、タイミングによっては重複が発生し得るため、Integrity Check Toolを別途用意し、重複チェックを定期的に行うなどの苦し紛れの方法で対応していた。
そんな中でDynamoDBにトランザクションの機能がリリースされ、トランザクションを使ったUNIQUE制約の実現を示した下記の記事は目からウロコだった。
AWSブログより
仕組みとしては非常にシンプルで、例として貼ってあったテーブルを下記に引用する。これを見るとどのようにUNIQUE制約を付けているのかは一目瞭然だ。
table: User Table (Sample)
pk userName email fullName phoneNumber
b201c1f2-238e btables bobby.tables@gmail.com Bobby Tables +1-202-555-0124
userName#btables
email#bobby.tables@gmail.com
8ec436a8-97e6 jsmith johnsmith@yahoo.com John Smith +1-404-555-9325
userName#jsmith
email#johnsmith@yahoo.com
eed78b78 phonork pphonork@calpoly.edu Peter Phonorkus +1-805-555-0820
userName#phonork
email#pphonork@calpoly.edu
このテーブルでは、userNameとemailがUNIQUE制約になっている。DynamoDBではプライマリキーの重複は許容されないため、userNameとemailのレコードを作ってしまえば重複回避ができるのだ。
これが実現できるようになったのはDynamoDBでトランザクションがサポートされたからだ。つまり、ユーザーのレコード作成時に、同時にuserNameとemailのレコードを作成できるため、確実に整合性担保ができるようになった。
単一テーブルで実現可能だが、複数テーブルに分けて管理することも当然ながら可能ではある。各自のポリシーに従うべし。
(疑問点)
シャーディングなどを考慮するとできるだけプライマリキーの値は分散させたほうが良いので、重複管理のためだけであればuserName#btablesのような値をそのまま使うのではなく、例えばHash化した値を使った方が良い気がするがどうなんだろう。ただしコードの複雑度の増加、運用の際の考慮点が増えるなどの問題点が考えられるので悩ましい話ではある。
(追記)
むしろ関連付けられたキーはパーティションが偏るようにキー名を設計したほうがいいかもしれないと思った。DynamoDBのシャード分割が完全にKey名で分割されていると仮定した場合、例えば ${userName} と ${userName}#${email}のような形式でレコードを生成しておくことで、これらのデータが物理的に同じシャードで管理されてトランザクション処理の際のパフォーマンスが有利になるのでは。考え方としては Cloud Spanner のインターリーブを簡易的に実現するようなものか。