「RDBが嫌いだから、DynamoDBを使いたい」
そう考えたことがあるなら、まず一度立ち止まることをおすすめします。
DynamoDBは、RDBの代替品として導入すれば自動的に幸せになれるデータベースではありません。
むしろ、RDBで当たり前だった設計思想をそのまま持ち込むと、後になってかなり厄介な問題に直面します。
RDBでは、正規化したテーブルを作り、必要なデータをSQLで柔軟に検索し、JOINで関連付けるという考え方が基本です。
一方、DynamoDBでは事情が大きく異なります。
重要なのは「どんなデータを保存するか」だけではなく、アプリケーションがどのようにデータを取得するのかを先に考え、そのアクセスパターンに合わせてキーやインデックス、データの持ち方を設計することです。
つまり、RDBからDynamoDBへの移行は、単なるデータベース製品の変更ではありません。
データモデリングそのものの発想を切り替える作業です。
ここを理解せずに「JOINが嫌だから」「スケールさせたいから」といった理由だけで採用すると、複雑なクエリの再現、過剰な読み取り、巨大なアイテム、整合性の扱いなど、別の苦労を抱えることになります。
この記事では、RDBの設計に慣れたエンジニアがDynamoDBへ移行するときに、どこで思考を切り替えるべきなのかを整理します。
特に、アクセスパターンから逆算するテーブル設計、パーティションキーとソートキーの使い方、GSIの設計、非正規化との付き合い方、トランザクションや整合性の考え方まで、実践でつまずきやすいポイントを順番に見ていきます。
DynamoDBを「RDBが苦手だから使うデータベース」と捉えるのではなく、「RDBとは異なる制約の中で、特定のアクセスパターンを効率よく処理するためのデータベース」と捉えること。
これが、リレーショナルから脱却しても設計を破綻させないための出発点です。
- RDBが嫌いだからDynamoDBを使う前に知っておきたいこと
- DynamoDBとRDBの違いを理解する:SQLとNoSQLでは何が変わるのか
- DynamoDBのテーブル設計はアクセスパターンから逆算する
- DynamoDBのパーティションキーとソートキーを正しく設計する
- RDBの正規化を捨てる?DynamoDBで非正規化する理由と設計方法
- DynamoDBのGSI設計で複数の検索パターンに対応する
- DynamoDBでJOINできない問題をどう解決する?データ取得を再設計する
- DynamoDBの整合性とトランザクションをRDBと比較して考える
- DynamoDBの料金とパフォーマンスで失敗しないための実践ポイント
- DynamoDBが向いているシステム・向いていないシステムを見極める
- RDBからDynamoDBへ移行するときに避けたい典型的な失敗
- RDBからDynamoDBへ脱却するときに大切なのはデータベース選定より設計思想
RDBが嫌いだからDynamoDBを使う前に知っておきたいこと

「RDBが嫌いだからDynamoDBを使いたい」という動機は、決して珍しいものではありません。
テーブル設計で正規化に頭を悩ませたり、JOINが増えてSQLが複雑になったり、スケールアウトを考えた途端にデータベースの構成が難しくなったり。
そうした経験が続くと、「いっそRDBをやめれば、この問題から解放されるのでは?」と考えたくなります。
ただし、ここで押さえておきたいのは、DynamoDBは「RDBの面倒なところをなくしたRDB」ではないという点です。
DynamoDBにはDynamoDBの設計原則があり、RDBとは異なる制約の中で性能や可用性を引き出す必要があります。
製品を変えるだけで問題が消えるのではなく、問題の種類そのものが変わる、と考えたほうが正確です。
たとえばRDBでは、まずエンティティをテーブルとして整理し、主キーや外部キーを設定し、必要に応じて正規化します。
そして、実際に必要なデータはSQLによって組み立てます。
検索条件が増えても、インデックスやクエリを調整しながら柔軟に対応できます。
これはRDBが持つ非常に強力な特徴です。
一方のDynamoDBでは、「あとでクエリを書けば何とかなる」という考え方が通用しにくくなります。
どんなデータを保存するかより先に、アプリケーションがどんな検索や取得を行うのかを考える必要があります。
ユーザーIDから注文履歴を取得するのか、商品IDから関連する情報を探すのか、特定期間のイベントを時系列で取得するのか。
こうしたアクセスパターンを起点に、パーティションキーやソートキー、必要なGSIを設計していきます。
これは、データベース設計の順番が逆転する感覚に近いものです。
RDBでは「データをどう整理するか」が中心になりやすいのに対して、DynamoDBでは「そのデータをどう読むのか」が設計の中心になります。
データモデルとアプリケーションの処理を切り離して考えるのではなく、両者を一体として設計する発想です。
そのため、DynamoDBを導入する前には、まず「自分たちはRDBの何が嫌なのか」を分解してみるべきです。
- JOINそのものが複雑なのか
- スケールアウト時の運用が難しいのか
- SQLのクエリ管理が負担なのか
- 高トラフィック時の性能設計が問題なのか
- データモデルの変更に伴うマイグレーションが面倒なのか
この切り分けをしないままDynamoDBへ移行すると、RDBで感じていた不満とは別の場所で苦労する可能性があります。
たとえばJOINを避けるためにデータを非正規化した結果、同じ情報を複数箇所に保持することになり、更新処理が複雑になるケースです。
あるいは検索要件を十分に洗い出さずにテーブルを作り、後から「この条件では効率よく検索できない」と気付くケースもあります。
特に重要なのが、DynamoDBでは後からの要求変更がRDBほど気軽ではないという認識です。
もちろんGSIなどによって検索方法を追加することはできます。
しかし、最初に想定していなかったアクセスパターンが次々と増えると、データ構造そのものを見直す必要が出てきます。
プロダクト開発では要件変更が避けられないため、「将来どんな検索が発生するか」を完全に予測することはできません。
だからこそ、主要なアクセスパターンを整理し、どこまで変更を許容する設計にするのかを考える必要があります。
もう一つ見落とせないのが、DynamoDBを採用する理由を「NoSQLだから」にしないことです。
NoSQLという分類だけでは、設計上のメリットは説明できません。
重要なのは、DynamoDBが提供するマネージドな運用、水平スケールを前提としたアーキテクチャ、アクセスパターンに適合した高速なキー・バリュー/ドキュメントアクセスなどが、対象システムの要件と噛み合っているかどうかです。
逆に言えば、複雑な集計や自由度の高い検索、データ同士の関係をその場で組み立てる処理が中心なら、RDBのほうが自然なケースもあります。
「RDBが嫌い」という感情を出発点にするのは構いません。
しかし、最終的な判断は感情ではなく、アクセスパターン、整合性、性能、運用負荷、コスト、将来の変更可能性といった技術的な要件から行うべきです。
DynamoDBは、RDBから逃げるためのデータベースではありません。
異なる前提で設計することで、大規模なアクセスや高い可用性が求められるシステムをシンプルに運用できる可能性を持ったデータベースです。
だからこそ、RDBの常識を捨てるだけでは不十分。
DynamoDBの常識を理解し、自分たちのシステムに本当に適しているのかを見極めることが重要です。
DynamoDBとRDBの違いを理解する:SQLとNoSQLでは何が変わるのか

DynamoDBを理解するうえで、最初に整理しておきたいのがRDBとの設計思想の違いです。
単純に「RDBはSQL、DynamoDBはNoSQL」と覚えるだけでは、実際の設計にはほとんど役立ちません。
重要なのは、両者がデータをどのように整理し、どのような問い合わせを想定し、どこで計算や整合性を担保するのかという違いです。
RDB、つまりリレーショナルデータベースでは、データを複数のテーブルに分割し、テーブル間の関係をキーによって表現します。
たとえばECサイトなら、ユーザー、商品、注文、注文詳細といった単位でテーブルを分けるのが自然です。
データの重複を抑えるために正規化し、必要なデータはSQLのJOINや集約関数を使って取得します。
このモデルの強みは、データの関係をデータベース側で柔軟に表現できることです。
「ユーザーごとの注文を取得する」「特定の商品を購入したユーザーを探す」「月別の売上を集計する」といった処理を、SQLという強力な問い合わせ言語で記述できます。
アプリケーションから見ると、保存されているデータ構造と、その場で必要なデータを取得する方法をある程度分離できます。
もちろん、その柔軟性にはコストもあります。
大量のデータをJOINしたり、複雑な集計を実行したりすれば、実行計画やインデックス、ロック、トランザクションなどを考慮しなければなりません。
アクセス量が増えれば、CPUやメモリ、ディスクI/Oといったデータベースサーバー側のリソースも問題になります。
RDBは万能ではありませんが、非常に汎用性の高いモデルだと言えます。
一方、DynamoDBはAWSが提供するフルマネージドなNoSQLデータベースです。
テーブルという言葉は共通していますが、RDBのテーブルと同じ感覚で考えると混乱します。
DynamoDBでは、リレーションを組み合わせて必要な結果を作るよりも、必要なアクセスパターンに対して、少ない読み取りで目的のデータを取得できるようにデータを配置することが重要です。
ここで大きく変わるのが「データを先に設計するのか、アクセスを先に設計するのか」という考え方です。
RDBでは、まずデータの正規化やエンティティ間の関係を整理し、その後にSQLを考えることが一般的です。
DynamoDBでは逆に、アプリケーションが必要とする読み取りと書き込みを先に洗い出し、それを実現できるキー設計を考えます。
たとえば「ユーザーIDを指定して、そのユーザーの注文を新しい順に取得する」という要件があるとします。
RDBならユーザーテーブルと注文テーブルを関連付け、WHERE句やORDER BY句を使って取得する設計が自然です。
DynamoDBでは、ユーザーIDをパーティションキー、注文日時などをソートキーとして利用することで、必要なデータをひとまとまりのアクセスとして取得できるように設計します。
この違いを一言で表すなら、RDBは「関係を組み立てて問い合わせる」のに対し、DynamoDBは「問い合わせる形に合わせてデータを配置する」と考えると分かりやすいでしょう。
もちろん実際のDynamoDB設計はもっと複雑ですが、この感覚を持っているだけでも、RDBの設計をそのまま移植するという典型的な失敗を避けやすくなります。
もう一つ重要なのが、DynamoDBではJOINを前提にできないことです。
RDBではテーブルを分割しても、JOINによって必要な情報を組み合わせられます。
そのため、データを正規化しておけば、複数の画面や機能から柔軟に利用できます。
しかしDynamoDBでは、RDBと同じように細かくテーブルを分割すると、アプリケーション側で複数回の読み取りを行ってデータを組み立てる必要が生じます。
そこで登場するのが非正規化です。
たとえば注文情報を取得するときに毎回ユーザー情報まで必要なら、関連する情報を同じアイテムやアクセスしやすい構造にまとめることがあります。
同じ情報を複数箇所に持つことになるため、更新時の整合性という別の問題は発生します。
しかし、読み取りを効率化するというDynamoDBの思想では、これは必ずしも悪い設計ではありません。
つまり、RDBで「データの重複はなるべく避ける」と考えていたエンジニアほど、DynamoDBでは最初に戸惑う可能性があります。
非正規化は設計ミスではなく、アクセスパターンを満たすための意図的なデータ複製として使われることがあるからです。
大切なのは重複しているかどうかではなく、その重複によって何を得て、どのような更新コストを負うのかを説明できることです。
また、RDBとDynamoDBではスケーリングの考え方も異なります。
RDBでは、まず1台のデータベースを高性能な構成にするスケールアップを検討し、その後にリードレプリカやシャーディングなどを組み合わせることがあります。
一方のDynamoDBは、最初から分散システムとしての利用を強く意識したサービスです。
データを適切なパーティションへ分散させることができれば、大量のアクセスを処理しやすくなります。
ただし、「DynamoDBなら勝手に無限スケールする」と考えるのも危険です。
パーティションキーの選択が偏っていれば、特定のパーティションにアクセスが集中するホットパーティションの問題につながります。
DynamoDBの性能を引き出すには、サービスのスケール特性を理解したうえで、データ分布とアクセス分布を設計する必要があります。
このように比較すると、RDBとDynamoDBには優劣というより得意分野の違いがあることが分かります。
RDBは複雑な関係を扱い、柔軟な検索や集計を行いたいケースに強い。
一方、DynamoDBはアクセスパターンが明確で、大量のリクエストを安定して処理したいケースで強みを発揮します。
したがって、DynamoDBを採用するかどうかを判断するときは、「SQLが嫌いか」「NoSQLを使ってみたいか」ではなく、システムの主要なアクセスパターンをDynamoDBのデータモデルに自然に落とし込めるかを基準にするべきです。
ここを判断できるようになると、RDBからDynamoDBへの移行は単なる製品変更ではなく、要件に合わせてデータアクセスを最適化するための設計変更として見えてきます。
DynamoDBのテーブル設計はアクセスパターンから逆算する

