DynamoDBのシングルテーブル設計:現実解

開発者に委託する前に、単一テーブル設計のデメリットを考慮する。

DynamoDB Data Modeling Series

1.DynamoDBのデータモデリングで本当に重要なことは?
2.DynamoDBのセカンダリインデックスはどれを選ぶべきか?
3.最適化されたDynamoDBセカンダリインデックスでコスト削減とスケーラビリティを最大化する
4.DynamoDBのシングルテーブル設計:現実 (YOU ARE HERE)

“シングル・テーブル・デザイン “について耳にしたことがあるでしょう。これは論争の的となっているトピックで、(いつものように)話には2つの側面があります。DynamoDBのデータモデリングに関する連載の4回目(最終回)となる今回は、”シングルテーブル設計 “の歴史についてお話します。それはどこから来たのか?どのように間違ったのでしょうか?そして、どのデータが同じテーブルに属するのか、別々のテーブルに属するのか、どのように賢い選択ができるのでしょうか?

もし、このシリーズをご覧になっているのであれば、アイテム・コレクション、セカンダリー・インデックス・プロジェクション、メータリングのような核となるコンセプトについて記憶を呼び覚ますために、以前の記事にざっと目を通すことをお勧めします。以下の議論を完全に理解するためには、その文脈が重要になります。

せっかちな人のためにTLDRを説明すると、「シングルテーブル設計」のテクニックや、まさに1つのテーブルという理想を推し進めるために使用されるサンプルモデルは、作為的なものであり、クリティカルな負荷をスケールで運用するDynamoDBの顧客の現実を代表するものではありません。詳細は、サービスの専門家の精査に耐えられないことが多く、テーブル数を減らすことに重点を置きすぎると、コストがかかり、スケールに制約があり、パフォーマンスの低い設計になる可能性が高いです。エンジニアとして、私たちは季節的な流行には懐疑的な見方をすることを学んできました。DynamoDBに飛びつく前に、多くのデメリットを考慮する必要があります。

特定のDynamoDBデータモデルに最適なテーブル数は1つかもしれませんが、多くの場合はそれ以上になります。

ああ、全容を知りたいのですか?いいですね。最初から始めましょう。

この “シングル・テーブル・デザイン “には何か意味があるのでしょうか?

最初に “シングルテーブル設計 “というフレーズが生まれました。Amazon.comのチームがデータベースの近代化目標の一環としてDynamoDBを理解しようとしていたとき、彼らはリレーショナルデータベースの経験から第3正規形のモデリングアプローチを持ち込む傾向がありました。無意識のうちに、彼らはDynamoDBの2つの非常に強力な概念であるアイテムコレクションとスキーマの柔軟性を利用する代わりに、アプリケーションコードにJOINを組み込んでいました。彼らは、DynamoDBの中核的な利点を見逃しており、データベースエンジンの一部を理由なくアプリケーションコードに組み込もうとしていました!

DynamoDBから最良の結果を得るには、非正規化の機会を特定する必要があります。非正規化とは、追加作業なしで簡単に検索できるように、完全に構築された結果を格納することです。これは、アイテムまたはアイテムコレクションを使用して行うことができます – 明白な指摘ではありませんが、同じアイテムまたは同じアイテムコレクション内のデータは、(必然的に)同じテーブルにもあります。DynamoDBの最大の強みは、コンピュートではなくストレージによるスケーリングです!DynamoDBの最大の強みは、計算ではなくストレージを経由してスケーリングすることです。高度な要件を満たすためには、アプリケーションコードで後処理が必要になるかもしれません。

非正規化はDynamoDB特有の手法ではありませんが、DynamoDBでは特に重要です。なぜか?なぜなら、最小限のリクエストでアクセスできる場所に情報をまとめて保存しておかないと、障害率が高くなったり、テールレイテンシが増加したりする可能性があるからです。DynamoDBは(極端な話)分散システムです。特定のデータ操作のデータを複数の異なるキーにまたがって保存する場合、別々のクライアントリクエストでそれらを取得する必要があります。異なるパーティション(異なるノード、異なるラック、場合によっては異なるAZ)に存在し、異なるネットワークリンク、ルーター、スイッチなどを経由する可能性があります。

