分散Key-Valueストアの本命「Bigtable」
連載インデックスへ
分散Key-Valueストアの本命「Bigtable」(3)

ここが大変だよBigtableとGoogle App Engine


有限会社スティルハウス
吉川和巳
2009/11/11


2つのインデックス「シングルプロパティ」「コンポジット」


「シングルプロパティインデックス」がカギ

 Datastoreサービスでは、あるテーブルに含まれるすべてのエンティティについて、すべてのプロパティ(テーブルのカラムに相当)の値をキーとして並べた「シングルプロパティインデックス」と呼ばれるインデックステーブルが自動的に作成されます。

 例えば、テーブルEmpが備える「name」「age」「dept_key」という3つのプロパティについて、「テーブル名+プロパティ名+プロパティ値」をキーとし、「Empテーブルの各行のキー」を値とする以下のようなインデックステーブルが作成されます。

図2 シングルプロパティインデックスによるクエリの例
図2 シングルプロパティインデックスによるクエリの例

 Datastoreサービスでは、このシングルプロパティインデックスを用いることにより、アプリケーションが実行するクエリを「インデックスとスキャンの組み合わせ」に背後で変換しています。

 例えば、上述の「age >= 20 & age <= 40」という範囲検索のクエリは、上記例のシングルプロパティインデックスのキー「Emp/age/20」から「Emp/age/40」までのBigtableスキャンに変換されます。これはいわば、「想定されるすべてのクエリの検索結果をあらかじめインデックステーブルに並べておくようなもの」です。

「コンポジットインデックス」は必要最小限に

 しかし、こうしたDatastoreサービスのクエリ機能は、あくまでも「RDB風な条件検索をBigtableでまねたもの」であって、実際のRDBのクエリやジョインが提供する機能に比べて数多くの制約や制限を抱えています。

 例えば、シングルプロパティインデックスによるクエリでは、「あるプロパティを対象に不等号(>、>=、<、<=)を使って範囲指定してしまうと、同じクエリ上でほかのプロパティを条件指定に含められない」という制限があります(なお、等号「=」による条件指定のみであれば複数プロパティを同時に使用できます)。

 よって、例えばプロパティageの範囲指定だけでは、対象エンティティ数が数千〜数万件に膨らんでしまうような状況であると、実用的な時間内(例えば、数秒程度)でクエリを終了できなくなってしまいます。これがRDBであれば、まずプロパティdept_keyで特定部署に絞り込み、加えてプロパティageの範囲指定を行うことで、対象行を数百行以下に抑えるといった対策が可能です。しかし、シングルプロパティインデックスだけでは、そうした複雑な条件指定に対応できません。

 そうした場合は、もう1つの種類のインデックスである「コンポジットインデックス」が利用できます。これは、シングルプロパティインデックスと同様のインデックステーブルを、複数のプロパティ値を組み合わせて作成したものです。なお、コンポジットインデックスを作成するには、App Engineの開発者がXML設定ファイルを通じて明示的に作成を指示する必要があります。

図3 コンポジットインデックスによるクエリの例
図3 コンポジットインデックスによるクエリの例

 このコンポジットインデックスを用いることで、「dept_key = 'D1' & age >= 30 & age <= 40」といった2つのプロパティを条件指定に利用可能になります。

 しかし、コンポジットインデックスの問題点は、「あまり安易に多用はできない」という点です。インデックスであるからには、対象のテーブルの行が更新されるたびにインデックスも更新が必要です。よって、コンポジットインデックスを何十個も作成してしまうと、パフォーマンスの低下が懸念されます。

 よって、既存のSQL文に含まれる検索条件を次々とコンポジットインデックスに置き換えていくのではなく、「ここぞ」という重要な用途に絞って使う必要があります。

 基本的には、シングルプロパティインデックスによるクエリとプログラムコード上でのフィルタリングソートを組み合わせるのが定石といえるでしょう。