DynamoDBの設計で、RDB経験者が最初につまずきやすいのが「テーブルをどう分けるか」という問題です。
RDBなら、まずユーザーや商品、注文といったエンティティを洗い出し、それぞれをテーブルとして整理するところから始めるのが自然です。
しかしDynamoDBでは、この順番をそのまま適用すると設計がうまくいかないことがあります。
DynamoDBでは、テーブル構造を考える前に、アプリケーションがどのようにデータへアクセスするのかを洗い出すことが重要です。
これを一般にアクセスパターンとして考えます。
つまり、「何を保存したいか」だけではなく、「誰が、どの条件で、どんな順番で、どれくらいの頻度でデータを読むのか」まで具体化してから、データモデルを決めていくわけです。
たとえばECサイトを設計するとしましょう。
「ユーザー」「商品」「注文」というデータがあるからといって、単純に3つのテーブルを作ればよいとは限りません。
実際の画面やAPIを考えると、必要になる操作はもっと具体的です。
- ユーザーIDからユーザーのプロフィールを取得する
- ユーザーIDから注文履歴を新しい順に取得する
- 注文IDから注文の詳細を取得する
- 商品IDから商品の詳細を取得する
- 商品カテゴリから商品一覧を取得する
このようにアクセスパターンを列挙すると、必要なキー設計が徐々に見えてきます。
逆に、アクセスパターンを整理しないまま「とりあえずRDBと同じようにテーブルを作る」と、後になってDynamoDBでは効率よく取得できないデータ構造になってしまいます。
ここで重要なのは、「DynamoDBではクエリを書く」という発想を少し変えることです。
RDBでは、テーブルにデータを格納しておけば、SQLによってさまざまな条件で検索できます。
もちろんインデックスなどの設計は必要ですが、ある程度は後から問い合わせ方を変更できます。
一方、DynamoDBでは、キーを使った効率的なアクセスが基本になるため、どんな条件でデータを取得するのかが、テーブル設計そのものに直結します。
たとえば「ユーザーごとの注文を注文日時の新しい順に取得する」というアクセスパターンがあるなら、ユーザーを単位としてデータをまとめ、注文日時などを並び順に利用できる構造が考えられます。
ここでパーティションキーとソートキーの役割が効いてきます。
パーティションキーによってアクセス対象を絞り、ソートキーによって、その中のデータを効率よく検索・並べ替えできるようにする設計です。
この考え方は、DynamoDBの設計を「データ中心」から「アクセス中心」へ切り替える典型的な例です。
データベースの教科書で学んだ正規化の知識が無駄になるわけではありません。
ただし、その知識だけでDynamoDBを設計しようとすると、最適な答えにたどり着けないことがあります。
まずAPIと画面からアクセスパターンを洗い出す
実務では、いきなりDynamoDBのキーを決めるより、アプリケーション側の要件から考えるほうが安全です。
特にWebアプリケーションなら、画面やAPIエンドポイントを一覧にするとアクセスパターンを整理しやすくなります。
たとえば注文管理システムなら、「注文一覧画面ではユーザー単位で最新の注文を表示する」「注文詳細画面では注文IDから商品明細まで取得する」「管理画面ではステータスと日時を条件に注文を検索する」といった具合です。
ここまで具体化すると、「どのキーで検索したいのか」「どのデータを一緒に取得したいのか」「並び順は必要か」といった設計上の論点が明確になります。
さらに、読み取りだけでなく書き込みも整理する必要があります。
注文作成時にどのデータを追加するのか、ユーザー情報を更新したときに複製されたデータも更新する必要があるのか。
DynamoDBでは非正規化を積極的に利用することがあるため、読み取りだけを見て設計すると、今度は更新処理が複雑になる可能性があります。
したがって、アクセスパターンの一覧には、少なくとも「操作内容」「検索条件」「取得するデータ」「想定される頻度」などを整理しておくとよいでしょう。
ここで重要なのは、単に検索条件を並べることではありません。
システムにとって重要なアクセスを優先順位付きで把握することです。
「検索できるか」ではなく「効率よく取得できるか」で考える
DynamoDBの設計でありがちな誤解が、「データが検索できればよい」という考え方です。
実際には、検索できることと、効率よく検索できることは別問題です。
DynamoDBでは特に、キーを利用して必要なデータを絞り込めるかどうかが重要になります。
たとえば、数百万件の注文データから「特定ユーザーの注文だけを探す」という要件があるとします。
全データを読み出してアプリケーション側でユーザーIDを比較するような設計は、機能としては実現できても、コストや性能の面で現実的ではありません。
アクセス対象をキーによって効率よく限定できるデータモデルにする必要があります。
この視点を持つと、「このAPIはどのキーでQueryできるのか」「必要なデータを1回、あるいは少ない回数の読み取りで取得できるか」といった問いが自然に出てきます。
これはDynamoDBの設計レビューでも非常に有効な観点です。
将来のアクセスパターンまで考えすぎない
一方で、すべての将来要件を完璧に予測しようとするのも危険です。
「将来こういう検索が必要になるかもしれない」と考えてインデックスや複雑なデータ構造を大量に追加すると、設計も運用も分かりにくくなります。
現実的なのは、現在の要件と高い確度で見込まれる主要なアクセスパターンを中心に設計し、変更が発生した場合にどこまで対応できるかをあらかじめ確認しておくことです。
DynamoDBでは、RDBのように「とりあえず保存しておいて、後からSQLで好きなように取り出す」という自由度が低いからこそ、要件とデータモデルの境界を明確にしておく必要があります。
アクセスパターンから逆算する設計は、最初こそ少し面倒です。
しかし、ここを丁寧に行えば、パーティションキー、ソートキー、GSI、非正規化といったDynamoDB特有の設計判断にも一貫性が生まれます。
「このキーは何のために存在するのか?」に対して、具体的なAPIや画面を答えられる状態を目指すこと。
それが、DynamoDBのテーブル設計を破綻させないための基本です。
DynamoDBのパーティションキーとソートキーを正しく設計する

DynamoDBのテーブル設計で最も重要な要素の一つが、パーティションキーとソートキーです。
RDBに慣れていると「主キーを決めればよい」と考えがちですが、DynamoDBではキーの選び方が、単なるレコード識別だけでなく、データの配置やアクセス方法、さらには性能にも直結します。
ここを適当に決めてしまうと、後からアプリケーション側で無理にデータを加工することになり、DynamoDBのメリットを十分に引き出せません。
特に重要なのは、キーをデータそのものの属性としてではなく、アクセスパターンを実現するための仕組みとして考えることです。
「このデータには何という名前を付けるか」ではなく、「このデータをどの単位で取得したいのか」「その中でどのような順序や条件を使うのか」という観点から設計します。
パーティションキーは「何をひとまとまりとして取得するか」で考える
パーティションキーは、DynamoDBでデータへアクセスするときの中心となるキーです。
パーティションキーだけを持つテーブルでは、その値によってアイテムを識別します。
パーティションキーとソートキーを組み合わせる複合主キーの場合は、同じパーティションキーを持つ複数のアイテムを格納し、それらをソートキーで区別できます。
ここで大切なのが、「一意なIDなら何でもよい」と考えないことです。
もちろんUUIDのような一意な値をパーティションキーにする設計もあります。
しかし、それだけでアプリケーションが必要とするアクセスパターンを効率よく実現できるとは限りません。
たとえばユーザーごとの注文履歴を頻繁に取得するなら、ユーザーIDを軸にデータをまとめるほうが自然な場合があります。
たとえば次のような要件を考えてみましょう。
- ユーザーIDを指定して、そのユーザーの注文を取得したい
- 注文は複数存在する
- 注文を注文日時の新しい順に表示したい
- 特定期間の注文だけ取得したい
この場合、ユーザーIDをパーティションキーとして扱い、注文日時などをソートキーに利用する設計が候補になります。
すると、「ユーザーAの注文」というまとまりの中から、ソートキーを使って対象期間を絞り込んだり、並び順を指定したりできます。
これは「ユーザーと注文を別々のテーブルにして、あとでJOINする」というRDB的な発想とはかなり異なります。
DynamoDBでは、最初から「ユーザーIDを起点に注文を取得する」というアクセス方法に合わせてデータを配置するわけです。
ソートキーは並び順だけでなく検索条件にも使う
ソートキーという名前から、「データを並べ替えるためだけのもの」と思ってしまう人もいます。
しかし、実際にはそれだけではありません。
同じパーティションキーを持つアイテムを分類し、範囲条件などを利用して必要なデータを効率的に取得するためにも重要な役割を持ちます。
たとえば注文日時をソートキーにすれば、「2026年9月の注文」といった期間を意識した取得がしやすくなります。
また、ソートキーに状態や種別などの情報を組み込むことで、一定のアクセスパターンに対応できる場合もあります。
重要なのは、単純に「日付だからソートキー」という発想ではなく、そのキーを使ってどんなQueryを実行したいのかを先に決めることです。
一方で、ソートキーに複数の情報を詰め込みすぎると、データ構造が分かりにくくなることがあります。
区切り文字を使って複数の属性を連結する設計は強力ですが、アプリケーション側でその形式を正しく扱う必要があります。
将来的な仕様変更でキーのフォーマットを変えたくなったときにも影響範囲が広がるため、便利だからという理由だけで複雑化させるのは避けたいところです。
ホットパーティションを避けるにはアクセスの偏りを見る
パーティションキーの設計では、もう一つ重要な観点があります。
それがアクセスの偏りです。
DynamoDBは分散システムとしてデータと負荷を扱うため、特定のパーティションキーにリクエストが集中すると、設計上のボトルネックになる可能性があります。
たとえば、全ユーザーが共通して使う固定値をパーティションキーにしてしまうと、多数の読み書きが同じパーティションキーに集中することになります。
機能としては動いていても、アクセス量が増えたときに負荷が偏る設計です。
これがいわゆるホットパーティションにつながる考え方です。
したがって、キーを設計するときには「検索できるか」だけでなく、「アクセスが一部のキーに集中しないか」も確認する必要があります。
特に、ランキングや最新情報、イベントログなど、特定のキーへ大量の書き込みが発生するシステムでは注意が必要です。
もちろん、パーティションキーを無理にランダム化すればよいわけでもありません。
アクセス効率を落としてしまえば本末転倒です。
必要なのは、アクセスパターンとデータ分布の両方を見ること。
読み取りや書き込みをどの単位に分散させるべきかという視点です。
複合主キーでは「1つのパーティションに何を集めるか」を決める
パーティションキーとソートキーを組み合わせる場合、最初に考えたいのは「同じパーティションキーを持つアイテムを何の単位でまとめるか」です。
ユーザー単位なのか、店舗単位なのか、注文単位なのか、それともテナント単位なのか。
ここが決まるとソートキーの役割も見えやすくなります。
たとえばマルチテナント型のSaaSなら、テナントIDをパーティションキーにして、その配下にユーザーや設定、イベントなどをまとめる設計が考えられます。
一方で、テナントごとのデータ量やアクセス量が極端に異なるなら、単純にテナントIDだけをキーにするのが最適とは限りません。
大規模なテナントだけに負荷が集中する可能性があるためです。
ここでも「キーとして一意か」という問いだけでは不十分です。
「このキーを指定したとき、どの範囲のデータを取得できるのか」「そのキーへのアクセスはどれくらい集中するのか」という問いをセットで考える必要があります。
RDBの主キー感覚で決めると設計を誤りやすい
RDBでは、主キーはレコードを一意に識別するという意味合いが非常に強いものです。
そのためDynamoDBでも、「とりあえずIDをパーティションキーにしよう」と考えるのは自然です。
しかし、DynamoDBではキーがアクセスパターンを決めるという側面が強いため、識別子として便利なだけでは不十分です。
たとえば注文IDをパーティションキーにすれば、注文IDから注文詳細を取得する処理は非常に分かりやすくなります。
しかし、「ユーザーIDから注文履歴を一覧取得したい」という要件には、そのキーだけでは直接対応できません。
別のインデックスが必要になるか、そもそものデータモデルを見直すことになります。
だからこそ、キー設計では「一意性」よりも先に「アクセス」を確認します。
どのAPIがどのキーを使うのか。
そのAPIが必要とするデータを効率よく取得できるか。
そして、読み書きが偏らないか。
この3点を順番に確認すると、設計の意図がかなり明確になります。
DynamoDBのパーティションキーとソートキーは、単なるIDの置き場ではありません。
データをどう配置し、どう取得し、どうスケールさせるかを決める設計上の中心部品です。
RDBの主キー設計をそのまま持ち込むのではなく、まずアクセスパターンを定義し、そのアクセスを最小限の読み取りで実現できるキーを逆算する。
この考え方を身につけることが、DynamoDBを使いこなすための大きな一歩になります。
RDBの正規化を捨てる?DynamoDBで非正規化する理由と設計方法

