[レポート] Dive deep into Amazon DynamoDB #DAT330 #AWSreInvent

[レポート] Dive deep into Amazon DynamoDB #DAT330 #AWSreInvent

Clock Icon2023.12.01

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

re:Invent 2023にて「DAT330 Dive deep into Amazon DynamoDB」を聴講したので以下のとおりレポートします。

セッション概要

概要

This session provides a deep dive into Amazon DynamoDB, and it shares insights into how DynamoDB is architected to deliver the scalability and response times that customers have come to expect. Learn how popular capabilities like on-demand capacity, global tables, DynamoDB Streams, and transactions work and how you can best take advantage of them in your workloads.

このセッションでは、Amazon DynamoDBを深く掘り下げ、お客様が期待するスケーラビリティとレイテンシーを実現するためにDynamoDBがどのように設計されているかについての洞察を共有します。オンデマンドキャパシティ、グローバルテーブル、DynamoDB Streams、トランザクションなどの一般的な機能がどのように動作し、どのようにワークロードで活用できるかを学びます。

スピーカー

  • Amrith Kumar

レポート

「DynamoDBに移行する予定はありますか?」と質問するが、多くの場合、DynamoDBがトラフィックを処理できるかどうかを確信していない。もしDynamoDBがトラフィックを処理できるかどうか疑問に思っているなら、以下のいくつかの数字を見てほしい。DynamoDBはフルマネージドでサーバーレスです。つまり、ユーザーがDynamoDBにワークロードを持ち込むと、あなたのデータはAWS最大の顧客と同じ場所に保存されます。あなたのリクエストは、それらのリクエストを処理している同じハードウェアによって処理されます。

  • 一秒あたり50万回
  • 一時間あたり何百億回

このような規模で運用していることを意味する。一秒あたり1000リクエストを行っているか、一秒あたり100万リクエストを行っているかに関係なく、予測可能なレイテンシーを提供ができると考えている。

キャパシティモード

最初のトピックはキャパシティモードについての3つの質問

  • DynamoDBはどのようにスケールするのか?
  • オンデマンドは実際にどのように機能するのか?
  • DynamoDBのレート制限は実際にどのように機能するのか、そしてどのオンデマンドが許可されるべきなのか?

DynamoDBはどのようにスケールするのか?

DynamoDBはデータをテーブルに保存する。テーブルを作成するとき、ユーザーに3つのことを尋ねる。

  • テーブルの名前は何か?
  • プライマリキーは何か?
  • キャパシティモードはオンデマンドかプロビジョンドか

DynamoDBは、パーティションキーから暗号化ハッシュを計算し、データを複数のパーティションに分散します。各パーティションを約10ギガバイトに保つように努められます。データをパーティションに分散し、水平スケールすることで各パーティションが一秒あたり50万リクエストの一部を処理します。また、可用性と耐久性のために、異なる場所にデータの複数のコピーを保存します。

DynamoDBはサーバーレスですが、実際のデータはノードに存在しており、ホストの数は有限です。したがって、制限は存在します。しかし、現実的には、その数は非常に大きいので、あなたはそれを心配する必要はありません。一秒あたり50万リクエスト、それらの何百ものリクエストがあっても、200TBのデータを持つ場合でも、簡単に処理することができます。

どのような大規模なデータ、リクエストでも、それらを10GBの小さなパーティションに分割し、たくさんの数で処理します。

オンデマンドが実際にどのように機能しますか?

オンデマンドが実際にどのように機能するかを理解するには、レート制限がどのように機能するかを理解する必要があります。以下の図ではアプリケーションのインスタンスから、リージョナルエンドポイントに接続を確立しています。ここがリクエストルーターになります。そして、ストレージノードがデータを実際に保存している場所です。

アプリケーションからリクエストが受け取った際、リクエストルーターは最初に認証し、許可されているリクエスト数内であるかどうかを確認します。プロビジョンドモードのテーブルの場合、その制限はユーザーが設定するものです。オンデマンドテーブルの場合、その制限はユーザー側が受けるものです。そして、そのリクエストを処理する予定であるかを確認し、その後、リクエストをストレージノードに送り、ストレージノードはパーティションレベルの制御を行います。