そのほかのDatastoreクエリの“大変”なトコロ

 Datastoreによるクエリには、ほかにも以下のようなさまざまな制約があります。

テーブルの結合(join)ができない

 Datastore APIの最大の制約は、やはりテーブルの結合(join)ができない点です。その対処として、結合対象となる個々のテーブルへのクエリを個別に実行する方法や、テーブル設計を非正規化し結合済みのテーブルとする方法があります。

 またDatastore APIでは、エンティティに「List Property」と呼ばれる複数の値を保持するプロパティを持たせることができるので、これを利用して一対多の関連を表現できます。

クエリ構文の制約

 クエリの構文には多数の制約があり、その代表が「LIKEによる部分一致検索ができない」というものです(前方一致検索はサポートする)。よって全文検索を実施したい場合は、検索対象の文字列からキーワードを切り出した転置インデックスをアプリケーション側で作成して利用する必要があります。

 またクエリでは、「OR」「!=」が使えないほか、不等式条件(「<」「<=」「>=」「>」)を同時に複数のプロパティに適用できない不等式条件に指定したプロパティが最優先でソートされるといった制約があります。

HTTPリクエストの処理時間は30秒まで

 これはDatastore APIの制約ではなく、App Engine全体の制約です。1つのHTTPリクエストに対しては30秒以内にHTTPレスポンスを返す必要があり、その時間を超えると例外が発生する仕組みです。よって、Datastore APIを介して大量のデータにアクセスしたり、バッチ処理を実行したりする場合には、個々の処理を25秒程度で切り上げて、次のリクエストで処理の続きを再開するようなロジックが必要となります。

集約関数や組み込み関数がない

 「min()」「max()」「sum()」といった、SQLの集約関数、およびgroup byの機能がサポートされていません。よって、例えば「あるプロパティの最大値」を得るには、そのプロパティの降順でソートするクエリを実行し、最初の1行を得るといった工夫が必要です。

逆に「複雑なクエリやジョインなんていらないさ」

 このように、Datastore APIでは従来のRDBでは「当たり前」にできたことがまったくサポートされていないことも少なくなく、相当なワークアラウンドを強いられることもしばしばです。

 実際に筆者の開発案件でも、RDB上に構築された既存のスキーマをそのままDatastore APIに載せただけでは、クエリ構文の制約が障害となって要件を満たせない状況が発生しました。そこで、「テーブル結合の代用としてインデックス的に使用するテーブルを新たに追加する」というワークアラウンドで対処しています。

 しかし、これらの制約を前向き受け止め、「逆に考えるんだ、複雑なクエリやジョインなんていらないさ」という発想で、「分散KVS時代の新たなデザインパターン」を構築していけるかが、冒頭で紹介した事例のような2けたレベルの劇的なコストダウンを可能にする発想の転換への近道ではないかと筆者は考えます。

結局、DatastoreのAPIはどれがいいの?

 App Engineをこれから使おうと考えるJava開発者は、Datastoreサービスが提供するJDO、JPA、そしてLLという3種類のAPIのうちどれを選択すべきかが、悩ましいポイントです。これら3つのAPIのうち、グーグルが最も豊富なドキュメントやサンプルを提供しているのはJDOベースの実装です。

 一方、JPAやLLについては、APIドキュメント以外にあまり豊富なドキュメントは用意されていません。そのため、筆者を含め、Datastoreサービスを使い始める方の多くは、まずJDO実装から学んでいます。しかし実はここ数カ月の間に、App Engine Java版の利用者の間からは、JDO実装について、以下のように指摘されるようになりました。

  • パフォーマンスが低い(LLのおよそ1/3)
  • Bigtable本来の機能から離れ過ぎていて理解しにくい
  • バグがある

 そのため、App Engineを使いこなす中級者は試行錯誤しながらLLを使い始める方もいます。また、ひがやすを氏が提供するApp Engine対応のMVCフレームワークSlim3」に備わる、「Slim3 Datastore」は、LLと同等のパフォーマンスを維持しながら、簡潔で使いやすく理解しやすいAPIを提供しており、急速に導入事例が増えつつある状況です。

