Amazon Web Services ブログ

トランザクションを使用した Amazon DynamoDB の一意制約のシミュレーション

大抵のリレーショナルデータベースシステム、そして一部の非リレーショナルデータベースシステムには、ユニークキーまたはユニーク制約として知られるコンストラクトがあります。この機能は、列またはフィールド内のすべての値が行全体で一意であることを確実にします。

たとえば、User テーブルがあるとします。それには、各ユーザーを一意に識別するプライマリキーとして UUID があるかもしれませんが、同じくユーザーにとって一意である必要があるユーザー名フィールドと E メールフィールド (DynamoDB 用語では「属性」) もあるかもしれません。このユースケースは、DynamoDB トランザクションに関する AWS Summit 2018 DAT374 セッションで言及されています。

Amazon DynamoDB では、プライマリキーがパーティションキー (テーブルにソートキーが選択されていない場合)、またはパーティションとソートキーの組み合わせのいずれかになります。プライマリキーは、テーブル内で一意であることが保証されています。しかし、DynamoDB には、プライマリキーではない属性の一意性を確実にするための組み込みメカニズムがありません。

この記事では、アプリケーション側からこのような類の一意性を実装するために使用されるパターンについて説明し、単一テーブルのスキーマ設計でこのパターンを使用するときに項目を作成、更新、および削除する方法の例をご紹介します。

ソリューションの概要

前述の例を使用して、User テーブルがあり、そのテーブルに以下のような属性があると考えてください。

  • pk (UUID として保存されたプライマリキー)
  • userName
  • email
  • fullName
  • phoneNumber

UUIDuserName、および email の各属性は一意である必要がありますが、fullNamephoneNumber にその必要はありません。複数の人物が同じ自宅電話番号を共有することができます。以下はサンプル行です。

pk userName email fullName phoneNumber
b201c1f2-238e-461f-88e6-0e606fbc3c51 btables bobby.tables@gmail.com Bobby Tables +1-202-555-0124
8ec436a8-97e6-4e72-aec2-b47668e96a94 jsmith johnsmith@yahoo.com John Smith +1-404-555-9325
eed78b78-29f9-4893-a432-4c4f50b0d1c4 phonork pphonork Peter Phonorkus +1-805-555-0820

DynamoDB は pk 属性が一意であることをすでに保証しているので、userNameemail 属性も一意であることを確実にするメカニズムが必要です。

これを行うには、同じテーブルに追加の項目を挿入し、pk 属性はハッシュ記号で区切った属性名と項目の値に設定します。新しいテーブルは以下の例のようになります。

pk userName email fullName phoneNumber
b201c1f2-238e-461f-88e6-0e606fbc3c51 btables bobby.tables@gmail.com Bobby Tables +1-202-555-0124
userName#btables
email#bobby.tables@gmail.com
8ec436a8-97e6-4e72-aec2-b47668e96a94 jsmith johnsmith@yahoo.com John Smith +1-404-555-9325
userName#jsmith
email#johnsmith@yahoo.com
eed78b78-29f9-4893-a432-4c4f50b0d1c4 phonork pphonork@calpoly.edu Peter Phonorkus +1-805-555-0820
userName#phonork
email#pphonork@calpoly.edu

新しい項目をテーブルに挿入するたびに、他の 2 つの項目も挿入する必要があります。これによって、userNameemail 属性の一意性が保証されます。同様に、項目を削除する場合も、対応する他の 2 つの項目を削除する必要があります。

最後に、一意の属性のひとつを変更する場合 (ユーザーがアカウントで E メールアドレスを変更したいときなど)、関連する一意の項目も更新する必要があります。これらの変更のすべてが DynamoDB トランザクションに共にバインドされ、それらのすべてが一斉に成功する、または失敗するようにします。

コマンドライン例

次に、この設計を実装するためのコマンドライン例を見ていきます。これらの CLI 例は、使用したいプログラミング言語のために、任意の DynamoDB SDK に簡単にポーティングすることができます。