ストレージノードのメモリは全員で共有されます。大きなテーブルと小さなテーブルを持つユーザーと一緒になります。各パーティションはストレージノードのキャパシティの小さなスライスを取得します。そこで、DynamoDBはガードレールを設置しています。これは、ユーザーのレイテンシー、全員のレイテンシーが予測可能であることを確認するためです。それはミリ秒単位でのレイテンシーです。

リクエストルーターによるテーブルレベル、各パーティションによるストレージノードレベル、二つの制限があります。

これらのレート制限を行うことは、数万のリクエストパラメータがある場合は難しいことです。DynamoDBはそれをGAC(Global Admission Control)というメカニズムを使って行っています。GACは、高度に分散したレート制限システムであり、数万のリクエストルータがあります。リクエストが受け取られるたびに、そのリクエストを処理すべきかどうかを検証します。そして、ストレージノードのガードレールと一緒に、全員が予測可能な応答を得ることを確認します。

GACの実際の動作を見てみましょう。GACはテーブルレベルのトークンバケットを維持します。トークンバケットは非常にシンプルで効率的なレート制限アルゴリズムです。それはどのレートを強制したいかによって、一定のレートでバケットにトークンを追加するという方法で動作します。リクエストを処理したいとき、バケットにまだトークンがあるかどうかを確認します。バケットにトークンがなければ、リクエストは処理されません。バケットにトークンがあれば、トークンを取り出し、リクエストを処理を進めます。

GACはテーブルレベルのトークンバケットを維持しますが、それは非常に困難です。リクエストが一秒あたり50万回、一時間あたり何十億回も発生するたびに、リクエストルータがGACにアクセスしてリクエストを処理できるかどうかを確認することは非常に困難です。その代わり、リクエストルータはバーチャルトークンを維持します。

トークンはGACによって一定のレートでトークンバケットに追加されます。それらはある一定のポイントまで溜まります。そして、アプリケーションがリクエストを持ってきたとき、そのリクエストはリクエストルータに届きます。

リクエストルーターがトークンを持っていない場合、GACに行き、1つのトークンではなくスタック(いくつかのトークンの集合体?)を手に入れます。トークンはそのうちの1つを使い、リクエストに応答し、完了となります。さて、このアプリケーションは多数のリクエストルータに接続している。やがて、トークンを持つリクエストルータがたくさん出てきます。定期的にリクエストルーターはGACに戻り、トークンのスタックをくれと言います。

このプロセスは、大きなスポーツ会場に何十もの入り口があり、それぞれを管理する警備員がいる例に似ています。警備員がコントロールセンターに連絡して「私は一人を中に入れたい」と言うと、非常に忙しくなるでしょう。しかし、警備員が一定数の人々を案内し、その後にコントロールセンターに報告すると、コミュニケーションがはるかに少なくて済みます。これは、DynamoDBのGACとリクエストルーターがどのように連携して動作するかを示しています。

基本的に、GETリクエストルーターはトークンのスタックを受け取ります。彼らはリクエストを提供し、報告します。ここにその3つのステップがあります。リクエストルーターは中央のトークンバケットからトークンを取得し、自分たちのトークンバケットに保存し、リクエストを提供し、統計を公開し、テーブル上の実際の負荷について把握します。

顧客としてテーブルを設定するとします、それがプロビジョンドモードのテーブルだと仮定しましょう。あなたは秒間50万リクエストを設定しました。おそらく10,000のリクエストルーターを通じて、秒間50万リクエストを提供しますが、各リクエストを定期的に取得するようなことはしません。スケールすると、それがどのように我々がレートリミッティングを行うかです。規模に応じてレート制限を行うのです。この利点はGACに加えて、テーブル上のトラフィックの実際のレート制限を非常によく理解できることです。

プレエンプティブ・スプリット

パーティションを複数のピースに分割し、トラフィックを知るために複数のパーティションを作成するメカニズムです。テーブル上のトラフィックを知って、予防的に分割することができます。

レプリカモード

ストレージノードがパーティションを保持し、異なるパーティションは異なる量のトラフィックを提供します。時間をかけて開発チームはいくつかのストレージノードが他のものよりも激しく動いていることに気付きました。レプリカを移動させて、ストレージへのロードバランスを取っています。

繰り返しになるが、顧客はどのような規模でも予測可能なレスポンスタイムを求めています。よって、これらのことはすべて舞台裏で起こっており、ユーザーは何もする必要はありません。テーブルを作成し、データをロードし、トラフィックを実行するだけです。

この2つのセット、プレエンプティブ・スプリットとレプリカ・モードを押さえておいてください。