RDBを学んできたエンジニアほど、DynamoDBのデータ設計で最初に違和感を覚えるのが「同じデータを複数箇所に持っていいのか?」という問題です。
RDBでは、データの重複を減らし、更新時の不整合を防ぐために正規化を行うのが基本です。
ユーザー情報ならユーザーテーブル、商品情報なら商品テーブル、注文なら注文テーブルというように責務を分け、必要なときにJOINして組み立てる。
この考え方は非常に合理的です。
ところがDynamoDBでは、正規化しすぎることが、かえって性能や実装の複雑さにつながる場合があります。
DynamoDBではJOINを前提としたデータ取得ができないため、複数のテーブルに分散したデータを毎回別々に読み込み、アプリケーション側で結合する設計では、読み取り回数が増えてしまいます。
そこで登場するのが非正規化です。
ただし、ここで「DynamoDBでは正規化を捨てればいい」と理解するのは危険です。
非正規化は、データを適当に重複させることではありません。
どのアクセスパターンを高速化したいのか、そのためにどのデータを同じ場所へ持たせるのか、そして重複したデータをどのように更新するのか。
これらを明確にしたうえで行う、意図的なデータ設計です。
なぜDynamoDBでは非正規化が重要なのか
RDBでは、たとえば注文情報とユーザー情報を別テーブルに保存しておき、注文を取得するときにJOINすれば必要な情報をまとめられます。
ユーザー名が変更されても、ユーザーテーブルを更新するだけで済みます。
データの一貫性を維持しやすい、という大きなメリットがあります。
一方、DynamoDBでは「注文IDから注文を取得し、その注文に紐づくユーザー情報も必要」という要件に対して、毎回複数のアイテムを別々に取得する設計を採用すると、読み取り処理が複雑になります。
そこで、注文を表示するために必要なユーザー名などを注文データ側にも保持しておけば、1回のアクセスで画面に必要な情報を揃えやすくなります。
これは一見するとデータベース設計の原則に反しているように見えます。
しかし、DynamoDBでは「データを重複させないこと」よりも、「必要なアクセスパターンを効率よく処理できること」を優先するケースがあります。
読み取り性能とアクセス効率を得る代わりに、更新時の複雑さを引き受けるというトレードオフです。
非正規化は「読み取り最適化」として考える
非正規化を設計するときは、まず「どの画面やAPIで、どのデータが一緒に必要なのか」を考えます。
たとえばECサイトの注文一覧画面で、注文番号、注文日時、合計金額、配送先の名前を表示するとしましょう。
この画面のために毎回注文データとユーザーデータを別々に取得するより、一覧表示に必要な値を注文側へ複製しておくほうが合理的な場合があります。
ここで重要なのは、すべての属性を複製する必要はないということです。
ユーザーの住所や電話番号など、頻繁に変更される情報まで無条件にコピーすると、更新処理が急激に難しくなります。
一方、注文確定時点の配送先名や商品名など、「その注文が作成された時点の情報として保持したい」属性なら、むしろコピーすることに意味があります。
つまり、非正規化する属性には理由が必要です。
「取得時に必要だから」「履歴として当時の値を保持したいから」「複数回の読み取りを避けたいから」といった設計意図を説明できる状態が理想です。
単に「DynamoDBだから重複させる」というルールにしてしまうと、後からデータの整合性を維持できなくなります。
データの複製には更新戦略が必要
非正規化で最も注意したいのが、データの更新です。
同じユーザー名を複数の注文アイテムにコピーしていた場合、ユーザー名を変更したときに、それらすべてを更新する必要があるのでしょうか。
答えは要件によって変わります。
もし「注文履歴には注文時点のユーザー名を表示したい」という仕様なら、過去の注文データを更新する必要はありません。
むしろ、過去の注文まで現在のユーザー名に書き換えてしまうほうが要件違反になる可能性があります。
これは非正規化によって、単なるコピーではなく「履歴としてのスナップショット」を作っている例です。
逆に、「常に最新のユーザー名を注文一覧に表示する」という仕様なら、ユーザー情報の変更時に複製先も更新する必要があります。
この場合は、更新対象のアイテムをどう特定するのか、更新処理が大量になった場合にどうするのかまで設計しなければなりません。
非正規化を採用すると、RDBではデータベース側の制約やJOINによって自然に解決していた問題を、アプリケーションの責務として引き受けることがあります。
読み取りを簡単にする代わりに、書き込みと整合性の管理が難しくなる。
この関係を理解しておくことが重要です。
1つのデータを複数のアクセスパターンに合わせて持つ
DynamoDBでは、同じ論理的なデータを異なるアクセスパターンに対応させるため、複数の形で保持することもあります。
たとえば商品を「商品IDから取得する」だけでなく、「カテゴリから一覧取得する」「販売者から一覧取得する」といった複数の検索方法が必要なら、それぞれのアクセスを効率よく実現できるようにキーやインデックス、アイテム構造を設計します。
RDBなら、商品テーブルを1つ用意して複数のインデックスやSQLで対応する発想が自然です。
しかしDynamoDBでは、アクセスパターンそのものをデータモデルに反映させることがあります。
場合によっては、同じ情報を別のキー構造で参照できるように設計することも選択肢になります。
ただし、複製を増やせば増やすほど更新処理は複雑になります。
ここでも「何でも複製」ではなく、主要なアクセスパターンに絞ることが大切です。
読み取りのたびに複雑な処理をするのか、それとも書き込み時に必要な形を作っておくのか。
DynamoDBでは、後者を選ぶことで読み取りをシンプルにできるケースが多くあります。
正規化を捨てるのではなく、目的に応じて使い分ける
ここまで読むと、「DynamoDBでは正規化は不要なのか」と思うかもしれません。
しかし、そうではありません。
正規化には、データの重複を減らし、更新異常を防ぎ、データ構造を論理的に整理するという重要な価値があります。
特にマスターデータや頻繁に更新される情報では、正規化された構造が有利なこともあります。
DynamoDBで重要なのは、正規化か非正規化かの二択にすることではありません。
アクセスパターンごとに、どのデータをまとめ、どのデータを分離し、どこで整合性を担保するのかを判断することです。
たとえば「ユーザーの現在のプロフィール」は独立して管理しつつ、「注文時点のユーザー名」は注文データに保持する、といった設計もできます。
これは正規化と非正規化を目的に応じて使い分けているわけです。
RDBの経験が長いほど、データの重複を見ると「悪い設計ではないか」と反射的に考えてしまいがちです。
しかしDynamoDBでは、重複していること自体が問題なのではなく、その重複をなぜ許容しているのか説明できないことが問題です。
非正規化によって得られるのは、単なる高速化だけではありません。
アクセスパターンに必要なデータをあらかじめまとめておくことで、アプリケーションの読み取り処理そのものを単純化できます。
その代わり、データの複製、更新、整合性、障害時の再同期など、新しい責務が発生します。
結局のところ、DynamoDBの非正規化は「正規化を捨てる」という話ではありません。
読み取りを中心としたアクセスパターンに合わせて、データの重複を意図的に設計するという話です。
RDBで身につけた正規化の知識を捨てる必要はありません。
それを前提として、DynamoDBでは「このデータをどう取得したいのか」という問いをさらに上位に置く。
この発想の転換ができるかどうかが、DynamoDBのデータモデリングを成功させる大きな分岐点になります。
DynamoDBのGSI設計で複数の検索パターンに対応する