同じテーブルにデータを保存しても、この問題には対処できない。スキャン・コールをするときの極端に小さなテーブルを除いては。

同じアイテムや同じアイテムコレクション(つまり、同じパーティションキー値)に関連情報を格納することです。

DynamoDBのデータモデリングで非正規化すると、標準的な第3正規形パターンよりもテーブルの総数が少なくなるというボーナス効果がありました。シングルテーブル設計が最初に売り出された頃は、自動スケーリングも、オンデマンドも、適応キャパシティもありませんでした。そのため、テーブル数が少ないことには魅力がありました。しかし、可用性を最優先するのであれば、「テーブル数が少ない方が常に良い」という主張はインチキだと私は主張しています(そして、最初からそうでした)。DynamoDBは長い道のりを歩んできており、データの特定のサブセットの要件に応じて、キャパシティモード、オートスケーリングポリシー、バックアップ、ストレージクラスなどを調整するオプションが欲しいのは間違いありません。すべてのデータ(まったく関係のないものであっても)を1つのテーブルに強制的に格納すると、すべてのデータに対して1つの設定しかできなくなります。

何が悪かったのか?

「シングル・テーブル・デザイン」という言葉は、もともとAmazon.comのチームがDynamoDBに移行する際に、データモデリングの可能性を見直すことを促すためのものでした。振り返ってみると、あまりにも誤解されやすい言葉の選択でした。

その目的は、Amazon.comのチームに、スキーマの柔軟性とアイテムのコレクションを利用して、関連するデータを非正規化し、すでにJOIN化された形で保存し、効率化を図るよう説得することでした。社内では、”シングルテーブル “というメッセージは混乱を招く可能性があるという懸念があったにもかかわらず、この用語は万能薬として対外的に紹介されました。AWSのマーケティング支援とソーシャルメディアの力で、この用語は一人歩きしました。一部の実務家は物事を文字通りにとらえすぎ、まさに1つのテーブル(そして “オーバーロード “されたグローバルセカンダリインデックス(GSI)さえも-BAD IDEA-)を伝道する設計のゴールとしました。宣伝のために、不必要な複雑さはウィザードリーとして扱われ、Twitter上ではクールキッズクラブへの入場料として、誰もがそれを完全に理解したと言いたがった!混乱の中で、スケーラビリティ、操作性、複雑さといったものはすぐに振り払われ、ベストプラクティスやパフォーマンス向上という(誤った)主張でごまかされた。すべてのデータモデルについて、すべてのデータを1つのテーブルだけに押し込むテクニックをやみくもに適用することは、非常にスループットの低いユースケースであれば何とかなるかもしれないが、本番でスケールアウトした場合、すぐに深刻な運用上の後悔を生むことになります。

私は、DynamoDBの何百(何千?)もの大規模な顧客と一緒に、この強力なデータベースを使用して最大限の成功を収めるために、彼らのデータモデルを見直してきました – これには、DynamoDBを中心に効率性とスケールのための重要な依存関係を構築している多くのAWSサービスチームも含まれます。私は、DynamoDBが信じられないほどうまく機能するのを見てきましたが、DynamoDBが誤用されたときに、様々な形で炎上することも見てきました。

私の経験から、テーブル数はDynamoDBのデータモデリング手法の成果として扱うことを強くお勧めします。

可能で有利な場合は非正規化し、それ以上は運営上の要件や標準について考えましょう。あるいは、それを実現するために、多くの複雑でコストのかかる妥協案を逆算することになるでしょう。そうしないと、実現するために多くの複雑で高価な妥協をしながら後戻りすることになります。