オンデマンドか?プロビジョンドか?

それを理解するには、そもそも、なぜオンデマンドを構築したのかを理解する必要があります。DynamoDBを立ち上げてテーブルを作成するとき、ユーザーはプロビジョニングのキャパシティを教えてくれました。しかし、DynamoDBの約束は常にフルマネージドであることした。サーバーレスでフルマネージドということは、ユーザーに質問すべきではないということです。

テーブルを作成し、データをロードし、アプリケーションを実行する能力をいくつ持っているか、あとは想像してください。DynamoDBに支払うコストだけでなく、ストリームやLambda、Kinesis Data Streamsを使ってイベント駆動型のアプリケーションを構築する場合も多い。DynamoDBのすぐ下にあるパイプ。これらのシステムはスケールするのか誰か?

負荷が上昇し、また戻ってきたアプリケーションの例です。例えば、オートスケーリングを用いたテーブルが最初に7500のRC(またはWC)を確保していて、実際には一秒あたり4000のリクエストを処理しているケースを考えてみてください。そして、突如としてトラフィックが急増します。短期間のスパイクに対しては、我々が対応すべきだと多くの顧客からフィードバックを受け取りました。そこで、我々はバーストキャパシティを導入しました。このバーストは5分以上続き、その後スロットリングを開始し、顧客またはオートスケーリングがテーブルを増やすまで待つことになりました。

しかし、もしバーストが長期間続く場合、またはスロットリングを避けたい場合はどうでしょうか。アプリケーションのトラフィックが時間とともに増加するという顧客の要望に応え、オンデマンドを導入した。顧客からは、2倍、1.5倍、5倍、600倍など、さまざまな数値が挙げられましたが、最終的には最後のハイスポットから2倍以下であれば対応可能という結論に至った。

GACで行っていることは、テーブルの利用状況を把握し、オンデマンドのテーブルであれば、常に2倍の負荷に対応できるようにテーブルを事前に分割します。これにより、テーブルのトラフィックが2倍以上に増えた場合でも、テーブルを分割してからトラフィックを処理する短時間のスロットリング期間を経て、トラフィックを処理することができます。また、同じブログ記事の中で、一時的に一秒あたり10,000リクエストを処理していたテーブルが、一秒あたり4000から5000リクエストを処理していたところから18,000まで急増し、その後顧客のトラフィックがゼロになった例を紹介しました。この場合、オンデマンドでは使用した分だけを支払います。したがって、ワークロードが変動する場合や、ゼロまでスケールダウンしたい場合、オンデマンドが最適な解答となります。

常にこれらのことについて革新を続けている。これらすべてのことは裏側で行われ、100%利用可能です。したがって、DynamoDBの動作方法のアーキテクチャを変更しても、ユーザーはそれに気付かないでしょう。これがフルマネージドサービスのプロセスです。

では、オンデマンドとプロビジョンド、どちらを使用すべきなのでしょうか?それは完全にコストの問題です。

  • リクエストを処理しないことのコストは何か?
  • それらを処理しないことは許容できますか?

リクエストを絶対に処理したい場合は、オンデマンドが最善です。Amazon内でのベストプラクティスをお勧めします。

  • まずはアプリケーションとそのパターンを理解するまでオンデマンドを使用する
  • その後、プロビジョンドに移行するかどうかを決定する。オートスケーリングを使用すれば、テーブルを自動的に増やすことができます。

一日に三回のピークロード(朝食、昼食、夕食)を持つ顧客がいます。彼らはタイミングスケーリングとオートスケーリングを組み合わせて、テーブルを増減させています。これが彼らにとって最善の方法です。また、他の顧客からは「スポーツコートが必要になるタイミングが予測できない。いつでも需要が急増する可能性がある。そのトラフィックを失うことは我々にとって高すぎる」という声を聞きます。リクエストを処理するコストとスロットリングのコスト、どちらが高いのかを理解することが重要です。その理解があることで、適切な選択をすることが可能になります。

DynamoDBトランザクション

同時に複数のアイテムを更新できるアプリケーションを作成したい、と顧客からのフィードバックがあった。全てが成功するか、全てが失敗するかの二択です。そこでDynamoDBトランザクションを導入した。

提供する分離保証は何か?