DynamoDBでアクセスパターンを設計していると、「この条件でも検索したい」という要件がほぼ必ず出てきます。
メインのパーティションキーだけで必要なアクセスをすべて表現できれば理想ですが、現実のアプリケーションはそれほど単純ではありません。
ユーザーIDから取得したい一方で、メールアドレスからも探したい。
注文をユーザー単位で一覧表示したい一方で、管理画面では注文ステータスと日時を軸に検索したい。
こうした複数のアクセスパターンに対応するための重要な仕組みがGSI、つまりグローバルセカンダリインデックスです。
GSIを理解するときに重要なのは、RDBのインデックスと完全に同じものだと考えないことです。
もちろん、検索しやすくするための仕組みという意味では似ています。
しかしDynamoDBでは、GSIをどのアクセスパターンのために用意するのかを、テーブル設計の段階から明確にする必要があります。
インデックスを後から適当に追加するのではなく、アプリケーションの読み取り要件とセットで考えることがポイントです。
GSIは別の検索軸を作るための仕組み
DynamoDBのテーブルには、基本となる主キーがあります。
ところが、主キーだけでは取得できない検索条件が存在します。
たとえばユーザーIDをパーティションキーとして設計した注文データがあったとしても、「注文IDを指定して取得する」「ステータスが発送待ちの注文を探す」といったアクセスを、その主キーだけで効率よく実現できるとは限りません。
そこでGSIを使います。
GSIでは、元のテーブルとは異なるパーティションキーやソートキーを設定できるため、同じデータを別のアクセス軸から検索できるようになります。
言い換えるなら、1つのデータセットに対して、アプリケーションが必要とする別の入口を作るようなものです。
たとえば注文データに「ユーザーID」「注文ID」「注文ステータス」「注文日時」があるとします。
ユーザーIDをメインテーブルのパーティションキーとして使い、ユーザーごとの注文履歴を取得できるようにします。
一方、管理画面では「発送待ちの注文を古い順に処理したい」という要件があるなら、ステータスをGSIのパーティションキー、注文日時をソートキーとして利用する設計が考えられます。
この場合、同じ注文データを「ユーザー」という軸と「ステータス」という軸から効率よく参照できます。
RDBなら複数のインデックスを作り、SQLのWHERE句やORDER BY句で対応することが多いでしょう。
DynamoDBでも似た目的は達成できますが、どのインデックスがどのアクセスパターンを支えるのかを、より明示的に設計する必要があります。
GSIはアクセスパターンから逆算して設計する
GSI設計でありがちな失敗は、「将来検索するかもしれないから、とりあえずインデックスを追加しておこう」という発想です。
確かにGSIは便利ですが、インデックスを増やすほど管理対象が増え、書き込み時の負荷やストレージ利用量、コストなどにも影響します。
何より、設計者自身が「このGSIは何のために存在するのか」を説明できなくなります。
まず必要なのは、アプリケーションのアクセスパターンを具体化することです。
たとえば次のように整理します。
- ユーザーIDから注文履歴を取得する
- 注文IDから注文詳細を取得する
- ステータスごとの注文一覧を取得する
- ステータスと期間を指定して注文を絞り込む
このうちメインテーブルのキーだけで対応できないものがGSIの候補になります。
そして、それぞれの検索で「どの値をパーティションキーにするか」「どの値をソートキーにするか」を検討します。
ここで大切なのは、GSIの数を最小化することだけではありません。
1つのGSIで複数の関連するアクセスパターンを効率よく満たせるかという視点も重要です。
たとえばステータスをパーティションキー、日時をソートキーにすることで、「発送待ちの注文を取得する」と「発送待ちの注文を期間指定で取得する」の両方に対応できる場合があります。
GSIのパーティションキーとソートキーをどう決めるか
GSIでも基本的な考え方はメインテーブルのキー設計と同じです。
まず、どの単位でデータをまとめたいのかを決めます。
次に、そのまとまりの中でどのような条件で絞り込みたいのか、どの順番で取得したいのかを考えます。
たとえば「未発送の注文を古い順に処理する」という要件なら、ステータスをパーティションキー、注文日時をソートキーにする構造が考えられます。
これによってステータスを指定して対象を絞り、その中から日時の範囲や順序を利用して処理できます。
ただし、ステータスの種類が少ない場合には注意が必要です。
「未発送」「発送済み」「キャンセル」といった少数の値しか存在しないなら、アクセスが特定のパーティションキーに偏る可能性があります。
特に「未発送」に大量の注文が集中し、管理画面がそこへ頻繁にアクセスするようなシステムでは、負荷分散について検討する必要があります。
つまり、GSIのキーは「検索しやすい属性」を選べば終わりではありません。
検索条件としての使いやすさと、アクセスの分散性を同時に評価する必要があります。
GSIに何でも載せればよいわけではない
GSIを設計するときは、インデックスに投影する属性についても考える必要があります。
GSIは検索のためだけに存在するのではなく、検索した結果としてアプリケーションが何のデータを必要とするのかも重要です。
たとえば注文一覧画面で必要なのが注文ID、注文日時、金額、ステータスだけなら、その用途に必要な属性を中心に考えられます。
一方で、検索結果からさらに元テーブルへアクセスして詳細情報を取得する設計なら、GSIの検索結果とメインテーブルの読み取りを組み合わせることになります。
ここで「全部の属性をGSIへ投影しておけば便利では?」と考えるかもしれません。
しかし、GSIへ保持する属性が増えれば、その分だけストレージや書き込みに関するコストも増えます。
便利さだけでなく、実際のアクセス頻度やデータサイズを考慮する必要があります。
GSIを増やす前にデータモデルを見直す
検索要件が増えるたびにGSIを追加していくと、最終的には多数のインデックスを持つテーブルになることがあります。
もちろん複数のGSIが必要なシステムもありますが、「GSIを追加すればすべて解決」という考え方には注意が必要です。
たとえば、あるGSIを追加しても別の検索条件には対応できないから、さらにGSIを追加する。
この繰り返しが続くなら、そもそものデータモデルがアクセスパターンに合っていない可能性があります。
複数の検索軸が必要なら、GSIだけでなく、アイテムの構造やパーティションキー、ソートキーの組み合わせそのものを見直すべき場合もあります。
特に重要なのは、「このGSIはどのAPIのために存在するのか」を明確にすることです。
APIとGSIの対応関係が説明できれば、不要になった機能とともにGSIを整理する判断もしやすくなります。
逆に用途が曖昧なインデックスは、将来の設計変更時に負債になりやすいものです。
GSIはRDBの「自由な検索」の代わりではない
RDBのインデックスに慣れていると、GSIを「DynamoDB版のインデックス」として考えたくなります。
しかし、ここには少し注意が必要です。
RDBではインデックスを組み合わせながら、SQLによって比較的自由度の高い検索を構築できます。
一方、DynamoDBでは、キーを利用した効率的なアクセスを中心に設計することが基本です。
つまり、「あとでGSIを追加すれば、どんな検索条件にも対応できる」と考えるのは危険です。
検索条件そのものをデータモデルに反映させる必要があるため、要件定義の段階で主要なアクセスパターンを把握しておくことが重要になります。
GSIは、DynamoDBを柔軟にするための便利な機能です。
しかし、その本質は「好きな条件で検索できるようにする魔法」ではありません。
あらかじめ想定したアクセスパターンに対して、別の効率的な検索軸を提供する仕組みです。
したがって、GSIを設計するときは、まずアクセスパターンを洗い出し、その中でメインテーブルのキーだけでは対応できない検索を特定します。
そのうえで、検索対象のまとまり、並び順、アクセス頻度、データの偏り、投影する属性、運用コストまで確認する。
ここまで考えて初めて、GSIは単なる「検索用インデックス」ではなく、DynamoDBのデータモデルを構成する重要な設計要素になります。
DynamoDBでJOINできない問題をどう解決する?データ取得を再設計する

RDBからDynamoDBへ移行すると、多くのエンジニアが最初に戸惑うのがJOINの扱いです。
RDBでは、ユーザーと注文、注文と商品といった関連データを別々のテーブルに保存しておき、必要になったタイミングでSQLのJOINを使って組み合わせるのが一般的です。
データを正規化していても、問い合わせ側で必要な形に組み立てられる。
この柔軟性こそ、RDBの大きな強みです。
一方、DynamoDBではRDBと同じ意味でのJOINを前提にしたデータ取得はできません。
そのため、「JOINがないから不便だ」と考えるより、そもそもJOINが必要になるようなデータ構造を作らないという方向へ発想を切り替える必要があります。
これは単にJOINを別の処理で代替するという話ではありません。
データの持ち方と、アプリケーションがデータを読む方法を同時に再設計するという話です。
JOINをアプリケーション側で再現するだけでは解決にならない
DynamoDBで関連データを取得する方法として、最初に思いつきやすいのが「複数回読み取ってアプリケーション側でJOINすればいい」という方法です。
たとえば注文IDから注文を取得し、そこに含まれるユーザーIDを使ってユーザー情報を取得する。
さらに商品IDを使って商品情報を取得する、といった処理です。
小規模なデータや低頻度の処理なら、この方法でも十分な場合があります。
しかし、アクセス量が増えると話が変わります。
1回の画面表示のために複数回の読み取りが必要になれば、レイテンシが増えます。
注文一覧を表示するために数十件の注文を取得し、それぞれについてユーザー情報や商品情報を追加取得するような実装になれば、いわゆるN+1問題に近い状況も発生します。
つまり、「JOINができないからアプリケーションでJOINする」というだけでは、DynamoDBへ移行した意味が薄れてしまいます。
もちろん複数回の読み取りが必要なケースはありますが、まず検討すべきなのは、必要なデータを最初から一緒に取得できるデータモデルにできないかということです。
画面やAPI単位で必要なデータをまとめる
DynamoDBでは、データをエンティティ単位だけで考えるのではなく、実際のユースケース単位で考えることが重要です。
たとえばECサイトに「注文詳細画面」があるとします。
この画面では、注文情報だけでなく、購入者の名前、配送先、商品名、数量、価格などを一度に表示したいかもしれません。
RDBなら、注文、ユーザー、注文詳細、商品といった複数のテーブルをJOINして、この画面に必要なデータをSQLで作るのが自然です。
DynamoDBでは、逆に「注文詳細画面で何が必要なのか」を先に定義し、その画面を効率よく表示できるようにデータを配置することを考えます。
たとえば注文が確定した時点のユーザー名や商品名、価格などを注文データに保持しておけば、注文詳細を取得するだけで画面表示に必要な情報の多くを揃えられます。
これは前章で扱った非正規化の考え方そのものです。
ここで重要なのは、データを「正規化されたエンティティ」として見るのではなく、アプリケーションが実際に必要とする読み取り結果を中心にデータモデルを設計することです。
関連データを同じパーティションに集約する
DynamoDBでは、関連するデータを同じパーティションキーの下にまとめる設計がよく使われます。
いわゆるシングルテーブル設計では、異なる種類のエンティティを1つのテーブルに格納し、パーティションキーとソートキーの組み合わせによって関連性を表現します。
たとえばユーザーIDを軸に、そのユーザー自身のプロフィールと注文履歴を同じパーティションへ集約することが考えられます。
そうすれば、ユーザーIDを指定したQueryによって、関連する複数種類のアイテムをまとめて取得できます。
ただし、何でも同じパーティションへ詰め込めばよいわけではありません。
1つのパーティションに大量のデータやアクセスが集中すれば、別の問題が発生します。
また、異なるデータを同じ場所に集約すると、アプリケーション側でアイテムの種類を判別する必要も出てきます。
シングルテーブル設計の目的は「テーブルを1つにすること」ではありません。
関連するアクセスを少ない読み取りで実現するために、データを物理的に近い形で配置することが本質です。
データを一緒に取得できる構造を作る
JOINを避けるためのもう一つの方法が、1つのアイテムに必要な情報をまとめることです。
特に、常に一緒に読み取るデータであれば、別々のアイテムに分ける理由が薄くなる場合があります。
たとえばブログ記事と記事のメタ情報を考えてみましょう。
タイトル、本文、公開日時、著者名などが常に記事詳細画面で必要なら、これらを同一アイテムに保持することで、1回の取得で必要な情報を揃えられます。
ただし、アイテムを巨大化させるのは別の問題を招きます。
DynamoDBにはアイテムサイズの上限があるため、何でも1つに詰め込めるわけではありません。
また、更新頻度が異なるデータまで一体化すると、不要な書き換えが発生することもあります。
したがって、「一緒に取得するデータ」と「一緒に管理するデータ」は必ずしも同じではありません。
読み取りのまとまり、更新頻度、データサイズ、整合性の要件を見ながら境界を決める必要があります。
複数回の読み取りが必要なケースもある
ここまで読むと、「DynamoDBでは絶対に複数回読み取ってはいけない」と感じるかもしれません。
しかし、それも極端な考え方です。
すべての関連データを無理に1回のQueryへ詰め込むと、かえってデータモデルが複雑になります。
たとえば、ユーザーが注文詳細を開いたときに、現在のユーザープロフィールを別途取得する設計が合理的な場合もあります。
注文履歴に必要なのは注文時点の情報、プロフィール画面に必要なのは現在の情報、というように用途が異なるなら、同じデータを無理に統合する必要はありません。
また、取得頻度が低い情報や、キャッシュできる情報なら、別アクセスに分けても大きな問題にならない場合があります。
重要なのは「読み取り回数を必ず1回にする」ことではなく、システム全体として許容できるレイテンシ、コスト、実装の複雑さのバランスを取ることです。
JOINをなくす代わりに何を犠牲にするのか
JOINを避ける設計には、当然ながらトレードオフがあります。
データを非正規化すれば、読み取りは高速かつ単純になります。
しかし、その代わりに同じ情報を複数箇所へ保持することになり、更新処理が複雑になる可能性があります。
たとえば商品名を注文アイテムにコピーした場合、商品マスターの名前を変更しても過去の注文データを更新する必要はないかもしれません。
むしろ、注文時点の商品名を保持するほうが履歴として正しいでしょう。
一方、常に最新の商品情報を表示したい場合には、別の取得処理や更新戦略が必要になります。
このように、JOINをなくすこと自体が目的ではありません。
JOINによって得ていた柔軟性を手放す代わりに、読み取り性能やスケール特性を得る。
そして、データの整合性をどこで管理するのかを明確にする。
ここまで含めて設計する必要があります。
RDBのJOINをDynamoDBへそのまま移植しない
RDBからDynamoDBへ移行するときに最も避けたいのは、既存のテーブル構造をそのままDynamoDBへ移植し、「JOINだけアプリケーション側で再現する」という設計です。
これは技術的には動作しても、DynamoDBの強みを活かせないことが多いからです。
まず既存システムのSQLを眺めるのではなく、SQLが最終的に何のために実行されているのかを確認します。
そのSQLが注文一覧を表示するためなら、注文一覧というアクセスパターンを起点にデータモデルを考え直します。
管理画面の検索なら、その検索条件を満たすキーやGSIを設計します。
つまり、移行の対象はテーブルだけではありません。
SQLによって実現していた「読み取りのユースケース」そのものをDynamoDB向けに再設計する必要があります。
DynamoDBには、RDBのJOINを完全に置き換える便利な機能があるわけではありません。
だからこそ、JOINが必要だった理由を分析することが重要です。
関連データを組み合わせる必要があったのか、最新情報を参照する必要があったのか、複数の条件で柔軟に検索する必要があったのか。
理由が分かれば、その要件を満たすためのデータ配置やGSI、非正規化の方法を選べます。
結局のところ、DynamoDBでJOINできないことは、単純な弱点として捉えるべきものではありません。
RDBでは「保存したデータを後からJOINして必要な形にする」のが得意でした。
DynamoDBでは「必要な形で読み取れるように、あらかじめデータを設計しておく」ことが重要です。
JOINをなくすのではなく、JOINが必要になる設計を見直す。
この発想に切り替えられるかどうかが、RDBからDynamoDBへの移行を成功させる重要なポイントになります。
DynamoDBの整合性とトランザクションをRDBと比較して考える