ある賢人(Krogさん、こんにちは!)は、かつて「できるからといって、そうすべきとは限らない」と言いました。2つのアイテムを同じDynamoDBテーブルに配置することは常に可能で、必要なのは主キーのデータ型が同じで、キー属性が一意であることだけです。これらの要件を満たすのは簡単です(有料)。しかし、それを選ぶべきでしょうか?それは、データの管理と利用がより簡単で効率的になる場合だけです。シングルテーブル設計 “の話が勢いを増すにつれて議論されたテクニックの多くは、まったく関係のないデータ(アイテムコレクションに一緒に格納されたり、Queryを使用して一緒に検索されたりすることは決してない)を同じテーブルに詰め込む方法を見つけることを目的としていました。

同じテーブルにデータを保存する理由…

1.1つの “レコード “である – つまり、1つのアイテムに非正規化されるか、同じアイテム・コレクションに(テーブル自体またはセカンダリ・インデックスで)正規化される – これは、効率的に更新や検索ができるようにするためです – データは間違いなく関連しており、同じテーブルに保管せずにはいられません!

2.複数のレコードだが、同じタイプで、同じサービスによって権威的に “所有 “されている。これはS3バケットに似ています。もしあなたが “invoice “サービスを実行していたら、おそらく請求書用に別のS3バケットを作成するだろう?なぜか?分離することでリスクを減らし、他のデータに同じ設定を強いることなく、全てのデータを同じように管理できる柔軟性が欲しいからだ。

「まさに1つのテーブル」に支払う代償は……。

1.例えば、すべてのパーティション・キー値にテキストの接頭辞を追加するなどです。これはアイテムのサイズを大きくします(アイテムのサイズは、コスト効率とスムーズなスケーラビリティにおいて最も重要な要素であることを思い出してください)。また、開発者の複雑さを増し、可読性を低下させます。そして、同じテーブルにデータを格納する上記の理由のどちらも当てはまらない場合、見返りは何も得られません。

2.すべてのデータが同じStreams設定、同じバックアップ設定、同じキャパシティモードとオートスケーリングポリシー、同じTTL属性名、同じテーブルクラス、同じグローバルテーブルレプリケーション設定、同じテーブルレベルメトリクスを持つ必要があります(データタイプごとの重要な観測可能性を失う可能性があります)。データ・タイプが異なると、負荷変動のタイミングやスパイクの多寡、読み取り/書き込み/保存のバランスの違いなど、運用パターンが異なることがよくあります。柔軟性を大きく制限することになります。

3.データの種類でScanを制限することも、S3へのエクスポートを制限することもできません。これらはどちらもETLのための非常に一般的で便利なパスであり、また時折の一括更新のためのパスでもあります。例として、10kのユーザーアカウント(1KBのアイテム)と2Mの5KBの画像アイテムを持つアバター管理サイトがあるとします。あなたはこれらのレコードをすべて同じテーブルにまとめました(ソーシャルメディアにそうするように言われた)。今、あなたはアカウントレコード内のすべてのアカウント作成タイムスタンプを、文字列(ISO 8601)ではなく数値属性(エポック秒)として保存するように修正したいとします。これらのアカウントレコードを見つけるには、テーブル全体をスキャンするかエクスポートしなければなりません。

4.爆発半径の拡大。データストアをサービスごとに分離する(API経由でのみ共有アクセスする)というガイダンスに従っているとしても、サービス内の無関係な種類のデータを同じテーブルに混在させるのは意味がありません。上記のアバター管理シナリオでは、一括更新でミスを犯し、パーティションがスロットルするほど1つのアカウントアイテムに負荷がかかったとします。DynamoDBは、ホットキーを分離するために物事を分割しようとしますが(これには時間がかかります)、その間に、同じパーティションにアカウント・アイテムがあるユーザーの一部に影響が及びます。しかし、アバターへのアクセスが影響を受けている別のユーザーの経験にも影響を与えることになります。これらの無関係なデータ型への依存が独立していればいいのですが。アカウント管理ワークフローで大きなミスを犯し、ポイント・イン・タイム・リカバリを使ってアカウント・レコードを復元したい場合はどうでしょう?この場合、復元するデータ量が膨大になり、復元にかかる時間も長くなります。関連性のないデータが別々の機能をサポートしている場合、”正確に1つのテーブル “はあなたの味方ではありません。