標準的なACIDトランザクションをサポートしています。サポートしている分離レベルはシリアライザブルです。シリアライザブルとは、複数の事象がほぼ同時に発生する場合のことを指します。 システムがシリアライザブルであるとは、外部の観察者が複数の事象を見て、一つ目が起き、二つ目が起き、そして三つ目が起きたという具体的な順序を明確に認識できる状態を指します。

もしDynamoDBに対して2つのトランザクションをまったく同時に送信した場合、まず一つ目の全てが完了し、その後に二つ目が完了します。これがシリアライザブルです。

非トランザクションの読み取りや書き込みがある場合、各個別の読み取りと書き込みがトランザクションとシリアライズされます。バッチ取得やバッチ書き込みを行った場合、個々の取得や書き込みがトランザクションとシリアライズされますが、バッチ全体ではありません。クエリやスキャンを行った場合、クエリやスキャンで取得された各アイテムが個別の取得となります

トランザクションを使用する必要がある場合は、原子性と一貫性を保証します。一貫性は常に最終的に保証されます。また、強力にシリアライザブルな分離をサポートしています。DynamoDBがダーティリードを行うという意見をいくつか聞きましたが、決してダーティリードを行いません。書き込まれたがコミットされていない操作を露出させることはありません。

トランザクションの動作

トランザクションは実際にどのように動作するのでしょうか?

リクエストを送信すると、それはDynamoDBに伝えられます。リクエストルーターによって認証と承認が行われ、リクエストが処理されるべきかどうかが確認されます。そして、2フェーズコミットシステムに馴染みのある方にとっては標準的なトランザクションコーディネーターが存在します。ただし、実際のトランザクションの実装はそれよりもはるかにスマートです。

なぜなら、伝統的なトランザクションコーディネーターは分散ロックマネージャーを含むUVMメッセージを使用し、それは予測不能なレイテンシーを意味するからです。

DynamoDBは予測可能なレイテンシーに全てをかけています。したがって、トランザクションコーディネーターを持ち、2フェーズコミットを使用しますが、分散ロックは使用しません。

なぜDynamoDBはBEGIN-ENDトランザクションをサポートしないのか?

DyamoDBは分散トランザクションを行い、トランザクションコーディネーターを使用しますが、BEGIN-ENDトランザクションやマルチリクエストトランザクションは行いません。右側に表示されているのは、一般的なリレーショナルデータベースで見られるものです。BEGINを行い、読み取りや書き込み、SELECT、INSERT、UPDATEなどを行い、そして最後にコミットまたはロールバックを決定します。これが一般的なトランザクションの流れですが、これがDynamoDBのトランザクションではありません。DynamoDBのトランザクションでは、行いたい全ての操作を特定し、それらを一度に渡します。

単一リクエストを行う理由は何か、そしてなぜBEGINを行わないのかという質問に対する答えは比較的シンプルです。顧客がDynamoDBを使用する理由は、どんな規模でも予測可能なレイテンシーを保証するからです。BEGIN-ENDスタイルのトランザクションでアプリケーションを構築した経験のある方の中には、他の誰かがテーブルのロックを取得しているように見える状況を経験したことがあるかもしれません。そのロックを取得した人を探すと、彼らはBEGINを行い、いくつかの変更を加えた後でコーヒーブレイクに行ったという状況です。これをどのように解決しますか?

DynamoDBは、分散ロックマネージャーを持たずに予測可能なレイテンシーを保証する方法を考案しました。それが単一リクエストトランザクションです。リクエストが受信された瞬間に、何をすべきかを知ることができます。しかし、これにはコストが伴います。

従来のBEGIN-ENDスタイルのトランザクションでは、読み取りと書き込みを交互に行うことができます。BEGINを行い、読み取りを行い、値を確認し、書き込むべきかどうかを決定することができます。これら全てのことをクライアントアプリケーションで完全に制御することができます。しかし、DynamoDBはそれを行うことができません。なぜなら、トランザクションで行いたい全てのことを事前に伝えなければならないからです。しかし、単一リクエストトランザクションでは何も失うことはありません。BEGIN-ENDで行える全てのことは、単一リクエストトランザクションで行うことができます。

BEGIN-ENDを簡単に考えてみてください。全ての読み取りと書き込みを特定し、リストにします。まず全ての読み取りを行います。読み取りが完了したら、単一の書き込みトランザクションを構築します。これには、書き込みたい全てのものと、読み取ったもの全てを含めます。これらをチェック条件として使用します。

