前の記事のなかのAzure Table StorageとGAEの比較について、割と普通さんより質問があった。
断っておきたいのは、私自身、AzureとGAEについての知識の温度差はあるものの、(というかAzureはほとんど知らないが)、GAEを持ち上げAzureを蔑むような気持は毛頭ないということ。逆に、Azureについては、私はエンタープライズ向けPaaSでは唯一の選択肢と考えており、現在提案中の案件を含め、いろいろと勉強していかなければならないと考えていること。
そういう意味で、このように質問をいただけるの非常にありがたいと思っている。
とはいえ、Azureを調べた第一印象では、GAEの方がAzureより使い勝手がいいと率直に思っていることは事実であり、GAEびいきになっていることをご容赦願いたい。それから一問一答という形ではなく、具体的にAzureで困ったことを元に、property検索、トランザクション、スケーラビリティとパフォーマンスという形でまとめているので悪しからず。多分、回答としては網羅しているとは思うので、全体を通して読んでいただければ幸いである。
また、間違いはあると思うのでいろいろ突っ込み願いたい。
割と普通と申します。
Azure Table StorageとGoogle BigTableについて、それぞれ疑問点があるので質問させて頂きたいと考えています。
私の勘違いでしたら申し訳ありませんが、「Azure Table Storageは他のクラウドサービスよりも機能不足」と認識
されているのではと考えましたので、ご一考頂ければと考えています。
・Azure Table Storage
-Entity GroupによるトランザクションはTable Storage側でも実行できると思います(EGの意味がGAEと異なりま
す)。どこかの資料で「Table Storageではトランザクション処理はできない」という記述がありましたでしょうか。
-SQL Azureの提供は既存資産互換のためですが、BigTableに対応するものはTable Storageだと考えて頂きたいです。
逆に、現時点でGAE側にSQL Azureに対応するものはありません。何をもって「スケーラビリティをないがしろに
している」とされているのかが理解できませんでした。
-GAE側でも「「破壊的イノベーション」の痛み」は間違いなく伴うと考えていますが、私の認識違いでしょうか?
「GAEと比べても相対的にAzureは設計思想のギャップが大きい」と言われているのか、純粋「にクラウドサービ
スなので設計思想のギャップが大きい」のかが理解できませんでした。
・Google BigTable
-BigTableはCountが取得できるのでしょうか。私が調べた段階では効率的な取得方法が存在しなかったのですが、
「全件データを一旦取得する」等の方法以外がBigTable側で提供されているのでしょうか?
※Azure側でも「見た目上はCountを実行する処理」は書けますが、分散KVSの特性を生かすためにはCount用のテ
ーブルを作成する必要があります
-Table Storageでも「前方一致検索や大小比較を含む検索が可能」は可能だと考えていますが、LINQのWhere関数
等を利用した処理では意図された処理しては不十分でしょうか?
-「PartitionKeyのように物理的な配置をアプリケーションで意識する必要がない」は、意識したくなければ意識す
る必要はありません。データ検索の速度を向上させるために与えられた設計パラメータです。逆にGAE側では「物
理配置を意識することなく、常に高速な検索が可能」という事でしょうか?
※以下の記事を通読していたので、疑問に思っていました
http://agilecat.wordpress.com/2010/05/29/%E3%83%A1%E3%82%B8%E3%83%A3%E3%83%BC%EF%BD%A5%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%81%AE%E6%AF%94%E8%BC%83%E3%83%86%E3%82%B9%E3%83%88-part_1-%EF%BC%9A-%E6%80%A7%E8%83%BD%E3%81%AF-amazon-s3-%E3%81%A8/
-「batch get/batch put」の処理ですが、同じくAzure Table StorageのEntity Group Transactionでは不足でしょ
うか?
また、SLAについての言及がなかったことが気になりました。Azure側では99.9%~99.95%のSLAを保証しておりますが、
GAE側は現時点ではSLAを提示していません。その辺りもご考慮頂ければと考えています。
具体的にAzureで困ったこと(property検索)
おっしゃる通り、私はたしかに、Azure Table StorageはGAEよりも機能不足と感じている。その点について説明したい。
まず、Azureを提案するにあたって、拙作のReflex Tagging Serviceの移植を考えた。これは、統一したEntityモデルと共通のREST APIによる高い可搬性をウリにしており、一度アプリを作ってしまえば、GAEだろうとどこでも動くようにできるフレームワークである。(つまり私たちにとって重要なのはAzureやGAEではなくReflex Tagging Serviceが動く環境なのかどうかである)
ところが、いろいろAzureを調べていくと致命的な課題が見えてきて、結局のところ移植を諦めざるを得なかった。
例えば、property検索については、LINQのWhere関数は確認できたものの、INDEXが内部的に張られるかどうかわからなかった。Indexがなければ全走査になってしまうので、パフォーマンスを考えると対象件数がおのずと限られてくる。
Tagging Serviceでは、例えば、/foo/bar のentityについては、_parent=/foo , _selfid=barというような持ち方をしている。(_parentや_selfidはproperty)
このようにproperty検索を基本としている以上、INDEXのあるなしは、パフォーマンスやスケーラビリティに大きな影響を及ぼすことになる。
property indexを自前で作ろうと思っても、データとindexをatomicに更新する問題が残っている。index更新は、atomicか、あるいは、遅延更新で一方向であっても構わないが、データとIndexをentity groupにすることはできないため、確実に更新する術はないと思われる。
具体的にAzureで困ったこと(トランザクション)
トランザクションを考慮すると問題はもっと複雑になる。Entity Groupを考慮したKeyの設計をどうするか。
例えば、先節で指摘したindex更新の件もそうだし、以下のような3階層のEntity GroupにおけるAncestorQueryの結果など、そもそもAzureでは実現困難に思える機能もGAEにはある。
Person(1)をルートとして、Person(2)とPerson(3)をひとつのエンティティグループに設定する場合、 Person(1)/Person(2)/Person(3)とすることもできるし、Person(1)/Person(2)、Person(1) /Person(3)とすることもできる。
両者はAncestorQueryによる取得の際に結果が異なってくる。・・app engineのエンティティグループ
とはいえ、実は、私たちは3階層のEntityGroup設計を否定しているので、ここでは強く主張しないことにする。
これ以外で使いづらいと感じたのは、PartitionKey。特に、物理的な配置をアプリケーションで意識しながら設計しなければならない(と思った)こと。これは、トランザクションが絡むとなおさら難しい。
Tagging Serviceのkeyについては、親キーが/foo/bar、子キーが/foo/bar,0(数字はrevision)という持ち方をしている。Revisionを付けているのは履歴を保存するためだ。
また、/foo/barに対して複数の別名(alias)が付けられるが、alias /hoge/fugaについては、親キーが/foo/bar、子キーが/hoge/fuga,0という持ち方をしている。
親キーを同一にしているのは、original(/foo/bar)とalias(/hoge/fuga)でatomic性が要求されるからであり、entity groupとして括る必要があるからだ。検索においては、aliasの親キーを指定してancestor queryを使うことで、関連するすべてのentityを1発で取得できる。更新時にはトランザクション制御が利くのでatomic性を維持できる。
さて、これをAzureのTable Storageに当てはめるとどうなるだろうか。
Azureのentity groupは、Partition keyで括るらしい(参考)ので、親キーはPartition keyとし、子キーをRow keyとする。よって、Partition keyに、/foo/bar、Row keyに、/foo/bar,0や、/hoge/fugaなどを付けることになる。
問題は、冒頭で述べたように、データ検索の速度を向上させるために与えられたとされるPartition keyの意味が設計を難しくさせていることである。
entity groupとして括るためにPartition keyを同一にする必要がある。ところが、もっと高速化するためにPartition keyに他の親キーを入れてしまうと、今度はentity groupの粒度が大きくなり、同時実行性能が下がるといった問題が発生する。(詳しくは次節)
また、ancestor queryにしても、GAEでも内部的にはDatastoreのRange Scanが実行されているのでrow keyの前方一致検索で代替できるとは思うが、高速化のためだけに挿入したデータが混ざっていると具合悪い。
このように考えていくと、「データ検索の速度を向上させるために与えられた設計パラメータ」という説明は適切なのかどうか。また敢えてPartition keyとRow keyを分ける必要性があるのかどうか。逆に、データ検索の速度を強調するあまり、Key設計をとても難しくさせているんではなかろうかと思った次第である。
ちなみに、Datastoreでは、同一エンティティグループ所属エンティティは、100%そうなるとは限らないが、同じマシンに存在する可能性が高いらしい。しかし、検索の高速化のためにEntityGroupで括るといったような発想はしない。
具体的にAzureで困ったこと(スケーラビリティとパフォーマンス)
階層の親子関係(例えば/fooと/foo/barや/foo/bar/baz)については、entity groupの対象とはしていない。もし対象としてしまうと、ルートがすべての親になってしまい、entity全体がロック対象となって、同時実行性が著しく低下してしまうことになるからだ。
また、/foo/bar/bazのアクセス時には、/foo/barや/fooなど親階層すべてのACLを取得してアクセス権限を調べなければならないが、これにはBatch GETを使用することで高速化を図っている。Batch GETでは、GAE内部においてRPC通信コストが発生しないため、1件づつ実行するよりはるかに高速である。(ちなみに、このBatchGET/BatchPUTのようなDatastore側(AzureでいえばTable Storage側)でまとめて処理をすることで通信コストを下げるような機能はAzureには見当たらなかった。)
各entryのcountについては、初期のバージョンにおいて、shared counterを使って自作していた(関連記事)が、現在のバージョンでは、countEntities()に制限はなくなり、約10万件を1秒そこそこで実行できるようになった。(参考:Google App Engine1.3.8のcountもlimit付けるとやっぱり超速くなりました)
そもそも、shared counterのようなボトルネックを伴う設計はやってはいけない。Entity Groupの粒度を極力小さくして、同時実効性を高くすることこそが、パフォーマンス向上のための鉄板だと私は確信している。
同時実効性さえ高ければ、一つ一つの処理速度は遅くても大量のデータ処理を短時間で実行できる。例えば、Datastore PUTは、理論値で20TPS(50ms/ transction)である。Entity Groupで括るとさらに遅くなり、秒間8から10が限度といわれている。しかし、エンティティを取得するタスク(Splitter)とエンティティをバッチ処理するタスク(Mapper)を分けることで、47,000件のbatch putを16秒で処理
できたという事例もある。
逆にこういった工夫をしないで単純比較すると(秒間8から10なので) 質問で引用されている記事のような結果となってしまう。
一言付け加えておくが、これは当然の結果とはいえ公平ではない。GAEとAzureを公平に比較するとしたら、SQL Azureではなく、Table Storageとの比較をすべきだろう。
Thus, in the case of AppEngine, we used the offered transaction model inside an entity group. However, it turned out, that this is a big slow-down for the whole performance. We are now in the process of re-running the experiment without transaction guarantees and curios about the new performance results.
繰り返しになってしまうが、私が強調したいのは、単発の処理性能ではなく、Scale Outすることで全体のパフォーマンスを向上させることができるということ。当然、Auto Scaleは必須だし、Spin up時間などはパフォーマンスに含める必要があると考えている。その点、AzureはAuto Scale機能はなく、Management APIでインスタンスを増やした際に十数分かかってしまうのは致命的だと感じている。
最後に破壊的イノベーションの痛みについて
現時点でGAEにはSQL Azureに相当するものがない。個人的には、少なくとも、スケールしないRDBは必要ないとは思っている(※)が、GAE4Bが出るまでは、SQL Azureとは比較できるものはない。
(※ RDBはクラウドの世界では禁断の果実。きっと大いなる罠になると思っているので、GAE for Businessは出ない方がいい。)
もちろん、RDBはRDBで進化を続けていて、無限に拡張できる(本当かどうかは知らないが)IBM purescaleや、75万QPSを実現したMySQL+HandlerSocketといった技術もあるのは承知している。ただ、MAX50GBなんてのは論外である。
でもいざ作るとなると、Tagging Service程度のものであっても、RDBに比べたら大変なわけで、「破壊的イノベーションの痛み」はある。これはあくまで個人的な印象だが、GAE Datastoreであれば、Tagging Service程度であれば割と楽に作れるとは思う。
イノベーションの痛みについて関していえば、少なくともajnに集うような濃い連中であれば、痛みを全く感じてない(ように見える)。私から言わせれば、一見うさぎに見えて竹やぶの中を全速力で突っ走る犬である。血だらけになって痛そうだけど本人は全く気にしていないようだ。
でも、Azure Table Storageで作ることはとても辛いと感じるだろう。少なくとも私はできないと感じて諦めた。そういう意味で、AzureがやらなければならないことはGAEより多いはずだ。
こと、Table Storageに関しては、SQL Azureに比べて日が当たらないというか、進化が遅々として進まない気がしている。提案も何かに付けてSQL Azureが中心である。
それから、私が知らないだけかもしれないが、Key/Valueに精通した技術者が少ないのも気になる。こういう面をすべて含めて、スケーラビリティを蔑ろにしているんじゃないかと感じている次第である(気分を悪くされた方がいたらゴメンなさい)
最後にSLAについて。
Azure側では99.9%~99.95%のSLAを保証しておりますが、
GAE側は現時点ではSLAを提示していません。
これは全くそのとおりであって、GAEの信頼性が低いことは、実際に経験してみるとよくわかる。ビジネス向きではないのは明らかで、Azureと比べるまでもない。