5.複雑なアクセス・コントロール。関連性のないデータについては、テーブル・レベルでのポリシー管理が容易です。

でも、あるビデオで見たんです

1.「複数のアイテムが同じテーブル内にある場合は、複数のアイテムを取得する方がパフォーマンスや効率が良い」。 これは、Queryを使用して取得できるように、同じアイテムコレクションにアイテムがある場合にのみ当てはまります。そうでなければ、DynamoDBは全く気にしません。10個のテーブルから10個のアイテムをBatchGetItemした場合、読み込み単位の消費とレイテンシは、それらがすべて同じテーブルに格納されている場合と同じになります。

2.「DynamoDBの内部的なものがあるので、すべてのデータを1つのDynamoDBテーブルに強制的に格納すれば、魔法のようなことが起こる」。いや。現実にはそうはいきません。

3.”複数のデータタイプがテーブル内で同じオートスケール容量を共有することで、使用率の低いプロビジョニング容量を共有することができ、コストを削減できる”。稀にDynamoDBの請求にわずかなマージンを節約できるケースもあるかもしれないが、私はそのようなケースを見たことがありません。きっちり1テーブル “戦略は、通常、より大きなアイテム、セカンダリインデックスへのオーバープロジェクション、無駄な読み取り/書き込みユニットにお金を払うことを伴うことを考えると、これはかなり疑わしいと感じます – そして、あなたはサイコロを振るために将来の運用の成功を危険にさらすことをいとわないのでしょうか?それは私の推奨するところではありません。

ストレートスクープ

正直に言うと、私はAmazonでの職歴からDynamoDBに強い偏見を持っています。最近はMomentoで別のサーバーレス・インフラストラクチャ・サービスに取り組んでいるが、それでもDynamoDBには深い愛情を持っています。DynamoDBは、適切な目的のために効果的に適用されれば素晴らしいデータベースであり、最近のデータベース領域で多くの選択肢があることは非常に幸運なことです!DynamoDBを構築し運用するエンジニアリングの専門家は驚異的な人々で、顧客のために製品を改良し続けて10年以上の経験を持っています。彼らは、DynamoDBを中心に構築する際の成功を望んでいますし、私もそう思っています。

残念なことに、”単一テーブル設計 “の誤った解釈は、DynamoDBとその顧客の一部を辛い方向へと導いてしまいました。私は、DynamoDBをあきらめようとしていた数え切れないほどの人々と話をしました。なぜなら、彼らが考え出した(非常に合理的な)設計には複数のテーブルがあり、それらをマージしても意味がなかったからです。私は彼らの多くが混乱を乗り越えるのを助けることができましたが、私はしばしば、ソーシャルメディアにおける誤った「単一のテーブル設計」の圧力のために、他のどれだけの人がDynamoDBに裏打ちされたソリューションへの興味を放棄したのだろうかと思います。最近まで、DynamoDBのドキュメントは “シングルテーブル設計 “について言及していませんでした。悲しいことに、それは最近更新され、”single table design”(大きく誤解されたフレーズ)と “multi table design”(本当に奇妙で見当違いのコンセプト)の間の(明らかに二元的な)選択に関する情報を含むようになりました!

データベースにはフリーランチはなく、DynamoDBは、一部の人が信じているような違いや複雑さはありません。DynamoDBのデータモデルを構築するときは、アクセスパターンから始め、DynamoDBのテーブルがスキーマの柔軟性を可能にすることを認識し、アイテムを小さく保ち、必要に応じてアイテムのコレクションを構築して、読み取りと書き込みの単位消費を最適化します。同じ手法をセカンダリインデックスにも適用します。運用上の必要性を考慮する。最終的にはN個のテーブルを持つことになります。それでも構わないのです。

シングル・テーブル・デザインについて私と議論したい、あるいは今後の記事で書くべきトピックを提案してほしいという方は、ツイッター(@pj_naylor)かメールでご連絡ください。