キャッシュを使用してDynamoDBを高速化する(または置き換える)

DynamoDBは、大容量のアプリケーションのための強固なデータベースですが、キャッシュによってさらに優れたものになります。

私がDynamoDBの大ファンであることは周知の事実です。DynamoDBを使い始めたのは、サーバーレスアプリケーションとの相性の良さに惹かれたからだが、DynamoDBを愛するようになったのは、その一貫した予測可能なパフォーマンスのためです。DynamoDBのおかげで、パフォーマンスが大きく変動するサービスには警戒心を抱くようになりました。

私はデータ永続化のニーズがあれば、まずDynamoDBを使うようにしているが、DynamoDBがすべての状況に適しているわけではありません。DynamoDBが適している場合でも、アプリケーションのニーズを満たすためにDynamoDBと他のサービスを組み合わせる必要があるかもしれません。

このブログでは、DynamoDBをキャッシュで補強、あるいは置き換える場合について見ていきます。主に3つのパターンに焦点を当てます:
・キャッシュでDynamoDBのパフォーマンスを高速化;
・キャッシュでDynamoDBのスケーラビリティを高める;
・キャッシュによるDynamoDBコストの削減;

それぞれのパターンについて、DynamoDBの利用を補強または置き換えるためにキャッシュを検討する理由と、検討すべき主な要因を見ていきます。

キャッシュでDynamoDBのパフォーマンスを加速する

DynamoDBでキャッシュを使用する最初の、そして最も明白な理由は、パフォーマンスを向上させることです。圧倒的なスピードがキャッシュの存在意義なので、これは当然のことです。

私はよくDynamoDBのパフォーマンスの速さについて話すので、これは驚かれるかもしれません。しかし、私がここで言いたいのは、純粋なスピードよりももっと微妙なことです。

DynamoDBのパフォーマンスについて、私はよく以下のようなチャートを用いて説明します:

MySQL(または選択したリレーショナル・データベース)は、データベースのデータ量が増えるにつれて遅くなることが多いことに注目してください。それに対して、DynamoDBはテーブルのサイズが大きくなっても同じパフォーマンスです。

このグラフで強調したいのは、DynamoDBのパフォーマンスは、データベース・サイズや同時クエリ数などの要因に関係なく一貫しているということです。多くのリレーショナルデータベースのようなシングルノードシステムでは、より多くのデータとより多くのクエリは、より多くのリソースの競合につながり、クエリの待ち時間を増加させます。

DynamoDBのこの一貫性は、データベースの使用量が増えても、コストのかかるリファクタリングや最適化に時間を費やす必要がないので便利です。アプリケーションの起動初日でも、ユーザ数が大幅に増えた数年後でも、同じ1桁ミリ秒の応答時間を期待できます。

しかし、特定のユースケースでは、1桁ミリ秒の読み取り応答時間では十分な速さとは言えません。eコマースのユーザーはより速いページロードを求め、ゲームのユーザーはより速いゲームプレイを求めています。マイクロサービス・アーキテクチャで作業している場合、あなたのサービスは1つのページロードを満たすために呼び出される多くのサービスの1つかもしれません。

これは、DynamoDBを補強するためにキャッシュを使用する例です。DynamoDBの耐久性と可用性の保証は、コアアプリケーションの要件にまだ依存していますが、多くの場合、パフォーマンスを向上させるためにキャッシュも使用しています。キャッシュは、ディスクストレージの使用を避けて、より高速だが耐久性の低いRAMを使用することで、従来のデータベースの要件の一部を緩和します。その結果、最も頻繁に使用されるデータのレスポンスタイムが速くなります。

キャッシュでDynamoDBのスケーラビリティを高める

DynamoDBでキャッシュを使用する2つ目の理由は、スケーラビリティを高めることです。

*レコード・スクラッチ

ちょっと待てよ、DynamoDBの売りはスケーラビリティじゃないのか?なぜスケーラビリティを高めるためにキャッシュが必要なんだ?” と思うかもしれません。

