大きく値下げしたGoogle App Engineを更に低コストで使う工夫(1) Small Opsの活用

アマチュアですがプログラミングが好きで、特にGoogle App Engineであれこれ試しています。

登場時こそ大きな注目を集めたGoogle App Engineですが、癖が強く、価格改定などの紆余曲折もあって、EC2などAWSと比べると、最近まで随分下火になっていました。 それがGoogleクラウドプラットフォーム戦略強化や、大きめの成功事例も出始めたことで、ここ数ヶ月、また注目されはじめているように感じます。

Google App Engineの魅力として一番に挙げられやすいのが、無料でも使える点です。 本来はスケール性こそ最大の魅力ですが、やはり無料枠の存在は大きいです。 無料で使い始められるからこそ、私のような資金の無い個人でも色々試せますし。 しかし無料で収まるような小さな用途であれば、格安のVPSなどを小分けにして使った方が、開発にPaaS的な制約もなく、ロックインもされません。

無料にこだわるんでなく、スケール性という最大の魅力を活かせるような方向で、どう不要なコストを減らすかという考え方が健全だと思います。 そこで、これまで自分がGoogle App Engine使ってきて試した、コストを抑えるための工夫を、少ないですがひとつずつおさらいしてみます。

まずは最近もっともインパクトのあった、DatastoreのSmall Ops無料化について。

大幅値下げと課金設定

おさらい前にまず、最近の大幅な値下げと、課金設定の重要性について。

2014年4月1日からの新料金体系による大幅値下げ

この1~2年でGoogle App Engineを試し、コストを理由に見限った方は、一度今回の新料金体系を確認してみてください。 以下のページに詳細がありますが、これまでの料金設定を知っていると、かなり攻撃的に見えます。

最もインパクトが大きいのは、DatastoreのSmall Ops無料化。 後述しますが、これのお陰でDatastore周りのコストをかなり下げる余地が出てきました。 Datastoreでは更にReadやWriteのOpsも値下げされています。

Instanceの料金も、$0.05/instance hourからに値下げ。 他に、メール送信やPagespeed、そしてプレビュー中ですがソケットAPIも無料化。

これらの料金見直しと、同じく値下げされたGoogle BigqueryやGoogle Compute Engineなど、他のGoogle Cloud Platformサービスと組み合わせを考えれば、以前よりappengineの魅力は大きく増したと思います。

課金設定した方が良い

appengineは無料で始められるのが大きな魅力です。 ただ、実際に運用するのであれば課金設定だけはしておくべきで、以下の理由があります。

  • Socketなど課金設定をしておかないと使用できないAPIがある
  • 課金設定で各リソースやAPIのQuota Limitが大きく増える

ということで、課金設定は是非しておくべきです。 課金上限は非常に小さくできますし、課金される程アプリケーションが利用されているのなら、ペイできる収益を生む余地はある筈です。

Datastoreのコスト抑制

なんといってもGoogle App Engineの特徴であり、選定からの除外理由にもなるのが、標準のデータベースであるDatastoreです。

DatastoreはGoogleBigtableを、PaaS用に提供しているような代物です。 スケールアウトを重視した分散KVSであり、そのためRDBMSと比べると制約が色々あるように感じます。 DatastoreはRDBMSで当たり前だった正規化が逆効果となったり、クエリに制限があったり、また設計次第でとてつもなくコストが違ってきます。

今回はSmall Opsを採り上げますが、はじめに少しDatastoreのオペレーションについて。

3種類のops

Datastoreに対する操作はPythonJava、Goなど各言語のAPIで行いますが、内部では3種類のオペレーション(Ops)に分解され、課金もこのOps単位で行われます。 そのOpsには以下の3種類があります。

Read Ops
エンティティの取得時に発生
Write Ops
エンティティの登録時(削除含む)に発生
Small Ops
キーのみを返すクエリなどで発生

これらOpsの内、ReadとWriteは 100,000回あたり$0.06という料金ですが、Smallは値下げによって無料となりました。

Small Opsの活用