BEGIN-END にできることは、単一のリクエストトランザクションで行うことができます。そして、規模に関係なく予測可能なレイテンシー、分散ロック境界がないということです。そして、そのプロセスで何も失うことはありません。

アプリケーションの復旧はどのように対処すべきでしょうか?

トランザクションを行い、DynamoDBからの応答を受け取る前に、アプリケーションが再起動しました。アプリケーションが再起動すると、トランザクションリクエストが落ちていることがわかり、その状態がわからなくなります。アプリケーションの復旧はどのように対処すべきでしょうか?

クラウドを使用する際には、大規模でそれを構築するときにはアプリケーションが最も不適切なタイミングでダウンする可能性があることを覚えておく必要があります。そのためDynamoDBトランザクションもこの問題に対処するために構築されている。

トランザクションと共に、トランザクションのためのユニークな識別子であるプレーンリクエストを指定します。同じトランザクションを10分以内に再度実行し、同じクライアントリクエストを提供した場合、トランザクションを再実行せず、前回何が起こったかを伝えるだけです。

クライアントリクエストトークンはリクエストのハッシュとして構築することをお勧めします。そのため、リクエストトークンを復旧する際にどこかに保存する必要がなくなります。トランザクションを実行すると、DynamoDBはそれを認識し、同じトランザクションを再度行うことはありません。それが成功したか失敗したかは関係ありません。したがって、アプリケーションが予期せずに終了した場合でも、トランザクションが知らせてくれます。

テーブルが名前、パーティションキー、およびプライマリキーを持つと述べました。データをパーティションに分け、N個のレプリカを持ちます。レプリカはツリーとログを持ち、リクエストを処理します。しかし、他のことも行います。レプリカが終了すると、復旧の方法は他のレプリカに対してこのレプリカの状態を問い合わせることでした。

しかし、DynamoDBは規模に関係なく予測可能なレイテンシーを保証したいため、ストレージノード上のレプリカは読み書きのみを処理するようにしました。その他のすべてのことはログストリームを読むだけで十分に処理できます。レプリカ自体を複製する必要はありません、それをログから再構築することができます。

その結果、ログサービスと呼ばれる機能を実装しました。これは、他の機能を構築する能力を大幅に向上させました。最近は、テーブルからS3へのエクスポートを開始しました。エクスポートはレプリカに触れず、ログにアクセスします。他にも、パーティションの再構築やデータ破損の特定など、バックグラウンドで静かに行われる作業があります。これらすべてが、ログサービスという交差点に依存しています。

DynamoDBストリーム

ストリームについては、ユーザーが解答を求めていた3つの質問があります。

  • DynamoDBストリームは、どのような保証を提供しますか?
  • なぜシャードが必要なのですか?そして、なぜそれらが多いのですか?
  • シャードローテーションとシャード分割とは何ですか?

テーブルには挿入、更新、削除が行われ、それらはレプリカに送られます。それらはログエントリを生成し、ログサービスに送られます。ストリームリスナーがそれらをシャードに変換し、ストリームリクエストが行われると、それらはストリームルーターに送られます。ストリームルーターはリクエストを処理し、データをシャードから提供します。

シャードが多い理由は、テーブルが1秒あたり10回の書き込みを行うとします。それはおそらく単一スレッドのアプリケーションから行われているでしょう。単一スレッドのアプリケーションで、これらの変更をリアルタイムで処理することは問題ありません。

しかし、秒間に50万回のリクエストを行う場合はどうでしょうか?それは単一スレッドのアプリケーションからは来ていません。そして、単一のアプリケーションで秒間50万回の変更を処理することは現実的ではありません。

DynamoDBは水平スケールします。ストリームのシャーディングは、アプリケーション内で高速なトランザクションや高速なストリーム通知を処理する能力を提供するメカニズムです。ストリームのデータをリクエストすると、適切なシャードからデータを取得します。

なぜシャードを作り、どのような保証を提供するのかという質問に対する答えは、DynamoDBストリームはKinesisストリームではないということです。DynamoDBはアイテム上のすべての変更の厳密な順序を保証し、変更はストリーム内で一度だけ発生します。Kinesisストリームはこの順序保証を提供せず、重複が存在する可能性があり、それを処理する必要があると述べています。

アイテムに実際に発生するすべての変更はストリーム内で一度だけ表示されます。条件チェックの失敗により書き込みを試みた場合、アイテムは実際には変更されません。ストリームには何も表示されません。