DynamoDBは信じられないほどのスケールを扱うことができます。これについては、Jeff Barr氏のAmazon Prime Day 2022の総括を参照してください。Jeffは、プライムデーにおけるAmazonリテールのDynamoDBの使用量が数兆リクエストに達し、ピーク時には1秒あたり1億500万リクエストを超えたと述べています。

つまり、DynamoDBはスケールに対応できます。しかし、キャッシュはDynamoDBのスケーラビリティを向上させるのに有用です。これを理解するためには、DynamoDBの仕組みを少し理解する必要があります。

DynamoDBは、水平方向にスケーリングすることで、どのような規模でも一貫したパフォーマンスを提供します。DynamoDBは、テーブルのすべてのデータを1台のマシンに保持するのではなく、データを「パーティション」に分割し、多数のマシンに分割します。

これらのパーティションは、意図的に小さく保たれています。これらのパーティションは、特定のリージョン内のDynamoDBサービス全体で共有されるストレージノード上の他の多くのテーブルのパーティションと一緒に格納されます。

パーティションは他のパーティションの近くに保存されるため、DynamoDBサービスでは、個々のパーティションで実行できる1秒あたりの読み取りと書き込みのスループットを制限するパーティション・スループット制限を実施しています。現在のところ、個々のパーティションで1秒あたり1000ライトユニット、3000リードユニットを超えることはできません。

多くのユースケースでは、パーティションのスループット制限は問題ではありません。eコマース・アプリケーションで個々の顧客が毎秒1000回の書き込みを超えることはまずないでしょうし、ビデオゲームのキャラクターが毎秒3000回の読み込みを必要とすることもないでしょう。このような例では、スケーラビリティのためにキャッシュは必要ないかもしれません(それでもシステムレイテンシの低減には役立ちますが)。

しかし、パーティション制限が問題となるアプリケーションもあります。TwitterやRedditのようなソーシャルメディア・アプリケーションでは、人気のあるツイートやスレッドが短時間に何百万ものインプレッションを受けることがあります。あるいは、ソーシャル・ショッピング・サイトを考えてみましょう。そこでは、お買い得なキャンペーンが開催され、買い物客がそれを利用しようと急ぐため、大量のトラフィックが発生します。

これらの例はどちらも、最も人気のあるアイテムが平均的なアイテムよりも何桁も多くアクセスされるZipfian分布の例かもしれません。DynamoDBはデータをより均等に分布させたいので、アプリケーションは人気のあるアイテムにアクセスしようとしてスロットルされるかもしれません。

DynamoDBテーブルの前にキャッシュを追加することができます。Momentoのようなセントラルキャッシュは、大量の同時リクエストを処理するように設計されています。読み取りトラフィックをキャッシュに向けることで、DynamoDBのホットパーティションの負荷を軽減できます。

前の例と同様に、これもDynamoDBテーブルをキャッシュで補強する方法です。どちらの例も、レコードはDynamoDBに永続的に書き込まれるが、多くの読み取りは中央のキャッシュから提供されるread-asideキャッシュパターンでうまく動作します。これにより、DynamoDBのスロットリングの問題を軽減しながら、アプリケーションのパフォーマンスを向上させることができます。

書き込みの多いアプリケーションのコストを削減

上記2つのパターンがDynamoDBとキャッシュを併用するものであるのに対し、最後のパターンはDynamoDBをキャッシュで置き換えるものです。DynamoDBをキャッシュに置き換える正当な理由の中心はコストなので、まずはDynamoDBの課金の仕組みをおさらいしておきましょう。

DynamoDBは、CPU、RAM、ディスクIOPSのようなインスタンスベースのリソースに対して課金するのではなく、読み取りユニットと書き込みユニットによって直接読み取りと書き込みに基づいて課金します。リードユニットでは4KBの一貫性の高いデータを読み込むことができ、ライトユニットでは1KBのデータを書き込むことができます。DynamoDBは切り上げを行うので、100バイトの読み込みは完全な読み込み単位に、100バイトの書き込みは完全な書き込み単位に切り上げられることに注意してください。