User テーブルがあり、それが空であるとしましょう。トランザクションの一部としてユーザーの 3 つの行を挿入することによって最初のユーザーを登録し、冪等性を確保するためのトランザクション識別子を使用します。 このトランザクション識別子 (クライアントリクエストトークン) は、再起動または再開されたアプリケーションの場合などに、同一のトランザクションを複数回送信しても、同じ結果を得ることができるようにするものです。

aws dynamodb transact-write-items --client-request-token TRANSACTION1 --transact-items '[
  {
    "Put": {
      "TableName" : "User",
      "ConditionExpression": "attribute_not_exists(pk)",
        "Item" : {
          "pk":{"S":"b201c1f2-238e-461f-88e6-0e606fbc3c51"},
          "userName":{"S":"btables"},
          "email":{"S":"bobby.tables@gmail.com"},
          "fullName":{"S":"Bobby Tables"},
          "phoneNumber":{"S":"+1-202-555-0124"}
       }
    }
},
  {
    "Put": {
      "TableName" : "User",
      "ConditionExpression": "attribute_not_exists(pk)",
      "Item" : {
        "pk":{"S":"userName#btables"}
       }
    }
},
  {
    "Put": {
      "TableName" : "User",
      "ConditionExpression": "attribute_not_exists(pk)",
      "Item" : {
        "pk":{"S":"email#bobby.tables@gmail.com"}
       }
    }
}
]'

ここで項目をリストすると、3 つの項目すべてが作成されたことがわかります。

aws dynamodb scan --table-name User
{
    "Count": 3,
    "Items": [
        {
            "userName": {
                "S": "btables"
            },
            "pk": {
                "S": "b201c1f2-238e-461f-88e6-0e606fbc3c51"
            },
            "fullName": {
                "S": "Bobby Tables"
            },
            "phoneNumber": {
                "S": "+1-202-555-0124"
            },
            "email": {
                "S": "bobby.tables@gmail.com"
            }
        },
        {
            "pk": {
                "S": "email#bobby.tables@gmail.com"
            }
        },
        {
            "pk": {
                "S": "userName#btables"
            }
        }
    ],
    "ScannedCount": 3,
    "ConsumedCapacity": null
}

以下は、偽の Bobby Tables が同じ E メールアドレスを使ってサインアップしようとする場合に起こります。

aws dynamodb transact-write-items --client-request-token TRANSACTION2 --transact-items '[
   {
     "Put": {
       "TableName" : "User",
       "ConditionExpression": "attribute_not_exists(pk)",
         "Item" : {
           "pk":{"S":"8ec436a8-97e6-4e72-aec2-b47668e96a94"},
           "userName":{"S":"caulfield"},
           "email":{"S":"bobby.tables@gmail.com"},
           "fullName":{"S":"Phony Bobby Tables"},
           "phoneNumber":{"S":"+1-202-555-0124"}
        }
     }
 },
   {
     "Put": {
       "TableName" : "User",
       "ConditionExpression": "attribute_not_exists(pk)",
       "Item" : {
         "pk":{"S":"userName#caulfield"}
        }
     }
 },
   {
     "Put": {
       "TableName" : "User",
       "ConditionExpression": "attribute_not_exists(pk)",
       "Item" : {
         "pk":{"S":"email#bobby.tables@gmail.com"}
        }
     }
  }
]'

An error occurred (TransactionCanceledException) when calling the TransactWriteItems operation: Transaction cancelled, please refer cancellation reasons for specific reasons [None, None, ConditionalCheckFailed]

ご覧のとおり、「email#bobby.tables@gmail.com」の pk 値がある項目がすでに存在するため、このトランザクションの 3 番目の要素が ConditionalCheckFailed エラーを受け、トランザクションが失敗します。

Bobby Tables がバニティドメインを登録して、保存された E メールアドレスを変更したい場合は、2 つの項目だけを変更しなければなりません。しかし、DynamoDB は項目のプライマリキーの変更を許可しないため、一意の email 項目を削除し、新しい E メールを持つ別の項目を挿入する必要があります。