シャードを分割する理由とその方法については、次のように説明できます。ストリームを記述すると、いくつかのシャードが表示されます。一部のシャードは閉じており、一部は開いています。開いているシャードには終了シーケンス番号がありません。すべてのシャードには識別子、親、開始シーケンス番号、終了シーケンス番号があります。開いているシャードには終了シーケンス番号がなく、閉じているシャードには終了シーケンス番号があります。シャードのローテーションプロセスは、シャードを閉じて新たに作成することで行われます。

秒間1500回の書き込みを開始しました。1500回の書き込みは、単一スレッドで処理することはできないと考えられます。そのため、2つのシャードを提供します。単一のシャードを閉じて、2つの子シャードを生成します。そして、2つの子シャードはルートシャードとなります。シャードのローテーション、シャードの分割、これらはすべて、より高い書き込み速度を処理できるようにするためのものです。

時間とともに、シャードの構造は次のようになります。シャードのローテーション、シャードの分割、さらなる分割、シャードのローテーションなどが行われます。

ストリームの基本的な実装では、これらのシャードは特定のインスタンス上にホストされています。これらのインスタンスを管理し、時間とともに一部のホストが他のホストよりも高負荷になっていることに気付きました。

内部で負荷分散を行うメカニズムが必要でした。顧客が気付くことなく変更を加えることは、考えるすべてのことです。顧客がアプリケーションで対処しなければならないことを最小限に抑えたかったのです。シャードの分割をサポートしなければならず、シャードが閉じ、複数のシャードが生成される状況を顧客が対処しなければならないことを理解していました。したがって、ローテーションというメカニズムを導入しました。

これは、シャードが閉じ、新しいシャードが生成されるという状況を扱うものです。ローテーションは、内部で負荷分散を行うためのメカニズムです。これを実装し、約4時間ごとが適切なタイミングであると考えました。しかし、一部の顧客は、この4時間の間隔が一時的に変更されたことに不満を持ちました。

4時間ごとにタイマーを設定する場合は、必ずしも4時間が確実に保証されるわけではないことを覚えておいてください。基礎となるホストが再起動する必要がある場合など、それが早まることもあります。また、DynamoDBストリームに対してネイティブのアプリケーションを構築する場合、ストリームシャードの分割が発生すると、シャードが分割されて2つの子シャードが生成されることに注意してください。しかし、必ずしも2つになるとは限らないということを理解しておいてください。将来的には、3つになることもありますし、複数のシャードが結合されるシャードの統合も考えられます。

DynamoDB Global Talbes

これらの警告を理解した上で、グローバルテーブルについて説明します。

  • グローバルテーブルはどのように動作するのか?
  • グローバルテーブルにおける一貫性と順序付けの保証は何でしょうか?
  • グローバルテーブルとストリームを組み合わせた場合、すべてのリージョンの変更は同一になりますか?

顧客がより洗練されたアプリケーションを構築したいというニーズから、ストリームは構築されました。また、顧客がグローバルに分散したアプリケーションを構築したいというニーズから、グローバルテーブルが構築されました。

データがすべて一か所に格納されている場合、そのリージョンの顧客は良好なレイテンシーを得ることができます。しかし、顧客がオーストラリアにいて、データがus-east-1にある場合、彼らは最良の経験を得られないかもしれません。

データの複製は長い間存在していますが、DynamoDBはフルマネージドサービスです。データの複製と競合解決を提供します。テーブルを作成し、他のすべての場所でレプリカを作成するように指示すると、それらを処理します。オーストラリアの顧客はオーストラリアのDynamoDBテーブルに接続し、ブラジルの顧客はブラジルのテーブルに接続します。DynamoDBは複製を処理します。しかし、競合が発生する可能性があります。複数の場所で同じアイテムを書き込むことができます。我々は競合解決を提供します。そのルールは非常にシンプルで、最後に書き込んだ者が勝つというものです。

システムが安定状態に達したとき、アイテムの最後のWriterは、どの場所でもそのアイテムの起源となります。また、リージョン間でのレプリケーションの遅延は光の速度によるものです。リージョンの選択によりますが、通常、最悪の場合でも1秒未満です。

