Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Sthseminar Gae 20090715

5,458 views

Published on

  • Be the first to comment

Sthseminar Gae 20090715

  1. 1. GAE/J ってどう使う? スティルハウス 佐藤一憲
  2. 2. GAE/J ってどう使う? <ul><li>この資料のダウンロード </li></ul><ul><ul><li>http://tinyurl.com/kaz279 </li></ul></ul>
  3. 3. 自己紹介 <ul><li>スティルハウス 佐藤一憲 </li></ul><ul><ul><li>id:kazunori_279 </li></ul></ul><ul><ul><li>http://www.sth.co.jp/ </li></ul></ul><ul><li>主な業務 </li></ul><ul><ul><li>開発 </li></ul></ul><ul><ul><ul><li>Adobe Flex/AIR 、 Rails 、 GAE/J </li></ul></ul></ul><ul><ul><li>テクニカルライティングや翻訳 </li></ul></ul><ul><ul><ul><li>ペンネーム吉川和巳 </li></ul></ul></ul><ul><ul><ul><li>@IT や ITpro など </li></ul></ul></ul><ul><ul><li>セミナー講師など </li></ul></ul>
  4. 4. アジェンダ <ul><li>GAE と「ご都合 .com 」事例 </li></ul><ul><li>GAE のサーバー構成とスケーラビリティ </li></ul><ul><li>Bigtable と Datastore </li></ul><ul><li>Datastore ってどう使う? </li></ul>
  5. 5. GAE と「ご都合 .com 」事例
  6. 6. GAEとは <ul><li>Google App Engine </li></ul><ul><ul><li>自分のアプリをGoogleインフラで運用 </li></ul></ul><ul><ul><ul><li>Python版とJava版 </li></ul></ul></ul><ul><ul><li>クラウドコンピューティングサービス </li></ul></ul><ul><li>GAEのすごいところ </li></ul><ul><ul><li>無償で使える </li></ul></ul><ul><ul><li>Bigtableが簡単に使える </li></ul></ul><ul><ul><li>サーバ構築不要、デプロイ簡単 </li></ul></ul>
  7. 7. ご都合 .com (gotsugo.com) <ul><li>ご都合 .com </li></ul><ul><ul><li>スケジュール共有ツール </li></ul></ul><ul><ul><li>会議・飲み会に </li></ul></ul><ul><ul><li>無償・登録不要 </li></ul></ul><ul><ul><li>5/6 公開 </li></ul></ul><ul><ul><li>6 日間で 2000 UU </li></ul></ul>
  8. 8. ご都合.comの中身 <ul><li>Flexクライアント </li></ul><ul><li>Google App Engine for Javaサーバ </li></ul><ul><ul><li>BlazeDS </li></ul></ul><ul><ul><li>ビジネスロジックのクラス </li></ul></ul><ul><ul><li>Entityクラス </li></ul></ul><ul><li>開発期間:5日間 </li></ul><ul><ul><li>設計・実装・テスト・ドキュメント作成など </li></ul></ul>
  9. 9. BlazeDSを動かすには… <ul><li>Martin 氏のブログを見よ! </li></ul><ul><ul><li>http://martinzoldano.blogspot.com/2009/04/appengine-adobe-blazeds-fix.html </li></ul></ul><ul><ul><li>Martin さんありがとう!! </li></ul></ul><ul><li>BlazeDS ソースのビルドが必要 </li></ul><ul><ul><li>まずは BlazeDS をビルドする環境を用意 </li></ul></ul><ul><li>要点 </li></ul><ul><ul><li>JMX API の管理機能はオフに </li></ul></ul><ul><ul><li>AbstractAmfInput.java を修正 </li></ul></ul><ul><ul><li>BaseHTTPEndpoint.java を修正 </li></ul></ul><ul><li>以下ページでも詳しく解説 </li></ul><ul><ul><li>http://tinyurl.com/gaetips </li></ul></ul>
  10. 10. GAE のサーバー構成と スケーラビリティ
  11. 11. GAE の現状 <ul><li>現在の利用状況 </li></ul><ul><ul><li>8万以上のアプリを収容 </li></ul></ul><ul><ul><li>140M PV/day </li></ul></ul><ul><ul><li>20万人以上の開発者が利用 </li></ul></ul><ul><li>統合環境を提供 </li></ul><ul><ul><li>サーバーの構築や管理が不要。デプロイが容易 </li></ul></ul><ul><ul><li>ログ管理、管理コンソールや各種ツールを提供 </li></ul></ul>
  12. 12. GAE の特徴 <ul><li>スケーラブルな Web アプリ構築のためのベストプラクティス利用を促す環境 </li></ul><ul><ul><li>個々のリクエストの処理時間やリソース消費を制限 </li></ul></ul><ul><ul><li>ステートレス設計と内蔵 API の利用 </li></ul></ul><ul><ul><li>Datastore によるパーティション化されたデータモデルの利用 </li></ul></ul>
  13. 13. GAE の特徴 <ul><li>非常に高いスケーラビリティと可用性を備えるGoogleのクラスタ環境を簡単に利用できる </li></ul><ul><ul><li>自動クラスタリングによる負荷分散と高可用性を提供 </li></ul></ul><ul><ul><li>アプリの負荷状況に応じてApp Serverを動的に増減 </li></ul></ul><ul><ul><li>アプリ間の隔離性を維持、個々のアプリの安全性とパフォーマンスを確保 </li></ul></ul><ul><li>既存のGoogleテクノロジーがベース </li></ul>
  14. 14. Google App Engine Stack の構成 <ul><li>Google App Engine Stack の構成 </li></ul>
  15. 15. GAE Stack の構成要素 <ul><li>App Master </li></ul><ul><ul><li>アプリのデプロイ管理やバージョン管理 </li></ul></ul><ul><ul><li>Front EndへのApp Server位置の通知 </li></ul></ul><ul><ul><li>全体の統括 </li></ul></ul>
  16. 16. GAE Stack の構成要素 <ul><li>Front End </li></ul><ul><ul><li>HTTPのリクエスト受信とレスポンス送信を担当 </li></ul></ul><ul><ul><li>リクエストはクライアントから最も近いGoogleデータセンターに到着 </li></ul></ul><ul><ul><li>Google内部ネットワークを経由してFront Endに到着 </li></ul></ul><ul><ul><li>リクエストは最大10MBまで </li></ul></ul><ul><ul><ul><li>独自ドメイン利用時は1MBまで </li></ul></ul></ul><ul><ul><li>スタティックコンテンツはスタティックファイルサーバーに、ダイナミックコンテンツはApp Serverに転送 </li></ul></ul>
  17. 17. GAE Stack の構成要素 <ul><li>スタティックファイルサーバー </li></ul><ul><ul><li>スタティックコンテンツを提供するサーバー </li></ul></ul><ul><ul><li>スタティックコンテンツとは </li></ul></ul><ul><ul><ul><li>appengine-web.xml で static 要素に指定した、静的コンテンツ </li></ul></ul></ul><ul><ul><ul><li>HTML ファイルや画像ファイル、動画ファイルなど </li></ul></ul></ul><ul><ul><li>アプリコードとは別の専用サーバーに配置される </li></ul></ul><ul><ul><li>Front End が直接処理し、 App Server は関与しない </li></ul></ul>
  18. 18. GAE Stack の構成要素 <ul><li>App Server </li></ul><ul><ul><li>ダイナミックコンテンツのリクエストを処理。アプリコードが動作するアプリサーバー </li></ul></ul><ul><ul><li>多数のアプリを同時に収容し、アプリ間の隔離性を確保する </li></ul></ul><ul><li>各 API のサーバー </li></ul><ul><ul><li>Datastore 、 Memcache など、各種 API に基づくサービスを提供 </li></ul></ul>
  19. 19. GAE Stack の構成要素 <ul><li>GAE が提供する API </li></ul>
  20. 20. App Server について <ul><li>App Server の構成 </li></ul><ul><ul><li>Java 版は JDK 1.6 ベースの JVM </li></ul></ul><ul><ul><ul><li>サンの 1.6 版 JVM をそのまま使っている </li></ul></ul></ul><ul><ul><ul><li>アプリのロードを速くするため( Server VM ではなく) HotSpot Client VM を使っている </li></ul></ul></ul><ul><ul><ul><li>Jetty ベースのアプリケーションサーバー </li></ul></ul></ul>
  21. 21. App Server について <ul><li>App Server によるアプリの扱い </li></ul><ul><ul><li>アプリはステートレスに設計する </li></ul></ul><ul><ul><ul><li>アプリにステートは持たせない </li></ul></ul></ul><ul><ul><ul><ul><li>ステート=サーブレットのフィールドなど、 JVM 上の状態や変数 </li></ul></ul></ul></ul><ul><ul><ul><ul><li>アプリが異なる App Server に移動する場合でも、ステートは引き継がれない </li></ul></ul></ul></ul><ul><ul><ul><ul><li>多数の App Server による負荷分散とフェイルオーバーを簡素化 </li></ul></ul></ul></ul><ul><ul><ul><ul><li>ステートは Datastore や memcache に保存する </li></ul></ul></ul></ul><ul><ul><li>Datastore と memcache の内容は共有 </li></ul></ul><ul><ul><ul><li>どの App Server からでも同じ内容にアクセスできる </li></ul></ul></ul><ul><ul><ul><li>HttpSession は Datastore に保存される </li></ul></ul></ul><ul><ul><ul><ul><li>セッションの掃除が必要 </li></ul></ul></ul></ul>
  22. 22. App Server について <ul><li>ApiProxy による API コールの扱い </li></ul><ul><ul><li>API コールは ApiProxy を経由して実行される </li></ul></ul><ul><ul><li>個々のリクエストの情報( ThreadLocal )を保存する </li></ul></ul><ul><ul><li>API コールを AOP のようにインターセプトして、その前後に追加処理を挿入できる </li></ul></ul><ul><ul><ul><li>ログ記録など </li></ul></ul></ul><ul><ul><li>クラスパス変更( JAR 入れ替え?)だけで、 API のサービスプロバイダーを変更できる </li></ul></ul><ul><ul><ul><li>単体テストの記述に便利 </li></ul></ul></ul>
  23. 23. App Server について <ul><li>App Serverのサンドボックスによる制限 </li></ul><ul><ul><li>30秒以上を要するリクエスト処理 </li></ul></ul><ul><ul><ul><li>時間のかかる処理はTask Queueで </li></ul></ul></ul><ul><ul><li>レスポンス送出時のデータストリーミング </li></ul></ul><ul><ul><ul><li>cometできない </li></ul></ul></ul><ul><ul><li>ファイルシステムへの書き込み </li></ul></ul><ul><ul><li>外部サーバへのソケット接続 </li></ul></ul><ul><ul><li>スレッド生成 </li></ul></ul><ul><ul><li>ガベージコレクション実行やシステム停止 </li></ul></ul><ul><ul><li>カスタムクラスローダの利用 </li></ul></ul>
  24. 24. App Server について <ul><li>GAE/J で利用可能な既存資産 </li></ul><ul><ul><li>Dependency Injection Frameworks </li></ul></ul><ul><ul><ul><li>Guice, Spring, etc. </li></ul></ul></ul><ul><ul><li>Aspect Oriented Programming </li></ul></ul><ul><ul><ul><li>AspectJ, Spring AOP, etc. </li></ul></ul></ul><ul><ul><li>Web frameworks </li></ul></ul><ul><ul><ul><li>Google Web Toolkit, Tapestry, BlazeDS (Flex), etc. </li></ul></ul></ul><ul><ul><ul><li>Grails (Just Announced!) </li></ul></ul></ul><ul><ul><li>Alternate JVM languages </li></ul></ul><ul><ul><ul><li>Scala, Rhino, JRuby, Jython, Clojure, Groovy, PHP, etc. </li></ul></ul></ul>
  25. 25. GAE のスケールアウト <ul><li>高負荷時の自動デプロイ </li></ul><ul><ul><li>高負荷状態が50分程度続くと新しいApp Serverが追加されデプロイされて負荷分散し、負荷が低くなるとアンデプロイされるようです </li></ul></ul><ul><ul><ul><li>http://groups.google.com/group/google-app-engine-japan/browse_thread/thread/24cedb6808d634bf </li></ul></ul></ul><ul><li>アプリが受けられる負荷に上限はあるか? </li></ul><ul><ul><li>現状、safety limitが設けられている。それを超える負荷を扱いたい場合は、app engineチームに連絡してほしい。案件ごとにsafety limitを解除できる </li></ul></ul>
  26. 26. GAE のスケールアウト事例 <ul><li>ホワイトハウスの&quot;Open For Questions&quot; </li></ul><ul><ul><li>2日間だけ提供された投票サイト </li></ul></ul><ul><ul><ul><li>その結果を受けてオバマ大統領が記者会見を行った </li></ul></ul></ul><ul><ul><li>10万件の質問と360万件の投票を受け付けた </li></ul></ul><ul><ul><ul><li>ピーク時には毎秒700回のクエリを実行した </li></ul></ul></ul><ul><ul><li>GAEをout of boxで使用 </li></ul></ul><ul><ul><ul><li>Google Moderatorのソースをベースに、ホワイトハウス側の開発者がチューンしてデプロイ。Google側による特別な作り込み等は行っていない </li></ul></ul></ul><ul><ul><li>GAE上の他のアプリケーションには一切影響なし </li></ul></ul>
  27. 27. GAE のスケールアウト事例 <ul><li>&quot;Open For Questions&quot; のトラフィック推移 </li></ul>
  28. 28. そのほか <ul><li>Java 版と Python 版の性能の違いは? </li></ul><ul><ul><li>きちんとした比較はしていない。使い方によって異なるが、あまり大きな違いはない。 GAE 以外の環境で両者を比べた場合と同じ程度。 </li></ul></ul><ul><li>Comet サポートは? </li></ul><ul><ul><li>現状ではレスポンスのストリーム送信に対応しないため、 Comet は使えない。現状、 Comet サポートはロードマップにはないが、 XMPP はサポート予定なので、プッシュ通信は実装できるようになる </li></ul></ul>
  29. 29. そのほか <ul><li>Virgin America 事例 </li></ul><ul><ul><li>http://www.dayinthecloud.com/lounge/ </li></ul></ul>
  30. 30. Bigtable とは
  31. 31. Bigtableの概要 <ul><li>Bigtable とは </li></ul><ul><ul><li>構造化データを管理するための分散化ストレージ </li></ul></ul><ul><ul><li>膨大な数の汎用サーバーをつなげてペタバイト規模のデータを扱えるよう設計されている </li></ul></ul><ul><ul><ul><li>現在およそ数 PB </li></ul></ul></ul>
  32. 32. Bigtableの概要 <ul><li>Bigtable の歴史 </li></ul><ul><ul><li>およそ 7 人年の開発作業を経て、 2005 年 4 月からプロダクション利用を開始 </li></ul></ul><ul><ul><li>2006 年時点では、 Google の 60 以上のプロジェクトが Bigtable を利用 </li></ul></ul><ul><ul><ul><li>検索 , Analytics, Finance, Orkut, Earth, YouTube, Map など </li></ul></ul></ul>
  33. 33. Bigtable の構成要素 <ul><li>Bigtableのテーブル </li></ul><ul><ul><li>Bigtableのテーブルは、「分散化された多次元ソートマップ」 </li></ul></ul><ul><ul><ul><li>簡単に言うと、ソート済みのExcel表のようなもの </li></ul></ul></ul><ul><ul><ul><li>個々のセルは履歴を残せる(multidimensional) </li></ul></ul></ul>
  34. 34. Bigtable の構成要素 <ul><li>Bigtableのテーブル </li></ul><ul><ul><li>テーブルの実体は、巨大なkey-value store </li></ul></ul><ul><ul><ul><li>キー:行キー+カラムキー+タイムスタンプ </li></ul></ul></ul><ul><ul><ul><li>行キーは最大64KBまで(大半は10~100バイト) </li></ul></ul></ul><ul><ul><li>行キーの辞書順でソートされている </li></ul></ul><ul><ul><li>行単位のCRUDはアトミックに実行 </li></ul></ul><ul><ul><ul><li>複数行にまたがる更新処理は原子性が保証されない </li></ul></ul></ul><ul><ul><li>古い履歴データや削除された行は、自動的にGC </li></ul></ul>
  35. 35. Bigtableにできること <ul><li>Bigtable にできること </li></ul><ul><ul><li>キーを指定した行単位の CRUD </li></ul></ul><ul><ul><ul><li>行単位の ACID 特性が保証される </li></ul></ul></ul><ul><ul><li>2 種類のスキャン: </li></ul></ul><ul><ul><ul><li>prefix scan: キーの前方一致検索で複数行を一括取得 </li></ul></ul></ul><ul><ul><ul><li>range scan: キーの範囲指定検索で複数行を一括取得 </li></ul></ul></ul><ul><ul><ul><li>ソート済みなので高速に実行できる </li></ul></ul></ul><ul><ul><li>Bigtable ではカラムの値に基づく検索は一切実行できない! </li></ul></ul>
  36. 36. Bigtableにできること <ul><li>prefix scan と range scan </li></ul>
  37. 37. Bigtable の内部構造 <ul><li>Bigtableクラスター </li></ul><ul><ul><li>複数のBigtableテーブルからなるクラスター </li></ul></ul><ul><ul><li>2006年8月時点では、388のBigtableクラスターと24,500のタブレットサーバーが稼働中 </li></ul></ul><ul><ul><ul><li>(おおよそ1~2PB) </li></ul></ul></ul>
  38. 38. Bigtableの内部構造 <ul><li>Google の典型的なクラスターノード構成 </li></ul>
  39. 39. Bigtableの内部構造 <ul><li>個々のノードの基本構成 </li></ul><ul><ul><li>Intelベースの安いPC </li></ul></ul><ul><ul><li>Linux OS </li></ul></ul><ul><ul><li>Schedulerスレーブ </li></ul></ul><ul><ul><ul><li>Schedulerマスターの指示に従ってノード上に各種サービスをデプロイする </li></ul></ul></ul><ul><ul><li>GFSチャンクサーバー </li></ul></ul><ul><ul><ul><li>GFSのチャンク(データ)を保存する </li></ul></ul></ul><ul><ul><li>タブレットサーバー </li></ul></ul><ul><ul><ul><li>Bigtableのタブレットを管理する </li></ul></ul></ul>
  40. 40. Bigtableの内部構造 <ul><li>Bigtableクラスター全体を管理するサービス </li></ul><ul><ul><li>Schedulerマスター </li></ul></ul><ul><ul><ul><li>各ノードに各種サービスをデプロイする </li></ul></ul></ul><ul><ul><li>Chubby </li></ul></ul><ul><ul><ul><li>分散ロックサービス </li></ul></ul></ul><ul><ul><li>GFSマスター </li></ul></ul><ul><ul><ul><li>GFSチャンクサーバー群を統括する </li></ul></ul></ul><ul><ul><li>Bigtableマスター </li></ul></ul><ul><ul><ul><li>tablet server群を統括する </li></ul></ul></ul>
  41. 41. Bigtableの内部構造 <ul><li>タブレット </li></ul><ul><ul><li>Bigtableのテーブルを分割したもの </li></ul></ul><ul><ul><ul><li>テーブルの内容はタブレット単位で各タブレットサーバーに分散保存される </li></ul></ul></ul><ul><ul><ul><li>1つのタブレットは100~200MB程度のデータを保存。それ以上になると分割される </li></ul></ul></ul><ul><ul><ul><li>1台のタブレットサーバーは100以下のタブレットを保存 </li></ul></ul></ul>
  42. 42. Bigtableの内部構造 <ul><li>タブレット </li></ul><ul><ul><li>復旧が高速 </li></ul></ul><ul><ul><ul><li>1 台がダウンしても、その 100 個のタブレットは他の 100 台のサーバーが保有している </li></ul></ul></ul><ul><ul><li>Bigtable マスターが負荷分散を管理 </li></ul></ul><ul><ul><ul><li>高負荷のサーバーからタブレットを移動 </li></ul></ul></ul>
  43. 43. タブレットサーバーのメカニズム <ul><li>タブレットサーバーの階層問い合わせ </li></ul>
  44. 44. タブレットサーバーのメカニズム <ul><li>タブレットサーバーの検索 </li></ul><ul><ul><li>あるキーのデータを取得するとき </li></ul></ul><ul><ul><ul><li>クライアントはタブレットサーバーのIPアドレスを取得 </li></ul></ul></ul><ul><ul><ul><li>DNSライクな階層問い合わせ </li></ul></ul></ul><ul><ul><li>検索の流れ </li></ul></ul><ul><ul><ul><li>ブートストラップとなるChubbyサービスにアクセス </li></ul></ul></ul><ul><ul><ul><li>METADATAタブレットを持つタブレットサーバーのIPアドレスを取得 </li></ul></ul></ul><ul><ul><ul><li>METADATAタブレットには、各キーに対応するタブレットサーバーのIPが記録されている </li></ul></ul></ul>
  45. 45. タブレットサーバーのメカニズム <ul><li>タブレットサーバーの検索 </li></ul><ul><ul><li>タブレットサーバーの検索には、最大で3回のネットワーク通信が必要 </li></ul></ul><ul><ul><ul><li>通常はクライアント側にMETADATAタブレット内容がキャッシュされており、クライアントはタブレットサーバーに直接接続できる </li></ul></ul></ul>
  46. 46. キャッシュ管理とディスクアクセス <ul><li>Bigtableのキャッシュ構造 </li></ul>GFS GFS GFS SSTable (ディスク上のファイル) memtable (キャッシュ) minor compaction (ディスクにflush) major compaction (ゴミファイルをGC) テーブルへのアクセス コミット ログ
  47. 47. キャッシュ管理とディスクアクセス <ul><li>memtable によるキャッシュ管理 </li></ul><ul><ul><li>memtable は、タブレットにコミットされた更新内容を記録するソート済みのバッファ </li></ul></ul><ul><ul><li>個々の更新処理はディスク上のコミットログに記録され、更新内容は memtable に記録される </li></ul></ul>
  48. 48. キャッシュ管理とディスクアクセス <ul><li>マイナーコンパクション </li></ul><ul><ul><li>memtable がいっぱいになると、その内容が SSTable にフラッシュされる </li></ul></ul><ul><ul><li>( Oracle の DBWR +チェックポイントと同様) </li></ul></ul>
  49. 49. キャッシュ管理とディスクアクセス <ul><li>SSTable によるディスク保存 </li></ul><ul><ul><li>SSTable とは、 memtable の内容保存に利用されるファイルフォーマット </li></ul></ul><ul><ul><li>ソート済みのイミュータブル(変更不可)なマップ( key-value ペア) </li></ul></ul><ul><ul><ul><li>イミュータブルなのでファイルアクセス時のロックが不要、同時アクセスを効率的に扱える </li></ul></ul></ul><ul><ul><ul><li>コピーオンライトですばやくタブレットを分割できる </li></ul></ul></ul>
  50. 50. キャッシュ管理とディスクアクセス <ul><li>メジャーコンパクション </li></ul><ul><ul><li>削除されたデータがゴミとして残るので、マーク&スウィープ GC を実行する </li></ul></ul><ul><ul><ul><li>( PostgreSQL の vacuum と同様) </li></ul></ul></ul>
  51. 51. GFSの利用 <ul><li>GFS によるディスクへの書き込み </li></ul><ul><ul><li>GFS ( Google File System )とは、 SSTable 等のファイル保存に用いられるファイルシステム </li></ul></ul><ul><ul><li>ファイルは必ず 3 台以上のサーバーに書き込み </li></ul></ul><ul><ul><ul><li>ローカルの GFS チャンクサーバーが空いていれば、そこに 1 つを書き込み </li></ul></ul></ul><ul><ul><ul><li>残り 2 つは、離れた場所(少なくとも同じラックではない場所)の GFS チャンクサーバーに書き込み </li></ul></ul></ul>
  52. 52. GFSの利用 <ul><li>GFS によるデータのレプリケーション </li></ul>
  53. 53. GFSの利用 <ul><li>GFSによるディスクへの書き込み </li></ul><ul><ul><li>タブレットが移動しない限り、タブレットサーバーはローカルのGFSチャンクサーバーにアクセス </li></ul></ul><ul><ul><li>負荷分散のためタブレットが移動すると、データは残したままタブレットのみ移動 </li></ul></ul><ul><ul><li>バイナリアップグレード時などに、できるだけローカルに置くようにデータを再配置 </li></ul></ul>
  54. 54. Datastore とは
  55. 55. Datastore API とは <ul><li>GAE におけるデータ保存用 API </li></ul><ul><ul><li>Bigtable で実装 </li></ul></ul><ul><li>Datastore API でできること </li></ul><ul><ul><li>CRUD </li></ul></ul><ul><ul><ul><li>エンティティグループ単位で ACID を確保(後述) </li></ul></ul></ul><ul><ul><li>クエリ </li></ul></ul><ul><ul><ul><li>JDOQL または GQL </li></ul></ul></ul>
  56. 56. Datastore API とは <ul><li>GAE/J の Datastore API </li></ul><ul><ul><li>JDO (Java Data Objects) </li></ul></ul><ul><ul><ul><li>オブジェクト永続化の標準 API </li></ul></ul></ul><ul><ul><ul><li>オブジェクトデータベース的 </li></ul></ul></ul><ul><ul><ul><li>ドキュメントがもっとも豊富 </li></ul></ul></ul><ul><ul><li>JPA (Java Persistence API) </li></ul></ul><ul><ul><ul><li>ORM の標準 API </li></ul></ul></ul><ul><ul><li>low-level API </li></ul></ul><ul><ul><ul><li>ドキュメントがあまりない </li></ul></ul></ul><ul><ul><li>DataNucleus ( JDO/JPA の OSS 実装)がベース </li></ul></ul>
  57. 57. Datastore API とは <ul><li>JDOによるCRUDの例 </li></ul>        PersistenceManager pm = PMF.get().getPersistenceManager();         Employee e = new Employee(&quot;Alfred&quot;, &quot;Smith&quot;, new Date());         try {             pm.makePersistent(e);         } finally {             pm.close();         }
  58. 58. Datastore 用語 <ul><li>Datastore 用語 </li></ul><ul><ul><li>カインド( kind )=クラス/テーブル </li></ul></ul><ul><ul><li>エンティティ( entity )=オブジェクト/レコード </li></ul></ul><ul><ul><li>プロパティ( property )=フィールド/カラム </li></ul></ul><ul><ul><li>キー( key )= OID /プライマリーキー </li></ul></ul>
  59. 59. エンティティテーブル <ul><li>エンティティテーブルとは </li></ul><ul><ul><li>エンティティを保存するテーブル </li></ul></ul><ul><ul><ul><li>GAE内のすべてのエンティティテーブルが1つのBigtableテーブルを共有 </li></ul></ul></ul><ul><ul><li>個々のエンティティは、キーで識別 </li></ul></ul><ul><ul><ul><li>キーの辞書順でソートされている </li></ul></ul></ul><ul><ul><li>個々のエンティティのプロパティ内容は、すべて 1 つのカラム にシリアライズされて格納 </li></ul></ul><ul><ul><ul><li>Bigtableの履歴機能は使用していない </li></ul></ul></ul>
  60. 60. キー <ul><li>キー </li></ul><ul><ul><li>アプリ ID +パス+(エンティティ ID またはエンティティ名) </li></ul></ul><ul><ul><ul><li>エンティティ ID は自動採番 </li></ul></ul></ul><ul><ul><ul><li>エンティティ名はアプリ側で設定 </li></ul></ul></ul><ul><ul><li>パス </li></ul></ul><ul><ul><ul><li>エンティティグループのルートエンティティまでのパス </li></ul></ul></ul><ul><ul><ul><li>/Grandparent:Ethel/Parent:Jane/Child:William </li></ul></ul></ul><ul><ul><li>キーの文字列表現 </li></ul></ul><ul><ul><ul><li>キーを含むプロトコルバッファを base64 表示したもの </li></ul></ul></ul><ul><ul><li>キーによるエンティティ取得 </li></ul></ul><ul><ul><ul><li>通常、 20 ~ 40ms で処理 </li></ul></ul></ul><ul><ul><li>参考 </li></ul></ul><ul><ul><ul><li>http://groups.google.com/group/google-appengine/browse_thread/thread/2d2805486432355e/ea6d4d2e3915d155?show_docid=ea6d4d2e3915d155 </li></ul></ul></ul>
  61. 61. キー <ul><li>/Grandparent:Alice /Grandparent:Alice/Parent:Sam /Grandparent:Ethel /Grandparent:Ethel/Parent:Jane /Grandparent:Ethel/Parent:Jane/Child:Timmy /Grandparent:Ethel/Parent:Jane/Child:William /Grandparent:Frank </li></ul>カインド名 エンティティID 注:この図は概念図です (実際の実装ではありません)
  62. 62. プロパティ <ul><li>Datastore のプロパティの特徴 </li></ul><ul><ul><li>variable properties </li></ul></ul><ul><ul><ul><li>エンティティごとにプロパティの数や種類を変えられる </li></ul></ul></ul><ul><ul><ul><li>「プロパティがない」と「プロパティが null 」は区別される </li></ul></ul></ul><ul><ul><li>heterogenous property types </li></ul></ul><ul><ul><ul><li>エンティティごとにプロパティの型を変えることができる </li></ul></ul></ul><ul><ul><ul><li>( GAE/J でこれを使えるかは不明) </li></ul></ul></ul>
  63. 63. プロパティ <ul><li>Datastore のプロパティの特徴 </li></ul><ul><ul><li>multi-value properties (List Property) </li></ul></ul><ul><ul><ul><li>1 つのプロパティに List や tuple を保存できる </li></ul></ul></ul><ul><ul><ul><ul><li>シリアライズして保存される </li></ul></ul></ul></ul><ul><ul><ul><li>クエリの例: name == 'Foo' </li></ul></ul></ul><ul><ul><ul><ul><li>List 内のいずれか 1 つの値が Foo なら true になる </li></ul></ul></ul></ul><ul><ul><ul><li>非正規化や設計の最適化に活用できる </li></ul></ul></ul><ul><ul><ul><ul><li>1:N 関連の代わりに使う(非正規化) </li></ul></ul></ul></ul><ul><ul><ul><ul><li>ジョインテーブルの代わりに使う </li></ul></ul></ul></ul><ul><ul><li>Serializable オブジェクトを格納可能 </li></ul></ul>
  64. 64. クエリ <ul><li>Datastore のクエリとは </li></ul><ul><ul><li>複数のエンティティを条件検索できる </li></ul></ul><ul><ul><ul><li>最大 1000 件まで(!) </li></ul></ul></ul><ul><ul><ul><li>通常、 160 ~ 200ms 程度で処理 </li></ul></ul></ul><ul><ul><li>条件の記述方法 </li></ul></ul><ul><ul><ul><li>JDOQL </li></ul></ul></ul><ul><ul><ul><li>GQL </li></ul></ul></ul>Query query = pm.newQuery(&quot;select from Employee &quot; +                               &quot;where lastName == lastNameParam &quot; +                               &quot;order by hireDate desc &quot; +                               &quot;parameters String lastNameParam&quot;)     List<Employee> results = (List<Employee>) query.execute(&quot;Smith&quot;);
  65. 65. クエリ <ul><li>クエリは「インデックス+スキャン」で実装 </li></ul><ul><ul><li>Bigtable はクエリをサポートしていない </li></ul></ul><ul><ul><ul><li>「値」に基づく検索を実行できない </li></ul></ul></ul><ul><ul><li>GAE のアプリが実行するすべてのクエリは、 インデックスとスキャンの組み合わせ で実装される </li></ul></ul><ul><ul><li>クエリを記述すると自動的にインデックスを作成 </li></ul></ul><ul><ul><ul><li>明示的な作成も可能 </li></ul></ul></ul>
  66. 66. クエリ <ul><li>クエリの流れ </li></ul><ul><ul><li>検索/ソート条件に合うインデックスを探す </li></ul></ul><ul><ul><li>インデックスをスキャンし、検索条件に合うエンティティを取得 </li></ul></ul><ul><ul><li>インデックスの終端か1000件までスキャン </li></ul></ul>
  67. 67. クエリ <ul><li>クエリの多用は避けよう </li></ul><ul><ul><li>クエリを多用/誤用するとインデックスが増え、更新処理が遅くなる </li></ul></ul><ul><ul><ul><li>インデックス爆発(index explosion) </li></ul></ul></ul><ul><ul><li>RDBとは違い制約も多い(後述) </li></ul></ul>
  68. 68. インデックス <ul><li>Datastore のインデックスとは </li></ul><ul><ul><li>エンティティテーブルとは別のテーブル </li></ul></ul><ul><ul><li>3 種類のインデックス </li></ul></ul><ul><ul><ul><li>カインドインデックス </li></ul></ul></ul><ul><ul><ul><li>シングルプロパティインデックス </li></ul></ul></ul><ul><ul><ul><li>コンポジットインデックス </li></ul></ul></ul>
  69. 69. カインドインデックス <ul><li>カインド名順でソートされたインデックス </li></ul><ul><ul><li>あるクラスのすべてのエンティティの一覧を提供 </li></ul></ul><ul><ul><li>例: Grandparent でカインドインデックスをスキャン </li></ul></ul><ul><li>SELECT * FROM Grandparent </li></ul>
  70. 70. カインドインデックス <ul><li>カインドインデックス </li></ul>
  71. 71. シングルプロパティインデックス <ul><li>「エンティティの特定のプロパティ値」の順番でソートされたインデックス </li></ul><ul><li>特定のプロパティを条件に検索/ソートできる </li></ul><ul><ul><li>例:name == 'John' </li></ul></ul><ul><ul><li>例:ORDER BY name </li></ul></ul><ul><ul><li>例:age > 10 AND age <= 20 </li></ul></ul>
  72. 72. シングルプロパティインデックス <ul><li>シングルプロパティインデックス </li></ul>
  73. 73. シングルプロパティインデックス <ul><li>昇順用と降順用の2つが作成される </li></ul><ul><ul><li>例:nameプロパティの範囲指定+nameプロパティでソート </li></ul></ul><ul><ul><li>nameプロパティのインデックスでrangeスキャン </li></ul></ul><ul><li>WHERE name >= 'B' AND name < 'C' ORDER BY name </li></ul>
  74. 74. コンポジットインデックス <ul><li>「複数のプロパティ値の組み合わせ」でソートされたインデックス </li></ul><ul><ul><li>index.yaml で明示的に指定して作成 </li></ul></ul><ul><li>複数のプロパティ値で検索できる </li></ul><ul><ul><li>例: lastname = 'Smith' AND firstname >= 'B' AND firstname < 'C' </li></ul></ul><ul><ul><li>インデックスで「 Parent/Smith/B 」から「 Parent/Smith/C 」まで range スキャン </li></ul></ul><ul><ul><li>inequality filter ( < <= > >= )はひとつのプロパティに対してのみ利用できる </li></ul></ul>
  75. 75. コンポジットインデックス <ul><li>コンポジットインデックス </li></ul>
  76. 76. コンポジットインデックス <ul><li>コンポジットインデックスは使うべきか? </li></ul><ul><ul><li>すべてのプロパティ値の順列組み合わせでインデックス内容が作成されるので、インデックスサイズが膨大になりやすい </li></ul></ul>
  77. 77. マージジョイン <ul><li>マージジョイン( merge join )とは </li></ul><ul><ul><li>コンポジットインデックスに頼らずに複数プロパティの条件検索を実現 </li></ul></ul><ul><ul><li>複数のシングルプロパティインデックスをマージしながら検索 </li></ul></ul><ul><ul><li>&quot;zig-zag&quot; アルゴリズムにより、個々のインデックスを並行してスキャン </li></ul></ul>
  78. 78. マージジョイン <ul><li>zig-zag アルゴリズムによるマージジョイン </li></ul>
  79. 79. エンティティグループと トランザクション
  80. 80. CAPとBASE <ul><li>CAP 定理 </li></ul><ul><ul><li>Consistency, Availability, Partition </li></ul></ul><ul><ul><ul><li>クラウド( P )では C と A 間のトレードオフが発生する </li></ul></ul></ul><ul><ul><ul><li>ACID 保証の範囲を制限しないとスケーラビリティが頭打ちに </li></ul></ul></ul><ul><li>BASE トランザクション </li></ul><ul><ul><li>CAP 定理を反映した、クラウドでのトランザクション </li></ul></ul><ul><ul><li>Basically Available </li></ul></ul><ul><ul><ul><li>可用性の高さを優先する(悲観排他より楽観排他) </li></ul></ul></ul><ul><ul><li>Soft-State and Eventually Consistent </li></ul></ul><ul><ul><ul><li>状態の一時的な不整合を許容する </li></ul></ul></ul><ul><ul><ul><li>結果的に整合性が確保される仕組みにしておく </li></ul></ul></ul><ul><ul><ul><li>例: DNS 、 Google Wave の OT </li></ul></ul></ul>
  81. 81. エンティティグループとは <ul><li>エンティティグループとは </li></ul><ul><ul><li>エンティティの階層構造 </li></ul></ul><ul><ul><ul><li>親(parent)と子(children) </li></ul></ul></ul><ul><ul><ul><li>エンティティグループのルートエンティティ(root entity) </li></ul></ul></ul><ul><ul><li>デフォルトでは </li></ul></ul><ul><ul><ul><li>個々のエンティティは個別のエンティティグループを形成 </li></ul></ul></ul>Dept Emp 1 *
  82. 82. エンティティグループとは <ul><li>エンティティグループの指定方法 </li></ul><ul><ul><li>JDO の owned 関係 </li></ul></ul><ul><ul><ul><li>User と Address 間で親子関係を定義 </li></ul></ul></ul><ul><ul><ul><li>unowned 関係はサポートしていない </li></ul></ul></ul><ul><ul><ul><ul><li>エンティティグループが個別になるので ACID を保証できないため </li></ul></ul></ul></ul><ul><ul><li>明示的な指定 </li></ul></ul><ul><ul><ul><li>子のキーを、親のエンティティのキーを使って生成する </li></ul></ul></ul><ul><ul><ul><ul><li>詳しい手順: http://d.hatena.ne.jp/uehaj/20090509/1241856856 </li></ul></ul></ul></ul><ul><ul><li>OOP や RDB の「関連(リレーション)」ではない </li></ul></ul><ul><ul><ul><li>関連をそのまま当てはめると問題も(後述) </li></ul></ul></ul>
  83. 83. エンティティグループとは <ul><li>エンティティグループの2つの役割 </li></ul><ul><ul><li>ローカリティ を指定する </li></ul></ul><ul><ul><ul><li>パフォーマンスの向上 </li></ul></ul></ul><ul><ul><li>トランザクション・スコープ を指定 </li></ul></ul><ul><ul><ul><li>ACIDの保証 </li></ul></ul></ul><ul><ul><li>CAP定理とエンティティグループ </li></ul></ul><ul><ul><ul><li>特定範囲(ローカリティ)のみを対象にACID保証 </li></ul></ul></ul>001 abc 002 def ACID
  84. 84. ローカリティ <ul><li>ローカリティ </li></ul><ul><ul><li>エンティティグループのすべてのエンティティは、1つのサーバーに保存される確率が高い </li></ul></ul><ul><ul><ul><li>より高いパフォーマンスが期待できる </li></ul></ul></ul><ul><ul><ul><ul><li>参考: http://groups.google.com/group/google-appengine-java/browse_thread/thread/fd758c65e14b5c76/e4afc1e348a36a36?show_docid=e4afc1e348a36a36 </li></ul></ul></ul></ul><ul><ul><ul><li>大量のエンティティがある場合は複数サーバーに分割 </li></ul></ul></ul><ul><ul><ul><li>GFSによりデータは他2カ所にバックアップされる </li></ul></ul></ul><ul><ul><ul><li>キー順でソートされている </li></ul></ul></ul>/Grandparent:Alice /Grandparent:Alice/Parent:Sam /Grandparent:Ethel /Grandparent:Ethel/Parent:Jane /Grandparent:Ethel/Parent:Jane/Child:Timmy /Grandparent:Ethel/Parent:Jane/Child:William /Grandparent:Frank
  85. 85. トランザクション・スコープ <ul><li>Datastore のトランザクション・スコープ </li></ul><ul><ul><li>トランザクションの開始( begin )と終了( commit/rollback )を指示する </li></ul></ul><ul><ul><li>エンティティ・グループ </li></ul></ul><ul><li>エンティティグループ 単位で ACID を保証 </li></ul><ul><ul><li>Bigtable は行単位の ACID しか保証しない </li></ul></ul><ul><ul><li>Datastore ではエンティティグループ単位での ACID を保証している </li></ul></ul><ul><ul><ul><li>SERIALIZABLE 相当 </li></ul></ul></ul><ul><ul><li>異なるエンティティグループ間では保証されない </li></ul></ul>
  86. 86. Datastore と BASE <ul><li>楽観的排他制御(optimistic lock)を実装 </li></ul><ul><ul><li>エンティティグループのルートエンティティにて、トランザクションの最終コミット時間のタイムスタンプを記録 </li></ul></ul><ul><ul><li>トランザクションの開始時に同タイムスタンプを確認 </li></ul></ul><ul><ul><li>コミット時にタイムスタンプを再度確認する </li></ul></ul><ul><ul><ul><li>タイムスタンプが変化していなければ、更新内容を保存して、タイムスタンプを更新 </li></ul></ul></ul><ul><ul><ul><li>タイムスタンプが変化してれば、他のトランザクションとの競合が発生しているので、トランザクションをロールバック </li></ul></ul></ul>
  87. 87. Datastore と BASE <ul><li>楽観的排他制御(optimistic lock)を実装 </li></ul><ul><ul><li>RDBの悲観的排他制御(SELECT FOR UPDATE)のようにエンティティをロックしない </li></ul></ul><ul><ul><li>スループットは高いが、競合時のリトライが必要 </li></ul></ul><ul><ul><ul><li>Python版は3回まで自動リトライし、Java版は自動リトライしない </li></ul></ul></ul>
  88. 88. Datastore と BASE <ul><li>リトライの例 </li></ul>        for (int i = 0; i < NUM_RETRIES; i++) {             pm.currentTransaction().begin();             ClubMembers members = pm.getObjectById(ClubMembers.class, &quot;k12345&quot;);             members.incrementCounterBy(1);             try {                 pm.currentTransaction().commit();                 break;             } catch (JDOCanRetryException ex) {                 if (i == (NUM_RETRIES - 1)) {                     throw ex;                 }             }         }
  89. 89. Datastore と BASE <ul><li>分散トランザクションへの対応 </li></ul><ul><ul><li>GAE では異なるエンティティグループ間の分散トランザクション(グローバルトランザクション)はサポートされていない </li></ul></ul><ul><ul><li>ただしアプリレベルでの実装例はある </li></ul></ul><ul><ul><ul><li>http://code.google.com/intl/ja/events/io/sessions/DesignDistributedTransactionLayerAppEngine.html </li></ul></ul></ul><ul><ul><ul><li>http://code.google.com/intl/ja/events/io/sessions/TransactionsAcrossDatacenters.html </li></ul></ul></ul>
  90. 90. GAEトランザクションの注意点 <ul><li>1 TX = 1 エンティティグループ </li></ul><ul><ul><li>1 つのトランザクション内では、 1 つのエンティティグループの更新処理しか実行できない </li></ul></ul><ul><ul><li>複数のエンティティグループを更新する場合は、個別のトランザクションが必要 </li></ul></ul><ul><ul><ul><li>=ルートエンティティの更新は個別 TX が必要 </li></ul></ul></ul><ul><ul><ul><li>@Transactional を使うと便利 </li></ul></ul></ul><ul><ul><ul><ul><li>http://d.hatena.ne.jp/kazunori_279/20090711/1247300915 </li></ul></ul></ul></ul><ul><li>1 つのエンティティの更新は 1 回まで </li></ul><ul><ul><li>1 つのトランザクション内では、 1 つのエンティティを複数回更新できない </li></ul></ul>
  91. 91. GAEトランザクションの注意点 <ul><li>トランザクション内で実行可能なクエリの制限 </li></ul><ul><ul><li>ancestor filter を持つクエリのみ実行可能 </li></ul></ul><ul><ul><li>コミット前の値は読み込みできない </li></ul></ul><ul><ul><ul><li>READ_COMMITTED 相当 </li></ul></ul></ul><ul><ul><ul><li>http://code.google.com/intl/ja/appengine/articles/transaction_isolation.html </li></ul></ul></ul><ul><ul><ul><li>http://groups.google.com/group/google-appengine-java/browse_thread/thread/4a67044929428295 </li></ul></ul></ul>
  92. 92. エンティティグループの注意点 <ul><li>1つのエンティティグループにトランザクション負荷を集中させない </li></ul><ul><ul><li>エンティティグループの利用は必要最小限に抑えた方が性能は向上する </li></ul></ul><ul><ul><li>リレーショナルモデルやオブジェクト指向の関連をそのままあてはめると問題が生じることも </li></ul></ul>Dept Emp 1 *
  93. 93. エンティティグループの注意点 <ul><li>例:1つのDepartmentと1000のEmployee </li></ul><ul><ul><li>1000のEmployeeのうち、いずれか1つのEmployeeしか同時にトランザクションを実行できない </li></ul></ul><ul><ul><li>他はエラーとなりリトライが必要 </li></ul></ul><ul><li>例:1つのUserと数個のAddress </li></ul><ul><ul><li>1人のユーザーの住所に対して複数のトランザクションが同時実行される頻度は低い </li></ul></ul><ul><ul><li>負荷は集中しない </li></ul></ul>
  94. 94. エンティティグループの注意点 <ul><li>連番の採番はどうする? </li></ul><ul><ul><li>例:メッセージの 1 つ 1 つに、書き込み順の連番を振る </li></ul></ul><ul><ul><li>単純な方法:採番用エンティティの MessageIndex をルートエンティティとし、 Message を子とする </li></ul></ul><ul><ul><ul><li>MessageIndex と Message が同じトランザクションで更新処理され、 ACID を確保できる </li></ul></ul></ul><ul><ul><ul><li>しかし大量のメッセージ書き込みには対応できない </li></ul></ul></ul>
  95. 95. エンティティグループの注意点 <ul><li>連番の採番はどうする? </li></ul><ul><ul><li>対処方法:採番用エンティティを分散化する </li></ul></ul><ul><ul><ul><li>ユーザーごとの採番用エンティティ UserIndex をルートエンティティとし、 Message を子とする </li></ul></ul></ul><ul><ul><ul><li>Message の ID には「タイムスタンプ+ユーザー ID + UserIndex で採番した値」を設定する </li></ul></ul></ul><ul><ul><ul><li>UserIndex 単位のトランザクションとなり、大量の書き込みが可能となる </li></ul></ul></ul><ul><ul><ul><li>タイムスタンプ順でソート可能で、かつユニークな ID となる </li></ul></ul></ul>
  96. 96. エンティティグループの注意点 <ul><li>連番の採番はどうする? </li></ul><ul><ul><li>別の対処方法:GUIDベースとする </li></ul></ul><ul><ul><ul><li>「タイムスタンプ+長い乱数」など </li></ul></ul></ul><ul><ul><li>参照:Google I/O 2008 - Building Scalable Web Apps with App Engine </li></ul></ul>
  97. 97. Datastore ってどう使う?
  98. 98. Datastore のパフォーマンス <ul><li>Datastore のパフォーマンスは、エンティティの数とは無関係 </li></ul><ul><ul><li>保存されているエンティティが 1 件でも、 1000 件でも、 1000 万件でも、パフォーマンスに変化はない </li></ul></ul><ul><li>個々のエンティティに対する更新処理のスピード </li></ul><ul><ul><li>平均 100ms 程度 </li></ul></ul><ul><ul><ul><li>http://code.google.com/status/appengine/ </li></ul></ul></ul><ul><ul><li>個々のエンティティの更新処理は遅い </li></ul></ul><ul><ul><li>アプリケーションのパフォーマンスを決めるのは、更新処理の実装方法。参照処理は桁違いに速い </li></ul></ul><ul><ul><ul><li>平均数 10ms 程度 </li></ul></ul></ul>
  99. 99. Datastore のパフォーマンス <ul><li>対応策 </li></ul><ul><ul><li>エンティティグループを分散させる </li></ul></ul><ul><ul><ul><li>エンティティグループが個別ならば、同時に何千でも並列処理できる </li></ul></ul></ul><ul><ul><li>memcache や Task Queue を活用する </li></ul></ul><ul><ul><ul><li>memcache は高速 </li></ul></ul></ul><ul><ul><ul><ul><li>参照・更新ともに 5ms 前後 </li></ul></ul></ul></ul><ul><ul><ul><li>リクエスト処理では Task Queue への登録だけ行い、処理結果は後で表示する </li></ul></ul></ul><ul><ul><ul><li>リクエスト処理では memcache に書き込み、 Task Queue のバックグラウンド処理でエンティティに保存する </li></ul></ul></ul>
  100. 100. Datastore にできないこと <ul><li>テーブル間の join ができない </li></ul><ul><ul><li>非正規化して対処する </li></ul></ul><ul><ul><ul><li>「正規化するな、 JOIN 済みのでっかいテーブルを作れ」 </li></ul></ul></ul><ul><li>select * from PERSON p, ADDRESS a </li></ul><ul><li>where a.person_id = p.id and p.age > 25 and a.country = “US” </li></ul><ul><li>↓ </li></ul><ul><li>select from com.example.Person where age > 25 and country = “US” </li></ul><ul><ul><li>複数のクエリに分割する </li></ul></ul><ul><ul><li>List Property を使う </li></ul></ul>
  101. 101. Datastore にできないこと <ul><li>集約関数がない( group by できない) </li></ul><ul><ul><li>count() で全件カウントできない </li></ul></ul><ul><ul><ul><li>毎回対象データをすべて取得してループで集計するのは非効率(また最大 1000 件の制限がある) </li></ul></ul></ul><ul><ul><ul><li>集約したい値は、集約用のエンティティを用意して集計 </li></ul></ul></ul><ul><ul><ul><ul><li>sharding counter: 書き込みが集中しないように複数のエンティティに分散して書き込みし、後で集計 </li></ul></ul></ul></ul><ul><ul><ul><ul><li>memcache counter: memcache に書き込みし、 Task Queue でエンティティに保存 </li></ul></ul></ul></ul>
  102. 102. Datastore にできないこと <ul><li>集約関数がない( group by できない) </li></ul><ul><ul><li>max()/min() で最大値/最小値を得られない </li></ul></ul><ul><ul><ul><li>対象プロパティで降順/昇順でソートして、 1 件目の値を得る </li></ul></ul></ul>
  103. 103. Datastore にできないこと <ul><li>関数やストアドプロシージャはない </li></ul><ul><ul><li>toUpper/toLower などがない </li></ul></ul><ul><ul><li>別のプロパティを設け、 toUpper 済みの値を入れる </li></ul></ul><ul><ul><li>書き込み時にできるだけ事前処理を行っておくことで、読み込みを高速化できる </li></ul></ul>
  104. 104. Datastore にできないこと <ul><li>クエリの構文の制約 </li></ul><ul><ul><li>全文検索ができない </li></ul></ul><ul><ul><ul><li>LIKEによる部分一致検索はできない </li></ul></ul></ul><ul><ul><ul><li>前方一致なら可能: name >= 'a' AND name <= 'a<UTF-8コードポイントの最大値>' </li></ul></ul></ul><ul><ul><ul><li>検索対象の文字列を形態素解析し、ワードごとのインデックスを作成する </li></ul></ul></ul>
  105. 105. Datastore にできないこと <ul><li>そのほかの制約 </li></ul><ul><ul><li>OR 、 != が使えない </li></ul></ul><ul><ul><li>inequality filters ( < <= >= > )は 1 つのプロパティにのみ利用可能 </li></ul></ul><ul><ul><li>Text 型や Blob 型のプロパティはインデックスを作成できない(クエリできない) </li></ul></ul><ul><ul><li>あるプロパティで inequality filters を使うと、他のプロパティを最優先にしたソートができない </li></ul></ul><ul><li>対策 </li></ul><ul><ul><li>コーディングで対処 </li></ul></ul><ul><ul><li>ド・モルガンの法則で書き換える </li></ul></ul>
  106. 106. そのほかの tips <ul><li>インデックスの更新 </li></ul><ul><ul><li>エンティティの更新処理ではインデックスも更新 </li></ul></ul><ul><ul><ul><li>インデックスは最小限に抑えないと性能が落ちる </li></ul></ul></ul><ul><ul><li>GAE ではクエリの利用を最小限に抑えた方がよい </li></ul></ul><ul><ul><ul><li>「クエリは使ったら負け」 by ひがさん </li></ul></ul></ul>
  107. 107. そのほかの tips <ul><li>インデックス更新の回避方法 </li></ul><ul><ul><li>頻繁に変更されるデータに対し、範囲指定検索するにはどうすればよいか? </li></ul></ul><ul><ul><ul><li>変更のたびにたくさんのindexを更新したくない </li></ul></ul></ul><ul><ul><li>検索用のデータを適宜計算して持たせる </li></ul></ul>
  108. 108. そのほかの tips <ul><li>オークションサイトの例 </li></ul><ul><ul><li>価格帯ごとのオークション一覧を表示したい </li></ul></ul><ul><ul><li>あるオークションの価格が変化したら </li></ul></ul><ul><ul><ul><li>「0~1000円のオークション」のフラグをそのオークションのLPに追加しておく </li></ul></ul></ul><ul><ul><ul><li>範囲指定検索が不要になり、LPに対するequality filterですばやく検索できる </li></ul></ul></ul>
  109. 109. RDB から GAE への移行 <ul><li>プライマリーキーの扱い </li></ul><ul><ul><li>単一カラムの場合はそのまま使える </li></ul></ul><ul><ul><li>複合キーの場合はentityの親子関係に置き換える </li></ul></ul><ul><ul><li>N:N用のマッピングデーブル(ジョインテーブル)はList Propertyに置き換える </li></ul></ul>
  110. 110. RDB から GAE への移行 <ul><li>トランザクションの扱い </li></ul><ul><ul><li>データモデルからエンティティのルートエンティティを見つける </li></ul></ul><ul><ul><ul><li>オンラインサービスでは「ユーザー」がルートに最適な場合が多い </li></ul></ul></ul><ul><ul><li>複数のエンティティグループに対するトランザクションが必要な場合 </li></ul></ul><ul><ul><ul><li>システムの仕様を見直す </li></ul></ul></ul><ul><ul><ul><li>補償トランザクションを実装する </li></ul></ul></ul><ul><ul><ul><ul><li>2つめのトランザクションが失敗した場合は、最初のトランザクションをキャンセルする、など </li></ul></ul></ul></ul>

×