DynamoDBへ移行するとき、JOINと並んで大きな壁になりやすいのが整合性の考え方です。
RDBでは、トランザクションを使って複数の更新をひとまとまりとして扱い、ACID特性を前提にデータを設計することが一般的です。
「この処理では、AとBの両方が更新されるか、どちらも更新されない」と考えられるため、業務ロジックを比較的素直に表現できます。
一方のDynamoDBは、分散システムとして高いスケーラビリティと可用性を実現することを重視して設計されています。
そのため、RDBとまったく同じ感覚で整合性を考えると、思わぬ設計上の問題が発生します。
ただし、「DynamoDBは整合性が弱い」「トランザクションがない」と理解するのも正確ではありません。
DynamoDBにも強い整合性読み取りやトランザクション機能が存在します。
重要なのは、どの処理にどの程度の整合性が必要なのかを分解して考えることです。
まず「整合性が必要なデータ」を見分ける
整合性という言葉を使うと、すべてのデータを常に最新かつ完全に一致させなければならないように感じます。
しかし、実際のシステムでは、データによって必要な整合性レベルは異なります。
たとえばSNSの「いいね数」を考えてみましょう。
表示される数字が数秒程度遅れていても、通常は致命的な問題にはなりません。
一方、銀行口座の残高や在庫数、決済状態などは話が違います。
古い値をもとに処理すると、二重決済や在庫超過といった業務上の重大な不整合につながる可能性があります。
したがって、DynamoDBへ移行する際には、単純に「RDBと同じ整合性を再現する」ことから始めるのではなく、各データについて次のような問いを立てるべきです。
- 読み取り時に最新値である必要があるか
- 複数のデータを同時に更新する必要があるか
- 更新途中の状態が外部から見えても問題ないか
- 一時的な不整合を許容できるか
- 不整合が発生した場合に後から修復できるか
この整理をすると、すべての処理を強い整合性やトランザクションで囲む必要はないことが見えてきます。
むしろ、何でも強い整合性に寄せるほうが、DynamoDBの特性を活かせないケースもあります。
DynamoDBの読み取り整合性を理解する
DynamoDBの読み取りでは、結果整合性と強い整合性という考え方があります。
結果整合性では、書き込みが成功した直後に読み取った場合でも、最新の値がすぐには返らない可能性があります。
一方、強い整合性を指定した読み取りでは、成功した書き込みを反映した最新の値を取得できます。
ここで重要なのは、「強い整合性を選べば常に正解」というわけではないことです。
強い整合性が必要な処理だけに限定し、それ以外では結果整合性を許容することで、システム全体のコストや性能を適切に設計できます。
たとえば、ユーザーがプロフィールを更新した直後に、自分自身のプロフィール画面を表示するケースでは、更新直後の読み取り結果が重要になる場合があります。
一方、ランキングや集計結果などでは、多少の遅延を許容できることがあります。
この違いを意識せず、「RDBなら更新した直後に必ず最新値が見えるからDynamoDBでもそうしたい」と考えると、必要以上に強い整合性を要求する設計になりがちです。
データの性質とユーザー体験を基準に判断することが大切です。
トランザクションは「使えるか」ではなく「どこで使うか」
DynamoDBにはトランザクション機能があり、複数のアイテムに対する書き込みや更新を原子的に扱うことができます。
つまり、DynamoDBだから複数のデータを一括して整合性のある状態に更新できない、という理解は誤りです。
ただし、ここでもRDBと同じ感覚で「とりあえず全部トランザクションに入れる」と考えるのは危険です。
トランザクションには処理上のコストがあり、対象となるアイテム数や処理内容にも制約があります。
また、DynamoDBではアクセスパターンに合わせてデータを配置すること自体が重要なので、トランザクションを多用しなければ成立しないデータモデルになっているなら、そもそもの設計を見直す余地があります。
たとえば注文確定処理を考えてみます。
注文ステータスを確定済みに変更すると同時に、関連する在庫情報を減らす必要があるなら、この2つの更新を原子的に扱いたいケースがあります。
このような業務上の境界が明確な処理では、DynamoDBのトランザクションが有効です。
一方、注文確定後に分析用データを別途記録するといった処理まで、同じトランザクションに含める必要があるでしょうか。
分析データの反映が数秒遅れても問題ないのであれば、非同期処理やイベント駆動に分離したほうが、システム全体としてシンプルになる可能性があります。
ACIDをそのまま移植するのではなく業務上の不変条件を見る
RDBからDynamoDBへ移行する際に特に重要なのが、「ACIDを満たしているか」という技術的な視点だけで判断しないことです。
本当に守るべきなのは、業務上の不変条件です。
たとえば「在庫数は0未満になってはいけない」「支払い済みの注文を未払いに戻してはいけない」「同じ注文を二重に確定してはいけない」といったルールがあります。
これらはデータベースの機能そのものではなく、アプリケーションが守るべき業務上の条件です。
DynamoDBでは条件付き書き込みを利用することで、現在の値が期待した状態である場合だけ更新するといった制御ができます。
これは、RDBで単純なUPDATEを発行していた処理を、そのまま移植するのとは異なる考え方です。
たとえば在庫を減らす処理なら、「在庫が1以上であること」を条件に更新することで、複数のリクエストが同時に処理された場合でも、条件を満たさない更新を拒否できます。
単純に読み取ってから計算し、その結果を書き戻すだけでは競合に弱くなりますが、条件付き更新を利用すればデータベース側で重要な条件を検証できます。
つまり、DynamoDBでは「ロックしてから更新する」という発想だけに頼らず、競合が発生することを前提に、条件付き操作で不変条件を守るという設計が重要になります。
非同期処理と最終的整合性を味方にする
DynamoDBを活用するうえでは、すべての処理を同期的かつ完全に一致させようとしないことも重要です。
特に大規模なシステムでは、業務処理と周辺処理を分離することで、スケールしやすい構成を作れます。
たとえば注文が確定したら、注文情報そのものは即座に確定させ、その後にメール送信、分析データ作成、検索用データ更新、通知処理などを非同期で実行する設計です。
この場合、注文の確定とメール送信を同一トランザクションに含める必要はありません。
もちろん、非同期化には重複処理や再実行への対応が必要になります。
イベントが複数回届いても問題が起きないように冪等性を確保したり、処理に失敗した場合のリトライやデッドレターを考えたりする必要があります。
しかし、これはDynamoDB固有の問題というより、分散システムを設計するうえで避けて通れない問題です。
むしろ、RDBの大きなトランザクションだけに処理を押し込めるより、責務を分割することでシステムの可用性やスケーラビリティを高められる場合があります。
RDBのトランザクション境界をそのまま持ち込まない
RDBからDynamoDBへ移行するとき、既存コードのトランザクション境界をそのまま再現しようとすると、設計が不自然になることがあります。
RDBでは複数テーブルを更新する一連の処理を1つのトランザクションにまとめていたとしても、DynamoDBではそのデータを同じトランザクションで扱う必要がないように再設計できる可能性があります。
たとえば、注文、決済、配送、通知をすべて同期的に更新していた処理があるとします。
これをDynamoDBへ移行するなら、注文確定に本当に必要な状態だけを原子的に更新し、それ以外はイベントを起点に後続処理として分離する方法を検討できます。
ここで大切なのは、トランザクションを減らすこと自体を目的にしないことです。
守るべき整合性は守り、不要な整合性だけを緩める。
その境界を業務要件から決めます。
DynamoDBの整合性設計で難しいのは、機能を知らないことではありません。
RDBで自然に成立していた前提を、一つずつ分解することです。
「このデータは本当に即時一致が必要なのか」「この2つの更新は本当に同時でなければならないのか」「失敗した場合に後から修復できないのか」
こうした問いを繰り返すことで、必要な整合性と不要な整合性を切り分けられます。
DynamoDBは、RDBより整合性が弱いデータベースとして理解するべきではありません。
むしろ、整合性を必要な場所に限定し、条件付き書き込み、トランザクション、非同期処理、結果整合性などを組み合わせて設計するデータベースと考えたほうが本質に近いです。
RDBのトランザクションを再現することではなく、システムが絶対に守るべき状態を定義すること。
そこから逆算するのが、DynamoDBで整合性を扱うときの基本姿勢です。
DynamoDBの料金とパフォーマンスで失敗しないための実践ポイント