表 Datastoreサービスの4つのAPIの比較
  パフォーマンス 書きやすさ・
理解しやすさ
ドキュメント 事例の豊富さ
JDO ×
JPA ×
LL ×
Slim3

 また、Slim3のサイトで公開されているパフォーマンステストツールを使うと、JDOに比べてLLおよびSlim3が3〜4倍ほど高速なことを実際に確認できるので、参考にしてみてはいかがでしょうか。

図4 LL、Slim3、JDOのパフォーマンス比較結果
図4 LL、Slim3、JDOのパフォーマンス比較結果

@IT関連記事


いま再注目の分散処理技術
最近注目を浴びている分散処理技術「MapReduce」の利点をサンプルからアルゴリズムレベルで理解し、昔からあるJava関連の分散処理技術を見直す特集企画
Java Solution」フォーラム
Hadoop+Hive検証環境を構築してみる
Hive――RDB使いのためのHadoopガイド(前)
 Hadoop上でSQLライクな操作が可能なDWH向けのプロダクトHive。RDBに慣れた人にも使いやすいので、ぜひ試したい
Database Expert」フォーラム 2009/2/27
“動物図鑑”で知るCouchDBの特徴
ゆったリラックス! CouchDBがあるところ(1)
 ドキュメントをデータとして管理し、Webで公開することに最適化したオープンソースの「CouchDB」。RDBMSと何が違う?
Database Expert」フォーラム 2009/9/4
memcached+PostgreSQLで高速Webシステム構築
memcachedの使い方【前編】
 memcachedは多くの大規模サービスで運用実績があるシンプルなキャッシュサーバ。MySQLだけでなくPostgreSQLとの相性も抜群だ
Database Expert」フォーラム 2008/7/30
Google App Engineの3つの「簡単」コンセプトとは
インタビュー特集:Google直伝!(4) 発表から一年。バージョンアップされたGoogle App Engineでは、どんなアプリケーションが作れるのか?GAEの新たな可能性を聞いた
リッチクライアント & 帳票」フ ォーラム 2009/6/4
Google App Engineは20%プロジェクトから生まれた
Google Developer Day 2008特集(2) プロダクトマネージャーに聞く、App Engineの成り立ちと現状、ビジネス、Amazon EC2との違い、そして日本人への期待
リッチクライアント & 帳票」フ ォーラム 2008/6/24
Google App Engineで作るライフログ
これ、俺ならこう使う(4)
 気になる旬の技術の使い方を紹介する人気コラム。グーグルのWebアプリ・ホスティング「Google App Engine」で次世代ツール「logme」を作りました
リッチクライアント & 帳票」フォーラム 2008/10/15

筆者プロフィール
スティルハウス 吉川 和巳(よしかわ かずみ)
Adobe AIR/Flex、Ruby on Rails、Google App Engine for Javaを主軸とする開発業務に従事しています。

1-2
 

 Index
第3回 ここが大変だよBigtableとGoogle App Engine
  Page1
月間3000万PVの大規模サイトの運用費が月額4万円!?
繰り返すが、Bigtableの検索機能は「スキャン」だけ
Datastoreサービスと3つのAPI
Page2
2つのインデックス「シングルプロパティ」「コンポジット」
そのほかのDatastoreクエリの“大変”なトコロ
逆に「複雑なクエリやジョインなんていらないさ」
結局、DatastoreのAPIはどれがいいの?



Java Solution全記事一覧


TechTargetジャパン

Java Solution フォーラム 新着記事

@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

RSSフィード

キャリアアップ

- PR -
@IT Sepcial

イベントカレンダー

PickUpイベント

- PR -
もっと見る
- PR -

お勧め求人情報

ホワイトペーパーTechTargetジャパン

@IT Sepcial
ソリューションFLASH