
カットの下に、数字と写真の要約、キャッシングに関する簡単な推奨事項、および単純なキャッシャーとテストアプリケーションのソースコードがあります。
テスト方法。
テストのために、データベースにさまざまなランダムデータを入力し、さまざまな方法でそれらを読み取ることができる別のアプリケーションが起動されました(ソースには写真のテストもありますが、この記事の範囲外にすることにしました)。 リクエストは数秒間隔でほぼ同時に再生され、結果はAppStatsを使用して記録され、その後平均結果が選択され、スクリーンショットが作成されました。
データ。
ランダムジェネレーターの結果が使用されます。各サブセクションでは、最初に約60Kbのテキストを持つ1つのレコードの結果があり、次に1000個の小さなレコードのリスト(平均コメントにほぼ対応)があります。
データストア。
もちろん、最も信頼性が高く、最も遅いストレージから始めましょう。 get_by_key_name()メソッドを使用して、データストアから60Kbの1つのレコードを取得します。

見た目は良さそうです-ランタイムと消費リソースの両方の面で、今度は1000の「コメント」のサンプルを取得してみましょう。

これが、スケーラブルなApp Engine上のスケーラブルでないアプリケーションの主な理由です。データを取得するのに440ミリ秒かかり、わずかな量のコンピューティングリソースが消費されます。 このレートでは、アプリケーションはわずか数千のリクエストで無料の割り当てを消費できます。データをキャッシュする必要があります。
注:GqlQueryを実行すると、1000レコードのグループでall()。Fetch(1000)メソッドによって10秒のapi_cpuが取得され、より多くのリソースが消費されてより多くのレコードから選択されます。 私が見た最大のものは、1000レコードがHRDS(高レプリケーションデータストア)に追加されたときの561,000 api_cpu_msでした。 そのため、App Engineで多数の選択を行う場合は、非常に慎重に作業する必要があります。
Memcache
最も普遍的なツールはmemcacheであり、データストアへのクエリの結果を保存することをお勧めします。

録音はほぼ2倍高速で、必要なプロセッサー時間は2倍短くなります。 確かに、絶対的な意味で、10msの節約は深刻ではないようです。 次に、memcacheがリストをダイジェストする方法を見てみましょう。

ここで、節約ははるかに大きくなります。1.5倍速く、15倍安くなります。
Memcache + protobuf。
Memcacheは無料で、ほとんど制限はありませんが、それでもゴムではありません-プロトコルバッファ内のdb.Modelクラスのオブジェクトをシリアル化することで、データのサイズを小さくしてみてください( この記事で推奨)。 これにより、データをより長い間memcacheに保存できますが、アプリケーションの動作は改善されますか?..


最初のケースで差がほとんど見えない場合、2番目のケースでは実行時間と消費リソースの増加が20〜25%はっきりと見えます。
Memcache + protobuf + zlib。
サイズを縮小しているため、アーカイブを試みる必要があります。そのため、zlib.compressを使用してシリアル化された行を圧縮します。

Memcache.Getランタイムは、圧縮されたテキストがはるかに高速で送信されるため、1ミリ秒減少しました。 ただし、データを解凍すると、蓄積された利点はすべて失われます。

多数の小さなレコードでは、そのような利点はなく、時間とリソースが約5%増加するだけです。
ローカルメモリ。
そこで、生産性を向上させる極端な手段であるインスタンスメモリに到達しました。 多くの欠点に加えて(各インスタンスには独自のメモリがあり、このメモリは50 MBしかありません。インスタンスが最大9000リクエストに達することは非常にまれです)、主な利点があります-速度。

いつものように、1つのエントリについて、節約額は重要ではありません。リストを使用すると、まったく別の問題になります。

ランタイムが100倍に短縮され、コンピューティングリソースがまったく必要なくなりました。これは、インスタンスメモリを使用してすべてを連続してキャッシュするための適切な引数です。
しかし、欠点を忘れないでください:
- 各インスタンスには独自のメモリがあり、それに応じてキャッシュがあります。
- メモリの50メガバイトの合計。
最初の問題を解決するために、キャッシュ同期のトリッキーなメカニズムを考え出すことができます。 たとえば、キャッシュハッシュをmemcacheに保存し、一致しない場合はキャッシュを更新します。 しかし、紛らわしいように思えます。最後の手段として、使い慣れたprotobufとzlibの助けを借りて、消費されるメモリ量をより少なくしようとしましょう。
ローカルメモリ+ protobuf。


1つのレコードについて、時間の変動はほとんどエラー内にありますが、深刻な症状がリストに表示されます。インスタンスメモリとmemcacheの時間とリソースは実質的に一致しています。 このような単純な方法で、圧縮はパフォーマンスの向上を損なう可能性があります。
ローカルメモリ+ protobuf + zlib。
そして、注文のために、zlibを使用してインスタンスメモリからデータを取得します。


奇跡は起こりませんでした-リストの取得にはまだ時間がかかり、多くのリソースを消費します。
結論
- 多くの場合、小さな単一オブジェクトはキャッシュする意味がありません。10msのコードを複雑にするよりも、データベースからキーまたは名前でオブジェクトを取得する方が適切です。 フレームワークまたは最適でないアルゴリズムは、より多くを消費する可能性があります。
- シリアル化とデータ圧縮は、memcacheにのみ使用する必要があります。インスタンスメモリのデータは、すぐに使用できる形式で保存する方が適切です。
- インスタンスメモリは、頻繁に要求され、めったに変更されないデータを格納するのに最適な場所です。 アプリケーションが50 MB以内のデータ量の小さなサイトである場合、すべての「重い」リクエストにインスタンスメモリを安全に使用できます。
アプリケーションコード。
テストアプリケーションコードはこちらからダウンロードできます。 プロジェクトでこのアーカイブのキャッシュを使用しないことをお勧めします;代わりに、ここから受け取ったデータ用に最適化されたバージョンを使用することをお勧めします。