DynamoDBを採用するとき、「サーバーを管理しなくていい」「必要な分だけ使える」という点に魅力を感じる人は多いでしょう。
実際、DynamoDBは運用負荷を抑えながら大規模なアクセスを処理しやすいサービスです。
ただし、だからといって「使った分だけ払うので、とりあえず作れば安く済む」と考えるのは危険です。
DynamoDBは、データモデルとアクセスパターンが料金とパフォーマンスに直結するデータベースだからです。
RDBでは、インスタンスのスペックや接続数、ストレージ容量などを見ながら性能とコストを調整する場面が多くあります。
一方DynamoDBでは、読み取り・書き込みの方式、アクセスの集中、アイテムサイズ、インデックスの数、取得するデータ量などが、性能だけでなく料金にも影響します。
つまり、料金最適化とパフォーマンス改善を別々の問題として考えるのではなく、データアクセスそのものを設計対象として扱う必要があります。
DynamoDBの料金は「リクエスト数」だけで考えない
DynamoDBのコストを理解するとき、単純に「アクセスが増えたら料金が増える」と考えるだけでは不十分です。
読み取りや書き込みでは、リクエストそのものだけでなく、読み書きするデータ量やアイテムサイズなどが関係します。
さらに、オンデマンドモードとプロビジョンドモードではキャパシティの考え方も異なります。
特に注意したいのが、1回のリクエストでどれだけのデータを処理しているかです。
必要な属性だけを取得すれば済む処理で、毎回大きなアイテムを読み取っていれば、無駄な読み取りが発生します。
反対に、1件ずつ何度も取得する設計も、処理回数やレイテンシの面で不利になる場合があります。
そのため、コストを抑えるために「とにかくリクエスト数を減らす」と考えるのではなく、必要なデータを、必要な回数で、必要なサイズだけ取得するという視点が重要です。
これはそのままパフォーマンス設計にもつながります。
QueryとScanの違いが性能を大きく左右する
DynamoDBで特に避けたいのが、安易なScanへの依存です。
Scanはテーブル内のアイテムを広く読み取る操作なので、「とりあえず全部取得してアプリケーション側で絞り込む」という設計には向いていません。
データ量が少ない開発環境では問題が見えなくても、本番データが増えた途端に処理時間やコストが膨らむ可能性があります。
一方、Queryはパーティションキーを指定して、そのキーに対応するアイテム群を効率的に取得するための操作です。
ソートキーを適切に設計すれば、範囲条件や並び順を含めたアクセスも実現できます。
ここで重要なのは、「Queryを使うこと」だけを目標にしないことです。
Queryを使えるということは、裏を返せば、そのアクセスパターンに対応するキー設計ができているということです。
つまり、QueryとScanの選択はAPIの使い分けではなく、前章までで扱ってきたアクセスパターンとデータモデルの設計結果として考えるべきです。
管理画面で「条件に合う注文を全部検索したい」という要件が出てきたとき、既存テーブルをScanして解決するのではなく、その検索が本当に必要なのか、GSIで効率的に取得できるのか、検索用データを別に持つべきなのかを検討します。
自由度の高い検索が大量に必要なら、そもそもDynamoDBが適切なのかを再評価することも必要です。
ホットパーティションを避ける
DynamoDBのパフォーマンスを考えるうえで、料金以上に重要なのがアクセスの偏りです。
DynamoDBではパーティションキーをもとにデータが分散されるため、特定のキーに大量のリクエストが集中すると、システム全体では十分なキャパシティがあっても、一部に負荷が偏る可能性があります。
いわゆるホットパーティションです。
たとえば「status = ACTIVE」のように値の種類が少ない属性を、そのままGSIのパーティションキーとして利用するとします。
大量のデータが同じACTIVEというキーに集中すれば、アクセスも特定のパーティションへ偏りやすくなります。
同じ理由で、特定の有名ユーザーや非常にアクセスの多いテナントなど、アクセス頻度に極端な偏りがあるIDをパーティションキーとして利用するときも注意が必要です。
単純に「ユニークな値なら分散する」と考えるのではなく、実際のアクセス分布がどうなるかを見る必要があります。
設計段階では、平均アクセス数だけを見るのも危険です。
平均が低くても、一部のキーだけアクセスが集中していれば問題になります。
実際の利用状況を想定し、誰がどのデータをどの頻度で読むのかまで考えることが重要です。
アイテムサイズと取得データ量を意識する
DynamoDBでは、アイテムに多くの属性を持たせるほど、読み書きのデータ量も大きくなります。
非正規化によって読み取りを効率化できる一方、何でも1つのアイテムへ詰め込むと、1回の読み取りで大量のデータを扱うことになります。
たとえば一覧画面でユーザー名と注文日時だけ表示すればよいのに、画像データや長い説明文、履歴情報まで毎回取得していれば無駄があります。
必要なデータと詳細データを分けることで、読み取り量を抑えられる場合があります。
また、DynamoDBではProjection Expressionを利用して取得する属性を限定できます。
ただし、これも「必要な属性だけ指定すれば常に大幅に安くなる」という単純な話ではありません。
実際の読み取り容量はアイテムサイズなどの要素によって決まるため、仕組みを正確に理解したうえで利用する必要があります。
大切なのは、APIレスポンスのために必要なデータ構造を設計することです。
画面に必要な情報と、データベースに保存している情報は同じとは限りません。
GSIを増やしすぎない
GSIはDynamoDBの柔軟性を高める強力な機能ですが、検索要件が出るたびにGSIを追加していく設計には注意が必要です。
GSIを追加すれば新しいアクセスパターンに対応できますが、その分、データの書き込みやストレージなどにも影響します。
特に「将来使うかもしれないから」という理由だけでインデックスを大量に作るのは避けたいところです。
実際に必要なアクセスパターンを整理し、既存の主キーやGSIで複数の要件をまとめて満たせないかを考えます。
たとえば「ユーザーごとの注文履歴」と「ユーザーごとの注文履歴を注文日時順に取得する」という2つの要件なら、別々のインデックスを作る必要はありません。
1つのキー設計で両方を満たせる可能性があります。
インデックスは無料の検索機能ではありません。
検索要件を増やすたびにインデックスを増やすのではなく、アクセスパターン全体を見て最小限の構成にすることが重要です。
オンデマンドとプロビジョンドを使い分ける
DynamoDBには、主にオンデマンドキャパシティモードとプロビジョンドキャパシティモードという2つの考え方があります。
アクセス量が予測しにくいシステムではオンデマンドが扱いやすく、一定のトラフィックが継続するシステムではプロビジョンドを検討する余地があります。
ただし、「アクセスが多いならプロビジョンド」「少ないならオンデマンド」と単純に決めるべきではありません。
重要なのは、アクセス量の予測可能性と変動幅です。
日中だけアクセスが集中する、毎月決まった日に処理が増える、イベント開催時だけ急激に増えるなど、システムによって特性は異なります。
また、オートスケーリングなどを組み合わせることで、変動するアクセスに対応できる場合もあります。
料金モードの選択は、単純な現在のアクセス量ではなく、実際のトラフィックパターンと運用方針を見ながら判断するべきでしょう。
性能問題を「DynamoDBが遅い」で終わらせない
本番環境で性能問題が発生したとき、「DynamoDBが遅い」と判断するのは早すぎます。
まず、どの操作で時間がかかっているのかを分解する必要があります。
確認したいのは、読み取りなのか書き込みなのか、QueryなのかScanなのか、特定のパーティションにアクセスが集中していないか、アイテムサイズが大きくないか、GSIへの書き込みがボトルネックになっていないか、といった点です。
アプリケーション側の処理時間やネットワーク、外部サービスとの通信が原因である可能性もあります。
観測可能性を高めるために、CloudWatchなどのメトリクスを利用し、消費キャパシティ、スロットリング、レイテンシ、エラーなどを継続的に確認できる状態にしておくことも重要です。
性能問題は、感覚ではなく計測値から追うべきものです。
さらに、負荷試験も有効です。
開発環境の数百件のデータでは問題なくても、本番では数百万件のデータと大きなアクセス偏りが存在するかもしれません。
実際のアクセスパターンに近い条件でテストしておくことで、設計上の問題を早期に発見できます。
DynamoDBの料金とパフォーマンスを最適化するうえで、最も重要なのは小手先の設定変更ではありません。
アクセスパターンを明確にし、Query中心で取得できるキーを設計し、ホットパーティションを避け、必要以上のデータを読み書きせず、GSIを増やしすぎないこと。
これらはすべて、データモデリングの問題です。
つまり、DynamoDBでは「速くする」と「安くする」が同じ設計問題として現れやすいということです。
効率的なキー設計は読み取り性能を高めるだけでなく、不要なデータアクセスを減らし、結果としてコストにも影響します。
逆に、Scanや過剰なGSI、巨大なアイテム、偏ったパーティションといった設計上の問題は、性能と料金の両方を悪化させます。
RDBでは、まず正規化されたデータモデルを作り、必要に応じてインデックスやSQLを調整するという進め方が自然でした。
DynamoDBでは順序が逆です。
まず「どんなアクセスを、どのレイテンシで、どの程度の頻度で処理するのか」を定義し、その要求を満たすデータモデルを作る。
その結果として料金とパフォーマンスが決まっていきます。
ここを理解しているかどうかで、DynamoDBの運用難易度は大きく変わります。
DynamoDBが向いているシステム・向いていないシステムを見極める

ここまでDynamoDBのキー設計、非正規化、GSI、JOIN、整合性、料金とパフォーマンスについて見てきました。
ここまで理解すると、次に気になるのは「結局、どんなシステムならDynamoDBを選ぶべきなのか」という点でしょう。
DynamoDBは、RDBより新しいから優れているわけでも、NoSQLだからスケールするわけでもありません。
重要なのは、システムのアクセスパターンとDynamoDBの設計思想が噛み合っているかです。
逆に言えば、RDBで自然に解決できる問題を無理にDynamoDBへ持っていくと、アプリケーション側の実装や運用が複雑になり、かえってコストが高くなることもあります。
したがって、データベース選定では「DynamoDBを使いたい」という結論から要件を合わせるのではなく、システムの特性を先に整理することが大切です。
DynamoDBが向いているシステムの特徴
DynamoDBが特に力を発揮するのは、あらかじめ想定したアクセスパターンに対して、大量のリクエストを安定して処理したいシステムです。
ユーザーIDを指定してそのユーザーのデータを取得する、注文IDから注文詳細を取得する、テナントIDと日時を指定してイベント履歴を取得するといった、取得方法が比較的明確なケースです。
このようなシステムでは、パーティションキーやソートキーを適切に設計することで、データ量が増えても効率的なアクセスを維持しやすくなります。
アプリケーションから見ると、「この条件で検索したら、その結果を返してほしい」という曖昧な要求ではなく、「このキーを使ってこの範囲のデータを取得する」という明確な処理へ落とし込めます。
代表的なのは、ユーザー向けWebサービスやモバイルアプリ、IoTのイベント蓄積、ゲームのプレイヤーデータ、セッション情報、アクセスログ、メタデータ管理などです。
もちろん、これらのカテゴリなら必ずDynamoDBが適しているという意味ではありません。
重要なのは、それぞれのシステムで実際にどのようなアクセスが発生するかです。
また、急激なトラフィック変動に対応したいシステムとも相性があります。
キャンペーンやイベントなどでアクセスが一時的に増えるサービスでは、固定的なサーバー容量を前提にするより、マネージドなサービスとしてスケールさせる構成が有効な場合があります。
大規模アクセスをシンプルに処理したい場合
DynamoDBを検討する大きな理由の一つが、インフラ運用を比較的シンプルにしながら高いスケーラビリティを確保できることです。
RDBでもリードレプリカ、シャーディング、キャッシュ、接続プールなどを組み合わせれば大規模アクセスへ対応できます。
しかし、アクセス量が増えるにつれて、データベースそのものの運用設計が複雑になることがあります。
DynamoDBでは、最初から分散システムとしてデータを配置することを前提に設計します。
そのため、正しいパーティションキーを選択し、アクセスの偏りを避けることができれば、大量の読み書きを処理するための構成を比較的シンプルに保てます。
ただし、ここには重要な条件があります。
「DynamoDBなら自動的に高速になる」わけではありません。
パーティションキーが偏っていたり、Scanに依存していたり、アクセスパターンに合わないGSIを大量に作っていたりすれば、DynamoDBのメリットを十分に引き出せません。
つまり、DynamoDBは運用を簡単にする代わりに、データモデルについては最初から真剣に考えることを要求するデータベースです。
このトレードオフを理解しておく必要があります。
アクセスパターンが明確なシステムと相性がいい
DynamoDBを評価するときに特に重視したいのが、アクセスパターンの予測可能性です。
たとえば「ユーザーIDからそのユーザーの注文一覧を取得する」「注文IDから注文詳細を取得する」「ステータスと日時から未処理のイベントを取得する」といった要求なら、キーやGSIへ落とし込みやすくなります。
一方、「利用者が任意の項目を組み合わせて検索する」「どの項目を条件にするか実行時まで分からない」といった要求では、DynamoDBの設計は難しくなります。
GSIを増やして対応する方法もありますが、検索条件が増えるほどインデックス設計が複雑になり、RDBのSQLほど自由に扱えるわけではありません。
ここで一つ考えてみたい問いがあります。
検索条件が毎回変わる管理画面を作るとき、その検索条件をすべて事前に予測できるでしょうか。
もし答えが難しいのであれば、それはDynamoDBの採用を慎重に検討すべきサインです。
もちろん、検索用の別システムを組み合わせる方法はあります。
たとえば全文検索や複雑な検索を専用の検索基盤へ分離する設計です。
しかし、その場合はシステム構成が増えるため、「DynamoDBだけで全部解決したい」という発想からは離れる必要があります。
RDBのほうが自然なシステムとは
では、DynamoDBが向いていないのはどのようなシステムでしょうか。
まず考えやすいのは、複雑なリレーションと柔軟な検索が中心となるシステムです。
たとえば、顧客、契約、請求、商品、担当者など、多数のエンティティが複雑に関連し、業務ごとに異なる条件でデータを検索する基幹システムを考えてみます。
このようなシステムでは、JOINや集約、トランザクション、SQLによる柔軟な問い合わせが大きな価値を持ちます。
「この条件で顧客を絞り込み、その顧客に紐づく契約を取得し、さらに契約期間と請求状況を集計する」といった要求が頻繁に変わるなら、RDBのリレーショナルモデルは非常に強力です。
SQLという共通の問い合わせ言語を使って、保存されているデータから必要な結果を後から組み立てられるからです。
このような領域へDynamoDBを導入すると、アクセスパターンごとにデータを再設計し、GSIを追加し、場合によってはデータを重複して保持し、集計用データを別途生成するといった仕組みが必要になることがあります。
技術的には実現できます。
しかし、そこまでしてDynamoDBを使う合理性はあるでしょうか。
データベースの選択では、「できるかどうか」ではなく「その方法が最も自然か」を考えるべきです。
データ構造が頻繁に変わる場合は注意する
DynamoDBはスキーマレスなデータモデルを採用できるため、RDBのような固定スキーマによる制約が少ないことが特徴です。
しかし、「スキーマレスだから仕様変更に強い」と単純に考えるのも危険です。
DynamoDBでは、テーブルの物理的なスキーマ変更が少なくても、アプリケーションが期待するアイテム構造やアクセスパターンは存在します。
そこを変更すると、既存データとの互換性やGSI、読み取り処理などに影響します。
特に、サービスの初期段階で仕様が頻繁に変わり、「そもそもどんなデータをどのように検索するのか分からない」という状態では、アクセスパターンを前提にしたDynamoDBの設計が難しくなります。
ただし、これはDynamoDBが新規サービスに向かないという意味ではありません。
むしろ初期段階から主要なユースケースを整理できるチームなら、非常に相性がよい場合があります。
重要なのは、仕様変更そのものではなく、アクセスパターンの変更をどの程度コントロールできるかです。
強い整合性や複雑なトランザクションが中心なら慎重に考える
金融、在庫、決済などの領域では、データの整合性が特に重要になります。
DynamoDBにも強い整合性読み取りやトランザクション、条件付き書き込みがあるため、「整合性が必要だからDynamoDBは使えない」と決めつける必要はありません。
ただし、複数のエンティティをまたぐ複雑な業務ルールを常に一貫したトランザクションとして扱いたいのであれば、RDBのほうが設計しやすい場合があります。
特に、SQLによる集約やJOINとトランザクションを組み合わせることが業務処理の中心なら、DynamoDBへ移行することで得られるメリットより、設計上の複雑さが大きくなる可能性があります。
逆に、整合性が必要な範囲を明確に限定できるのであれば、DynamoDBでも十分に対応できます。
注文確定と在庫更新だけは原子的に処理し、それ以外の通知や分析データ更新は非同期化する、といった設計です。
ここでも重要なのは、技術名ではなく業務要件です。
DynamoDBかRDBかを感覚で決めない
データベース選定でありがちな失敗は、「RDBは古い」「NoSQLはモダン」「AWSを使うならDynamoDB」といったイメージだけで判断することです。
逆に、「今までRDBで困っていないから、今回もRDB」という判断も同じくらい危険です。
実際には、次のような観点から評価すると判断しやすくなります。
- 主要なアクセスパターンを事前に定義できるか
- 大量アクセスや急激なトラフィック変動が想定されるか
- JOINや複雑な集約がどの程度必要か
- 強い整合性や複雑なトランザクションがどの範囲で必要か
- データの重複を許容できるか
- 検索要件をキーやインデックスへ落とし込めるか
- データベース運用の複雑さをどこまで減らしたいか
この観点で整理すると、「DynamoDBを使うべきか」という抽象的な議論から、「このシステムのこの処理にはどちらが適しているか」という具体的な議論へ変えられます。
RDBとDynamoDBを併用する選択肢もある
さらに重要なのが、システム全体で必ず一つのデータベースを選ぶ必要はないということです。
ユーザー向けの高頻度アクセスデータはDynamoDBで管理し、複雑な分析や業務管理にはRDBや検索基盤を利用する、といった構成も現実的です。
ただし、データストアを増やせば、データ同期、障害時の復旧、監視、整合性、運用コストなど、新しい問題も増えます。
そのため「マイクロサービスだから全部別DB」といった機械的な判断は避けるべきです。
複数のデータベースを使うことで得られるメリットが、増加する運用負荷を上回るかを考える必要があります。
結局、DynamoDBが向いているシステムとは、単にデータ量が多いシステムではありません。
アクセスパターンが明確で、キーを中心とした効率的なデータ取得を設計でき、大量アクセスに対して高いスケーラビリティを求めるシステムです。
一方、複雑なJOINや柔軟な検索、複雑な集約処理、広範囲なトランザクションがシステムの中心にあるなら、RDBのほうが自然に問題を表現できる可能性があります。
「RDBが嫌いだからDynamoDB」という入り口自体は、技術選定のきっかけとしては構いません。
しかし、最終的な判断基準にするべきではありません。
嫌いなものから逃げるのではなく、どんなデータアクセスを、どの規模で、どの整合性レベルで、どれだけの運用負荷で実現したいのかを整理する。
その結果としてDynamoDBを選ぶのであれば、設計にも確かな根拠が生まれます。
RDBからDynamoDBへ移行するときに避けたい典型的な失敗