$0.001でも無料とは雲泥の差で、Smallが無料になった以上、とことんこれを活用する方向に調整すべきです。 もちろん、今後の値上げ再び有料化される可能性はゼロではありませんが、そもそもDatastoreの構造上、Small OpsはReadやWriteに比べて低価格に設定されるべきものであり、活用すべきことに変わりはありません。

Small Opsは、キーのみを返すクエリによって発生します(数値idに値を割り当てる場合にも発生しますが、コストにそう響くわけではないので省きます)。 キーやエンティティというDatastoreの構造が関わってくるのですが、要は「インデックスだけを見て返せるクエリはSmall Ops」という事です。

Datastoreは、原則として非常に単純なスキャンしかできないエンティティに対して、柔軟なクエリを実行するため、別途インデックスを生成しています。 そのインデックスにはクエリの対象となる値とキーを、全て列記して保持しており、インデックスに載っていないエンティティは、クエリで検出できません。 クエリによってインデックスが走査され、該当するキーを見つけ出すことで、エンティティを特定できます。

この発見したキーからエンティティ本体を読み出す際に、本来はReadが発生します。 しかしインデックスを見るだけで済む場合には、より低コストなSmall Opsとなります。 これがキーのみを返すクエリです。

キーのみを返すクエリ

無料のSmall Opsはクエリにおいて活用します。

DatastoreはKVSであり、キーによってエンティティを取得できます。 しかしそれだけでアプリケーションを構築するのは難しく、RDBMSのような問い合わせが必要です。 それを実現するのがクエリです。

クエリは前述の通り、インデックスの走査です。 エンティティが持つ値に基いて生成されるインデックスは、掲載されるべき全エンティティの値とキーがペアになった表です。 このインデックスの走査時に「キーのみを返す」と指定する事。 これがSmall Opsです。

実はこのキーのみを返すクエリを用いない場合、つまり通常のクエリでは、用途によっては非常に大きな無駄が生じます。 インデックスを走査して該当するキーを抽出し、そのキーを用いて対応するエンティティを取得、そのエンティティをクエリの結果を返す、というのが通常のクエリの流れです。 しかしカウント目的やオフセットを含む場合など、該当した全てのエンティティ本体が必要でない場合、最大で該当したエンティティの数だけ、無駄なRead Opsが発生してしまいます。 クエリの結果としてキーだけを受け取ることで、このような無駄を排除出来ます。

もちろん、エンティティ本体が必要な場合は、結果のキーからエンティティを取得するため、Read Opsが発生します。 しかしクエリの目的や状況に応じて、キーで留めたり、必要な分のみエンティティを取得したりと、無駄なReadの発生を防ぐことが出来ます。

無料のクエリ

値下げによるSmall Opsの無料化では、無料のクエリが可能になり、クエリの利用の幅が広がりました。

まず、クエリのオプションによって発生するOpsの違いです。

通常のクエリ1件
1 Read Ops + 該当エンティティ* Read Ops
キーのみのクエリ
1 Small Ops(但し必要なエンティティ分のRead Ops

このように、通常のクエリでは最低1回のRead Opsが発生し、更に該当するエンティティの分のRead Opsが発生します。 キーのみを返すクエリではSmall Opsしか発生しませんが、従来はこれが有料でした。

キーを指定して取得できれば1 Read Opsで済みます。 安いとはいえ有料のSmall Opsの発生を抑えるには、クエリを実行しなくてもキーが特定できるような設計が必要です。 しかしSmall Opsが無料となった以上、キーのみを返すクエリを使う限りは、クエリは無料で実行できることになります。 もちろんクエリ無しにキーで直接取得するのに比べれば、多少遅延は発生します。 しかし「クエリでキーのみを取得」 → 「必要なエンティティのみをキーで取得」という手順を、どの状況でもコストの違いなく利用できる方が、メリットは大きいと思います。

各言語別のキー

最後に、キーのみを返すクエリの指定方法を説明する、各言語のリファレンス。 Pythonはdbとndbという2つのパッケージがありますが、特に理由がなければ、ndbを使った方が良いでしょう。