DynamoDB はなぜ 1 桁ミリ秒を保証できるのか - パーティション分割とリクエストルーターの内部構造
DynamoDB が規模に関係なく 1 桁ミリ秒のレイテンシを維持できる仕組みを、パーティション分割アルゴリズム、リクエストルーター、ストレージノードの 3 層アーキテクチャから解説します。
1 桁ミリ秒の約束が意味すること
DynamoDB は「テーブルサイズやリクエスト量に関係なく、1 桁ミリ秒のレイテンシで読み書きできる」と公称しています。この約束は、1KB のテーブルでも 100TB のテーブルでも、秒間 10 リクエストでも秒間 1,000 万リクエストでも成立します。リレーショナルデータベースでは、テーブルサイズが大きくなるとインデックスの深さが増し、ディスク I/O が増加してレイテンシが劣化します。DynamoDB がこの問題を回避できるのは、データの格納方法とアクセスパスが根本的に異なるためです。DynamoDB はデータをパーティションと呼ばれる小さな単位に分割し、各パーティションを独立したストレージノードに配置します。リクエストは常に 1 つのパーティションに対して実行されるため、テーブル全体のサイズは個々のリクエストのレイテンシに影響しません。これが「規模に依存しない一定のレイテンシ」の核心です。
パーティション分割 - データを自動的に水平分散する仕組み
DynamoDB のパーティション分割は完全に自動化されており、ユーザーが意識する必要はありません。各パーティションは最大 10GB のデータを格納し、最大 3,000 RCU (Read Capacity Units) または 1,000 WCU (Write Capacity Units) のスループットを処理できます。テーブルのデータ量またはスループットがこの上限に達すると、DynamoDB は自動的にパーティションを分割します。パーティションキーのハッシュ値に基づいてデータが各パーティションに振り分けられます。ここで重要なのは、パーティションキーの設計です。すべてのリクエストが同じパーティションキーに集中すると、そのパーティションだけがホットスポットになり、スループット上限に達してスロットリングが発生します。2019 年に導入されたアダプティブキャパシティにより、ホットパーティションへのスループットの自動再配分が行われるようになりましたが、根本的な解決策はパーティションキーの設計でアクセスを均等に分散させることです。たとえば、日付をパーティションキーにすると「今日」のパーティションにアクセスが集中します。日付にランダムなサフィックスを付加する「ライトシャーディング」パターンで分散させるのが定石です。
リクエストルーター - 正しいパーティションを瞬時に特定する
DynamoDB のリクエストルーターは、クライアントからのリクエストを受け取り、パーティションキーのハッシュ値から対象のパーティションを特定し、そのパーティションを保持するストレージノードにリクエストを転送するコンポーネントです。AWS は 2022 年の論文「Amazon DynamoDB: A Scalable, Predictably Performant, and Fully Managed NoSQL Database Service」で、リクエストルーターの内部構造を公開しました。リクエストルーターはパーティションマップ (どのパーティションキー範囲がどのストレージノードに配置されているか) をメモリ上にキャッシュしており、ルーティングの判断はマイクロ秒単位で完了します。パーティションの分割や移動が発生した場合、パーティションマップは非同期で更新されます。一時的に古いマップを参照してしまった場合は、ストレージノードからリダイレクト応答が返り、リクエストルーターがマップを更新して再試行します。この仕組みにより、パーティションの再配置中もリクエストの処理が中断されません。リクエストルーターは複数のインスタンスが並列に動作しており、単一障害点にはなりません。
ストレージノードとレプリケーション - 3 重書き込みの仕組み
各パーティションのデータは、3 つのストレージノードにレプリケーションされます。書き込みリクエストは、まずリーダーノードが受け取り、2 つのフォロワーノードに複製します。3 つのノードのうち 2 つ (リーダー + 1 フォロワー) への書き込みが完了した時点で、クライアントに成功応答が返ります。これが「2 of 3 クォーラム書き込み」です。読み取りには 2 つのモードがあります。結果整合性のある読み取り (Eventually Consistent Read) は、3 つのノードのいずれか 1 つから読み取ります。最新の書き込みが反映されていない可能性がありますが、レイテンシが最も低くなります。強い整合性のある読み取り (Strongly Consistent Read) は、リーダーノードから読み取るため、常に最新のデータが返りますが、リーダーノードへの負荷が集中するため、結果整合性の読み取りと比較してスループットが半分になります。2023 年に発表された DynamoDB の論文では、ストレージノードが B-tree ベースのストレージエンジンを使用していることが明らかにされました。LSM-tree (Log-Structured Merge-tree) ではなく B-tree を採用しているのは、読み取りレイテンシの予測可能性を重視した設計判断です。
Global Tables と DAX - 1 桁ミリ秒をさらに短縮する
DynamoDB の 1 桁ミリ秒は、同一リージョン内でのレイテンシです。グローバルに展開するアプリケーションでは、ユーザーから最も近いリージョンでデータを読み書きしたいという要求があります。Global Tables は、複数のリージョンにテーブルのレプリカを自動的に作成し、どのリージョンからでも読み書きできるマルチマスターレプリケーションを提供します。東京リージョンへの書き込みは、数秒以内にバージニアやフランクフルトのレプリカに伝播します。レプリカ間の整合性は結果整合性ですが、同一リージョン内の読み書きは通常の DynamoDB と同じ 1 桁ミリ秒のレイテンシで処理されます。さらに低いレイテンシが必要な場合は、DAX (DynamoDB Accelerator) を使用します。DAX は DynamoDB の前段に配置するインメモリキャッシュで、読み取りレイテンシをマイクロ秒単位 (数百マイクロ秒) に短縮します。DAX は DynamoDB と API 互換のため、アプリケーションコードの変更はエンドポイントの差し替えだけで済みます。ただし、DAX はキャッシュであるため、書き込み直後の読み取りでは古いデータが返る可能性があります。キャッシュの TTL 設定と、書き込み後の読み取りパターンを考慮した設計が必要です。NoSQL データベースの設計パターンを深く理解するには、専門書籍 (Amazon)が参考になります。