RDBからDynamoDBへ移行するとき、最も危険なのは「DynamoDBの使い方を知らないこと」だけではありません。
むしろ厄介なのは、RDBでうまく動いていた設計や考え方を、そのままDynamoDBへ持ち込んでしまうことです。
RDBとDynamoDBは、どちらもデータを保存・取得するためのデータベースですが、得意とする問題の解き方が大きく異なります。
RDBでは、まずデータを正規化して保存し、必要になったデータをSQLで組み合わせるという設計が自然です。
一方DynamoDBでは、先にアクセスパターンを決め、そのアクセスを効率的に実現できるようにキーやデータ構造を設計します。
この違いを理解しないまま移行すると、「移行したのにクエリが複雑になった」「GSIだらけになった」「Scanが大量に発生して料金が高くなった」「整合性を保つためのコードが増えた」といった問題が起こります。
ここでは、RDBからDynamoDBへ移行するときに特に避けたい典型的な失敗を整理します。
既存のテーブル構造をそのまま移植する
最も典型的な失敗が、RDBのテーブルをほぼそのままDynamoDBのテーブルへ置き換えることです。
たとえば、users、orders、order_items、productsのようなRDBのテーブル構造があるからといって、それぞれをDynamoDBのテーブルにするだけでは、DynamoDBの特性を活かせません。
RDBでは、エンティティごとにテーブルを分けておき、必要なときにJOINすることができます。
しかしDynamoDBではJOINを前提にできないため、RDBと同じようにデータを分割すると、1回の画面表示のために複数回の読み取りが必要になることがあります。
もちろん、複数テーブルに分けること自体が間違いではありません。
問題なのは、「RDBではこうするから」という理由だけでデータ構造を決めることです。
DynamoDBでは、まず「このAPIは何を取得するのか」「どのキーで取得するのか」「1回のQueryで必要なデータを揃えられるか」を考える必要があります。
移行時には、テーブル定義ではなく、既存のAPIや画面、バッチ、SQLを分析するところから始めるべきです。
移行対象はテーブルではなく、既存システムが実現しているアクセスパターンだからです。
主キーを「一意なID」だけで決めてしまう
RDBでは、主キーとしてUUIDや連番IDを設定することが一般的です。
その感覚でDynamoDBでも「とりあえずIDをパーティションキーにする」と考えると、アクセスパターンを満たせないことがあります。
DynamoDBのパーティションキーは、単にアイテムを一意に識別するための値ではありません。
どのデータを同じグループとして取得するのか、そしてアクセスをどのように分散するのかを決める重要な設計要素です。
たとえば、注文IDだけをパーティションキーにした場合、注文詳細を1件取得することは簡単でも、「ユーザーごとの注文履歴を取得する」という要求には別のアクセス方法が必要になります。
その結果、後からGSIを追加したり、別のデータ構造を作ったりすることになります。
最初から「注文IDで取得する」だけでなく、「ユーザーIDで注文を一覧する」「ステータスと日時で未処理注文を取得する」といったアクセスパターンを洗い出しておくことが重要です。
キー設計をID設計の延長として考えないこと。
これだけでも移行後の手戻りを大きく減らせます。
JOINをアプリケーション側で大量に再現する
RDBから移行すると、「JOINできないならコードで複数回Getすればいい」と考えがちです。
確かに技術的には可能ですが、アクセス量が増えると問題になります。
たとえば注文一覧を取得した後、各注文についてユーザー情報と商品情報を取得する処理を作れば、1回の一覧取得が大量の読み取りへ変わる可能性があります。
開発環境の少量データでは気づきにくいものの、本番環境でデータ件数やアクセス数が増えると、レイテンシとコストの両方に影響します。
この問題への対策は、単純にキャッシュを追加することだけではありません。
注文一覧で必要なユーザー名などを注文データへ持たせる、関連アイテムを同じパーティションへ配置する、アクセスパターンに合わせてデータを非正規化するなど、読み取りそのものを再設計する必要があります。
「JOINがない」という制約を、欠けている機能としてだけ見るのではなく、「JOINが必要になるデータモデルを変えるきっかけ」と捉えることが重要です。
とりあえずGSIを追加して検索要件を解決する
移行後によくあるのが、検索できない要件が出るたびにGSIを追加するパターンです。
GSIは非常に便利なので、最初はこれで問題が解決したように見えます。
しかし、検索条件が増えるたびにインデックスを追加していくと、データモデルが徐々に複雑になります。
さらに、GSIは単なるRDBのインデックスと同じ感覚で扱えるものではありません。
どの属性をパーティションキーにするのか、ソートキーをどう組み合わせるのか、どの属性を投影するのか、アクセスが偏らないかなどを考える必要があります。
特に「ステータス」「種別」のように値の種類が少ない属性をそのままGSIのパーティションキーにすると、特定のキーへアクセスが集中する可能性があります。
検索条件を満たせるかだけでなく、データの分散とアクセス頻度まで考える必要があります。
GSIを増やす前に、「この検索は本当にDynamoDBから直接実行する必要があるのか」「検索用のデータを別途持つべきではないか」「そもそもこの要件はDynamoDBと相性がよいのか」を確認することが大切です。
Scanを検索処理の代わりに使う
「この条件でデータを探したいけれど、適切なキーがない」という状況で、最も手軽なのがScanです。
全件に近い範囲を読み取り、その後で条件に合うデータを絞り込めるため、実装自体は簡単です。
しかし、これはデータ量が増えるほど危険な設計になります。
数百件のデータでは問題なくても、数百万件になれば、必要な数件を見つけるために大量のデータを読み取ることになります。
レスポンス時間だけでなく、読み取りキャパシティや料金にも影響します。
開発初期にScanを使うこと自体が常に悪いわけではありません。
管理用途の低頻度処理や運用スクリプトなど、要件によっては許容できる場合もあります。
ただし、ユーザー向けAPIや高頻度の処理でScanを使うなら、かなり慎重に考えるべきです。
検索処理を作るときは、「検索条件をどうコードに書くか」より先に、「その条件でQueryできるキーを設計できるか」を考えます。
正規化を捨てること自体を目的にする
DynamoDBでは非正規化が重要だと説明されることが多いため、「DynamoDBではデータを重複させればいい」と誤解するケースがあります。
しかし、非正規化は目的ではありません。
あくまでアクセスパターンを効率化するための手段です。
意味のないデータコピーを大量に作れば、書き込み処理は複雑になります。
元データを更新したときに複製先も更新する必要があるなら、その同期処理が新しい障害ポイントになります。
一方で、「注文時点の商品名を注文データへコピーする」といった設計には明確な意味があります。
商品マスターが後から変更されても、過去の注文では購入時の商品名を保持したいからです。
この場合、重複は単なる冗長性ではなく、業務要件を実現するためのデータです。
したがって、非正規化された属性を見つけたら、「なぜ重複しているのか」を説明できる状態にしておくことが重要です。
理由を説明できないコピーは、将来的なデータ不整合の温床になります。
RDBと同じトランザクション境界を再現する
RDBから移行すると、既存のトランザクション処理をそのままDynamoDBへ持っていきたくなります。
しかし、RDBで一つのトランザクションに含まれていた処理が、DynamoDBでもすべて同時に更新されなければならないとは限りません。
注文確定、メール送信、分析データ作成、通知、検索用データ更新などをすべて一つの同期処理として扱っているなら、それぞれの処理に本当に同時性が必要なのかを確認します。
注文状態と在庫更新には強い整合性が必要でも、分析データの更新は数秒遅れても問題ないかもしれません。
DynamoDBにはトランザクションや条件付き書き込みがありますが、それらを使うべきなのは業務上の整合性を守る必要がある箇所です。
不要な処理までトランザクションへ詰め込むのではなく、非同期処理やイベント駆動へ分離することで、よりスケールしやすい構成にできる場合があります。
大切なのは「トランザクションを減らすこと」ではなく、本当に守るべき不変条件を明確にすることです。
小さなデータだけで性能を判断する
DynamoDBの設計で見落としやすいのが、開発環境と本番環境のデータ分布の違いです。
数千件程度のデータでQueryやScanを実行し、「十分速い」と判断してしまうのは危険です。
本番ではデータ量だけでなく、アクセスの偏りも変わります。
特定のユーザーだけが大量アクセスする、特定のテナントにデータが集中する、イベント開始直後だけリクエストが急増するといった状況です。
平均的なアクセス量だけを見ていると、この偏りを見逃します。
移行前には、本番で想定されるアクセスパターンとデータ分布をできるだけ再現して負荷試験を行うべきです。
特に、パーティションキーの分散、Queryあたりの取得量、GSIへのアクセス、アイテムサイズ、ピーク時のリクエスト数などを確認します。
性能問題が起きてから「DynamoDBは遅い」と判断するのではなく、設計段階でボトルネックを予測する。
分散システムでは、この姿勢が重要です。
移行した後にデータモデルを直せばいいと考える
最後に、プロジェクト計画上の失敗もあります。
それが、「まずRDBと同じ形で移行して、問題が出たら後からDynamoDB向けに直せばいい」という考え方です。
もちろん、実際に動くものを作ってアクセスパターンを検証することには価値があります。
しかし、本番データが大量に存在する状態でキー設計を変更すると、データ移行、GSIの追加、アプリケーション改修、バックフィル、切り替え、ロールバックなど、多くの作業が発生します。
そのため、移行前に少なくとも主要なアクセスパターンは洗い出しておきたいところです。
すべての将来要件を完璧に予測する必要はありませんが、現在の主要APIと画面について「どのキーで、どの程度のデータを、どの頻度で取得するのか」を整理しておくだけでも大きな違いがあります。
RDBからDynamoDBへの移行で失敗しないためには、RDBの構造を正しく変換することより、RDBで成立していた前提を疑うことが重要です。
テーブルをそのまま移植しない。
主キーを単なるIDとして決めない。
JOINを大量のGetで再現しない。
検索要件ごとにGSIを増やさない。
Scanを通常の検索として使わない。
非正規化そのものを目的にしない。
そして、RDBのトランザクション境界を無条件に再現しない。
これらに共通しているのは、DynamoDBをRDBの代替品として扱ってしまうことです。
DynamoDBにはDynamoDBの得意な問題領域があります。
アクセスパターンからデータモデルを設計し、必要な整合性を定義し、スケールとコストまで含めて構造を決める。
その前提に立てば、移行は単なるデータベース製品の置き換えではなく、アプリケーションのデータアクセスを再設計する機会になります。
「RDBをやめてDynamoDBにする」ことをゴールにしてはいけません。
DynamoDBを採用することで、何をシンプルにしたいのか。
どのアクセスを高速化したいのか。
どの運用負荷を減らしたいのか。
そこまで言語化できて初めて、移行する価値が見えてきます。
RDBからDynamoDBへ脱却するときに大切なのはデータベース選定より設計思想