では、グローバルテーブルはどのように動作するのでしょうか?以下にグローバルテーブルの簡単な説明を示します。リージョン1のテーブルでストリームの変更を読み取り、リージョン1にレプリケータがあり、それが他のすべてのリージョンにデータを書き込むだけです。直接ストレージノードには行きません。

リージョン1で書き込みを行います。レプリケータはそのトラフィックを各リージョンのリクエストルーターを経由します。グローバルテーブルを使用している方々にとって、なぜすべてのリージョンで書き込みキャパシティが同じでなければならないのか疑問に思うかもしれません。これがその理由です。読み取りキャパシティは他のリージョンと異なることがありますが、書き込みキャパシティは同じでなければなりません。

フルマネージドなレプリケーションです。では、一貫性の保証と順序保証は何でしょうか?それはかなり結果的な一貫性です。顧客は、書き込みを行ったときに予測可能なレイテンシーを求めます。書き込みはローカルで行われ、その後、他のリージョンに伝播されます。最新のものが結果的に一貫しています。

複数の場所でテーブルに更新を行うと、常に時間が前進するように見えます。古い更新はテーブルに適用されることはありません。最新のものが勝つということを覚えておいてください。これは、古い更新が通過する場合、それを無視することを意味します。

グローバルテーブルとストリームを組み合わせた場合、すべてのリージョンの変更は同一になりますか?

これはおそらく最も難しい質問ですが、大抵の場合、答えは「Yes」です。

しかし例外のケースについても説明します。リージョンで書き込みを行い、そのすぐ後でリージョン2で書き込みを行います。まず第一に、リージョンで新たに書き込みが行われることはありません。システムの最終状態はどうあるべきでしょうか?もちろん、最新のものが勝つべきです。

リージョン1で書き込みを行いました。実際には書き込みが行われました。つまり、ここにストリームの通知があります。リージョン2で書き込みを行い、その書き込みがリージョン1に伝播しました。リージョン1からの書き込みがリージョン2に伝播しました。これは結果的に一貫性があります。順序についての保証はありません。では、何が起こるでしょうか?書き込み1は書き込み2の前に行われました。この書き込みはどうなるでしょうか?無視されます。時間は常に前進するように見えます。決して後退しません。

リージョン2への書き込みがあり、そのアイテムには通知があります。ちなみに、このストリームの通知はアイテムが到着する前に行われた可能性がありますが、それは問題ありません。時間は決して戻りません。通知があるところに適用されます。したがって、答えは、「大抵の場合はYES」です。

したがって、アプリケーションを構築しており、グローバルテーブルとストリームを使用している場合、アイテムに対して行われるすべての書き込みがストリームに反映されるとは限らないということを前提にしないでください。その問題はアプリケーション内で処理する必要があります。

まとめ

  • DynamoDBはテーブルをパーティションに分割することで水平方向に拡張する
  • 名前、プライマリキー、および課金モードを指定する必要があります
  • パーティションキーは、データをパーティションに分散するために使用されます
  • 複数のレプリカは異なるAZにあります
  • すべての操作は共有インフラストラクチャ上で行われます。それはフルマネージドで、サーバーレスです
  • パーティションレベルの制限を持つテーブルがあります
  • グローバルアドミッションコントロールは、すべてのテーブルレベルの制限を強制するものです
  • 予測分割とアダプティブノードに行います
  • オンデマンドから始めることがベストプラクティスです
    • まずはアプリケーションに集中してください、悩む必要はありません
    • ワークロードを理解したら、どちらにするべきか選択してください
  • ACIDトランザクションをサポートしています。それらは厳密にシリアライゼーションが可能です
  • トランザクションは互いにシリアライゼーションされ、個々の読み書きもシリアライゼーションされます
    • クエリとスキャンは単一の読み書きではなく、複数の読み書きであることを覚えておいてください
  • マルチリクエストの読み書きを、トランザクションでは単一リクエストに変更します
  • コミットされていないアイテムを返さないし、ダーティリードを行いません
  • ストリームはイベント駆動型のアプリケーションを構築するのを可能にします
  • 厳密な順序付けと、DynamoDBストリームでの一度だけの処理を提供します
    • これはKinesisストリームではなく、DynamoDBストリームです
  • スケーラビリティのためにシャードとスプリットを行います。
  • グローバルテーブルを使用すると、グローバルに分散したアプリケーションを簡単に構築できます
  • レプリケーションと競合解決を行います
  • 伝播遅延は通常、1秒未満のオーダーです

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.