最後に、DynamoDBには2つの異なる課金モードがあります。Provisioned Capacityモードでは、アプリケーションで必要な読み書きの単位を秒単位で時間単位で支払います。もう1つは、On-Demandモードで、前もって容量を指定することなく、使用するごとに読み取り単位と書き込み単位を課金します。

オンデマンド・モードは、完全に利用される同等のプロビジョンド・キャパシティ・モードよりも約7倍高いが、完全に利用されることはほとんどありません。ワークロードの予測可能性に応じて、Provisioned Capacityの利用率は低いほうでも20%、高いほうでも70%になります。

このような背景を念頭に置いて、いくつか計算してみましょう。以下の例では、1GBのデータをDynamoDBに書き込むのにいくらかかるかを見ていく。まず、サーバーレスのOn-Demand課金モードを使用した場合のコストを示します。次に、Provisioned Capacityを100%の利用率で使用した場合と50%の利用率で使用した場合のコストを示します。これら3つの見積もりは、コストの上限と下限、そして可能性の高いシナリオを設定するのに役立ちます。

上のグラフでは、オブジェクトがちょうど1KBで(したがって1つのWCUを完全に消費する)、プロビジョニングされた容量をフルに利用する理想的なシナリオであっても、DynamoDBに書き込まれたデータ1GBあたり約18セントを支払っていることがわかります。利用率が50%で、半分のキロバイトのオブジェクトを書き込むようなシナリオでは、コストは書き込むデータ1GBあたり0.72ドルになります。On-Demand課金を使用している場合や、より小さなアイテムを書き込んでいる場合など、多くのユースケースではGBあたりのコストはさらに高くなります。

では、比較のためにMomentoを加えて、私が話しているコスト削減の形を見てみよう。Momentoの価格モデルはシンプルで、データの書き込みまたは読み込み1GBあたり0.50ドルです。これは、ほとんどの一般的なシナリオにおいて、顕著なコスト差を意味します。

もちろん、これはDynamoDBデータベースをやめて、すべてをキャッシュに移すべきだという意味ではありません。DynamoDBはプライマリデータベースとして、キャッシュでは利用できない耐久性、可用性、インデックスに関する多くのプロパティを提供します。

しかし、特定のユースケースでは、DynamoDBからMomentoにワークロードを移行してコストを削減することは理にかなっています。大量の書き込みを特徴とするワークロードがあるが、データは本質的に刹那的である(または、プライマリ、ディスクベースのデータベースの強力な耐久性保証を必要としない)場合、キャッシュを使用することが適している可能性があります。

ここで最も一般的な2つの例は、セッション管理とレート制限であす。どちらの状況でも、高い頻度でアクセスされる小さなデータのビットがあります。さらに、どちらのユースケースも、データが刹那的であったり、簡単に再生できる状況であるため、キャッシュの特性によく合致しています。プライマリー・データベースからキャッシュに移行することで、レイテンシーとコストを削減することができます。

結論

今回のブログでは、キャッシュをAmazon DynamoDBと組み合わせて、あるいはAmazon DynamoDBの代わりに使用する方法について考えてみました。これは、ワークフローを処理するために多くのデータアクセスリクエストが必要な場合に特に重要です。次に、キャッシュがホットパーティションへのプレッシャーを軽減することで、DynamoDBのスケーラビリティを向上させる方法を学びました。最後に、キャッシュがどのようにDynamoDBを置き換え、特定のワークロードのアプリケーションコストを削減できるかを探りました。

これらの状況では、必ずアプリケーションの要件を理解してください。ワークロードを考慮し、ニーズに合っていればキャッシュをレイヤー化しましょう。

Momento Cacheでは、セルフサービスCLIを使用して、無料で簡単に実験と評価を行うことができます。そして、PoCのために低リスクのワークロードを特定する準備ができたら、Momentoの開発者にDiscordで直接連絡を取ることができます。