ここまでDynamoDBの特徴や設計方法、RDBとの違い、移行時の注意点について見てきました。
ここで改めて強調したいのは、RDBからDynamoDBへ移行すること自体がゴールではないということです。
「RDBが嫌いだからDynamoDBを使いたい」という気持ちは、技術選定を考えるきっかけとしては十分に理解できます。
しかし、データベースを変更しただけで、システムの問題が自動的に解決するわけではありません。
むしろ重要なのは、データベースに合わせて設計思想を変えられるかです。
RDBでは、正規化したデータを保存し、必要なときにSQLでJOINや集約を行うという考え方が自然でした。
DynamoDBでは、必要なアクセスパターンを先に定義し、そのアクセスを効率的に実現できるようにデータを配置します。
データベースの違いは、APIやコードの書き方だけではなく、「データをどう捉えるか」という設計そのものに影響します。
「RDBをやめる」ことを目的にしない
データベース選定でありがちな失敗は、「現在のデータベースに不満があるから別のものへ移行する」というところで議論が止まってしまうことです。
RDBの運用が大変だからDynamoDBにする。
JOINが複雑だからNoSQLにする。
スケールさせたいからDynamoDBにする。
どれも動機としては理解できますが、それだけでは十分な判断材料になりません。
たとえばRDBの運用が大変なのであれば、本当に問題なのはデータベースそのものなのか、それともバックアップ、監視、スケーリング、接続管理などの運用方法なのかを切り分ける必要があります。
JOINが複雑なのであれば、SQLやインデックスの設計に問題がある可能性もあります。
スケールが必要なら、RDBでもリードレプリカやシャーディング、キャッシュなどによって対応できる場合があります。
つまり、「RDBが嫌い」という感情をそのまま技術選定へ変換するのではなく、何が嫌なのかを技術的な問題へ分解することが重要です。
そのうえで、DynamoDBがその問題を本当に解決できるのかを考えます。
DynamoDBへ移行すると別の制約が生まれるなら、その制約を受け入れる価値があるのかも評価しなければなりません。
データベースではなくアクセスパターンから考える
DynamoDBを使うときに最も大きく変わるのが、データモデルの出発点です。
RDBでは「ユーザー」「注文」「商品」といったエンティティを中心にデータ構造を考え、後からSQLで必要なデータを取得することが多くあります。
DynamoDBでは、まず「何をどう取得するのか」を考えます。
ユーザーIDから注文一覧を取得するのか、注文IDから詳細を取得するのか、ステータスと日時から未処理データを取得するのか。
こうしたアクセスパターンを整理してから、パーティションキー、ソートキー、GSI、非正規化の方法を決めます。
これは一見すると手間が増えたように感じます。
しかし、実際には「データベースに何でも検索させる」という発想から、「アプリケーションが必要とするアクセスを最初から効率化する」という発想への転換です。
アクセスパターンが明確なシステムであれば、この考え方は非常に強力です。
どのデータをどこに配置し、どのキーで取得するかが明確になるため、性能要件をデータモデルに反映しやすくなります。
正規化と非正規化に優劣はない
RDBの正規化に慣れていると、同じデータを複数箇所へ保存することに強い抵抗を感じるかもしれません。
データの重複は更新漏れや不整合につながるため、できるだけ避ける。
これはRDBでは非常に合理的な考え方です。
しかし、DynamoDBでは、データの重複が必ずしも悪い設計ではありません。
注文一覧で常に表示する情報を注文アイテム側へ持たせることで、複数の読み取りを減らせるなら、その重複には明確な意味があります。
もちろん、重複させたデータをどう更新するのかという問題は残ります。
そのため、非正規化する場合には、「なぜこの属性をコピーするのか」「元データが変更されたらどうするのか」「過去の値を保持する必要があるのか」を決めておく必要があります。
つまり、正規化が正しく、非正規化が雑という話ではありません。
どちらも目的に応じた設計手法です。
読み取り性能、書き込みコスト、整合性、データ更新頻度を考えた結果として、適切な構造を選ぶことが重要です。
「柔軟な検索」と「予測可能なアクセス」はトレードオフ
DynamoDBの設計思想を理解すると、RDBとの大きな違いがもう一つ見えてきます。
それは、問い合わせの自由度です。
RDBでは、保存されているデータに対してSQLを使い、後からさまざまな条件を組み合わせて検索できます。
これは非常に強力な抽象化です。
DynamoDBでは、あらかじめ想定したアクセスパターンを効率的に処理することに重点があります。
そのため、「将来どんな検索が必要になるかまったく分からない」というシステムでは、設計の難易度が上がります。
この違いは優劣ではありません。
検索要件が予測可能であればDynamoDBの制約はメリットになり得ます。
逆に、ユーザーが自由に条件を組み合わせる分析画面や、複雑なJOINと集約が頻繁に必要な業務システムでは、RDBの柔軟性が大きな価値になります。
技術選定では「どちらが高性能か」だけを見るのではなく、「システムの要求がどちらの前提に近いか」を見るべきです。
制約を理解すると設計の自由度が上がる
DynamoDBには、RDBと比較すると制約が多いように見えるかもしれません。
JOINがない、自由な検索が難しい、キー設計が重要、トランザクションにも適用範囲がある。
このような制約は、一見するとデメリットです。
しかし、ソフトウェア設計において制約は必ずしも敵ではありません。
むしろ、制約があるからこそ、システムの利用方法を明確にできます。
たとえば「このAPIはユーザーIDを起点にデータを取得する」と決めれば、その要求に最適化したパーティションキーを設計できます。
「検索条件はこの3種類に限定する」と決めれば、必要なGSIを設計できます。
要件が明確になることで、無制限な柔軟性のために複雑な仕組みを維持する必要がなくなる場合があります。
もちろん、要件を無理に制限するのは本末転倒です。
ユーザーが本当に必要としている検索機能まで捨てる必要はありません。
DynamoDBの制約に合わせて要件を変えるのではなく、要件と制約の両方を理解したうえで、適切な構成を選択します。
分散システムとして考える
DynamoDBを扱うなら、データベース単体だけでなく、分散システムとして考える視点も重要になります。
ネットワーク障害、リトライ、重複イベント、非同期処理、結果整合性など、RDBを中心にした単一データベースの設計では意識する機会が少なかった問題が前面に出てくることがあります。
たとえば外部サービスへのリクエストが失敗した場合、単純に処理全体をロールバックすればよいとは限りません。
イベントを再送するのか、状態を保留にするのか、後から補償処理を行うのか。
システム全体の状態遷移を設計する必要があります。
このとき重要になるのが冪等性です。
同じ処理が複数回実行されても、最終的な状態が壊れないように設計します。
DynamoDBの条件付き書き込みや一意性を利用した制御なども、こうした分散システムの設計に役立ちます。
つまり、DynamoDBへの移行はデータストアを変更するだけでなく、アプリケーションの状態管理や障害処理まで含めて再設計する機会でもあります。
一つのデータベースですべてを解決しようとしない
設計思想を変えるといっても、「これからはすべてDynamoDBで解決する」という意味ではありません。
むしろ、用途ごとに適切なデータストアを選択するほうが自然な場合があります。
ユーザー向けの高頻度なアクセスはDynamoDB、複雑な集計はRDBや分析基盤、全文検索は検索エンジン、キャッシュはインメモリストアというように、それぞれの得意領域へ責務を分ける考え方です。
ただし、データストアを増やせば、それだけ同期や障害対応、監視、バックアップなどの運用コストも増えます。
そのため、複数のデータベースを使うこと自体を目的にしてはいけません。
複雑さを増やしてでも得たいメリットがある場合にだけ採用します。
「DynamoDBだけで何とかする」という発想も、「全部RDBに入れる」という発想も、どちらも極端です。
システムの要件に合わせて、適切な抽象化を選ぶことが重要です。
最終的には「なぜDynamoDBなのか」を説明できる状態にする
DynamoDBを採用するなら、チーム内で「なぜDynamoDBなのか」を説明できるようにしておきたいところです。
「AWSだから」「スケールするから」「サーバーレスだから」だけでは不十分です。
たとえば、「主要なアクセスパターンが明確で、ユーザー単位の大量アクセスを低レイテンシで処理する必要がある」「トラフィック変動が大きく、データベースインフラの運用負荷を抑えたい」「複雑なJOINよりも決まったパターンでの高速な読み書きが重要」といった形で説明できれば、技術選定の根拠が明確になります。
反対に、「このシステムは複雑なJOINが中心で、検索条件も頻繁に変わる。
だからRDBを使う」という説明も同じように正しい判断です。
重要なのはDynamoDBを選ぶことではなく、要件と技術の関係を論理的に説明できることです。
RDBからDynamoDBへ脱却するとき、本当に捨てるべきなのはRDBそのものではありません。
「データを正規化して保存し、後から自由に検索すればよい」という一つの設計パラダイムだけに依存することです。
DynamoDBでは、アクセスパターン、キー、データ配置、整合性、スケール、コストを一体として考えます。
もちろん、DynamoDBを採用したからといって、すべての問題がシンプルになるわけではありません。
むしろ、最初に考えることは増えるかもしれません。
しかし、その代わりに、システムがどのようにデータへアクセスし、どこまでの整合性を必要とし、どのようにスケールするのかを明示的に設計できます。
結局のところ、RDBからDynamoDBへの移行で成功するかどうかを決めるのは、製品名ではありません。
データベースを選んだ後に、そのデータベースの前提に合わせて設計を組み立てられるかどうかです。
「RDBが嫌いだからDynamoDBを使う」というところから始めても構いません。
ただし、最後には「このシステムには、このアクセスパターンと運用要件がある。
だからDynamoDBという設計思想が適している」と言えるところまで議論を進めたいものです。
それができれば、データベース選定は単なる好みではなく、システム設計上の合理的な意思決定になります。


コメント