aws dynamodb transact-write-items --client-request-token TRANSACTION3 --transact-items '[
  {
    "Update": {
      "TableName" : "User",
      "Key" : {"pk":{"S":"b201c1f2-238e-461f-88e6-0e606fbc3c51"}},
      "UpdateExpression":"SET email = :email",
      "ExpressionAttributeValues":{":email":{"S":"bobby@tables.com"}}
    }
  },
    {
    "Delete": {
      "TableName" : "User",
      "Key" : {"pk":{"S":"email#bobby.tables@gmail.com"}}
    }
  },
  {
    "Put": {
      "TableName" : "User",
      "ConditionExpression": "attribute_not_exists(pk)",
      "Item" : {
        "pk":{"S":"email#bobby@tables.com"}
       }
    }
}
]'

スキャンすると、目的の変更が実行されたことがわかります。

aws dynamodb scan --table-name User
{
    "Count": 3,
    "Items": [
        {
            "userName": {
                "S": "btables"
            },
            "pk": {
                "S": "b201c1f2-238e-461f-88e6-0e606fbc3c51"
            },
            "fullName": {
                "S": "Bobby Tables"
            },
            "phoneNumber": {
                "S": "+1-202-555-0124"
            },
            "email": {
                "S": "bobby@tables.com"
            }
        },
        {
            "pk": {
                "S": "userName#btables"
            }
        },
        {
            "pk": {
                "S": "email#bobby@tables.com"
            }
        }
    ],
    "ScannedCount": 3,
    "ConsumedCapacity": null
}

同様に、Bobby がインターネットから彼自身を削除したい場合も、ユーザーに関連する 3 つの行のすべてを削除する必要があります。

aws dynamodb transact-write-items --client-request-token TRANSACTION4 --transact-items '[
  {
    "Delete": {
      "TableName" : "User",
      "Key" : {"pk":{"S":"b201c1f2-238e-461f-88e6-0e606fbc3c51"}}
    }
  },
  {
    "Delete": {
      "TableName" : "User",
      "Key" : {"pk":{"S":"userName#btables"}}
    }
  },
  {
    "Delete": {
      "TableName" : "User",
      "Key" : {"pk":{"S":"email#bobby@tables.com"}}
    }
  }
]'

最後のスキャンでは、テーブルが空になったことがわかります。

scan —table-name User
{
    "Count": 0,
    "Items": [],
    "ScannedCount": 0,
    "ConsumedCapacity": null
}

まとめ

このパターンは、リレーショナルデータベースからの移行を行っており、DynamoDB に一意性制約を維持しなければならない場合に役に立つでしょう。単一のトランザクションで複数のテーブルを変更できるのため、2 つのテーブルを使って同じパターンを実装することも可能です。「メインデータ」は User テーブル内に置いておき、特定の属性の一意性を確保する目的のみに使用される 2 番目のテーブルを準備します。

DynamoDB では可能な限り常に単一テーブルの設計概念を維持しようとしていますが、このパターンには 2 つのテーブルを使うほうが安心するという場合は、ぜひそうしてください!

 


著者について

Chad Tindel はニューヨーク勤務の DynamoDB スペシャリストソリューションアーキテクトです。Chad は大企業のお客様と連携して DynamoDB ベースのソリューションの評価、設計、およびデプロイメントを行っています。Amazon に入社する前は、Red Hat、Cloudera、MongoDB、および Elastic で同じような役割を担っていました。

 

 

 

Brett Hensley は AWS Solutions Architecture チームの一員で、このチームは、州、市、および国レベルの政府機関と共に、さまざまな政府のテクノロジーパートナーである GovTech が含まれる SLG (State and Local Government) に焦点を当てています。  AWS への入社前、Brett は Kimball Electronics、Maxim Group、BMG Columbia House、および Hearst Health の子会社である FDB (First DataBank) などの企業において、技術面での役割を多数担ってきました。  この業界で製造、小売り、ヘルスケア、および政府をサポートしながら 20 年以上を費やしてきた Brett は、お客様がそのビジネス、カスタマー、そして有権者により良いサービスを提供すべく、多種多様な技術、文化、および手続き上の課題に対応するお手伝いをしています。