Takashi Matsuo
Google, Inc.
Feb 18, 2011
App Engine
                 to infinity and beyond




Takashi Matsuo
Google, Inc.
Feb 18, 2011
自己紹介

松尾貴史 @tmatsuo

  App Engine Developer Advocate
  Kay's Daddy
  http://code.google.com/p/kay-framework/
> 10万 Developers / Month

   > 15万 Apps / Week

 > 10億 Page Views / Day
App Engine のこれまで 3 年 - 進化を続けるプラットフォーム
      Apr 2008   Python launch
      May 2008   Memcache API, Images API
      Jul 2008   Logs export
      Aug 2008   Batch write/delete
      Oct 2008   HTTPS support
      Dec 2008   Status dashboard, quota details
      Feb 2009   Billing, Remote API, Larger HTTP request/response size limits (10MB)
      Apr 2009   Java launch, Bulkloader (DB import), Cron jobs, SDC
      May 2009   Key-only queries, Quota API
      Jun 2009   Task queue API, Django 1.0 support
      Sep 2009   XMPP API, Remote API shell, Django 1.1 support
      Oct 2009   Incoming email
      Dec 2009   Blobstore API
      Feb 2010   Datastore cursors, Async URLfetch, App stats
      Mar 2010   Denial-of-Service filtering, eventual consistency support
      May 2010   OpenID, OAuth, App Engine for Business, new bulkloader
      Aug 2010   Namespaces, increased quotas, high perf image serving
      Oct 2010   Instances console, datastore admin & bulk entity deletes
      Dec 2010   Channel API, 10-minute tasks & cron jobs, AlwaysOn & Warmup
      Jan 2011   High Replication datastore, entity copy b/w apps, 10-minute URLfetch
      Feb 2011   Improved XMPP and Task Queue, Django 1.2 support
ロードマップ

 SSL access on non-appspot.com domains
 Full-text Search over Datastore
 Support for Python 2.7
 Background servers capable of running for longer than 30s
 Support for running MapReduce jobs across App Engine datasets
 Bulk Datastore Import and Export tool
 Improved monitoring and alerting of application serving
 Logging system improvements to remove limits on size and storage
 Raise HTTP request and response size limits
 Integration with Google Storage for Developers
 Programmatic Blob creation in Blobstore
 Quota and presence improvements for Channel API
日本での事例

 たくさんのミクシィアプリ
   the Actress - 60万ユーザー
   Mixi Xmas 2010 - 200万ユーザー
 朝日新聞 - メディア配信サービス
 ソニー - Chan-Toru
 業務用アプリケーションも多数
App Engine for Business とは?

App Engine プラットフォームに加え

  99.9% SLA
  クラウド SQL
  有償サポート
  ドメインコンソール
  Hosted SSL
本あります

プログラミング Google App Engine
http://www.oreilly.co.jp/books/9784873114750/




オープンソース徹底活用 Slim3 on Google App Engine for Java
ISBN-10: 4798026999
アジェンダ

App Engine で開発する際の注意点

  Datastore の設計
  最小限の仕事をする
  Datastore Contention を避ける
     シャーディングカウンター
     Fork-join queue
  Memcache を効果的に使用する
  さらなる高みへ
Datastore の設計

  非正規化 を嫌がらない - No join
  必要な index のみ作成
  最小限の Entity Group
     トランザクションが必要な箇所のみ
  可能なら速い方を使う
     query より keys_only query が速い
     query より get が速い
     複数の get より batch get が速い
  kind の分割が有効なケース
     一部のプロパティだけ取得
  少しの失敗を受け入れる
     リトライ、deadline の指定
Datastore の設計

  非正規化 を嫌がらない - No join
  必要な index のみ作成
  最小限の Entity Group
     トランザクションが必要な箇所のみ
  可能なら速い方を使う
     query より keys_only query が速い
     query より get が速い
     複数の get より batch get が速い
  kind の分割が有効なケース
     一部のプロパティだけ取得
  少しの失敗を受け入れる
     リトライ、deadline の指定
Datastore の設計 - 非正規化
Before
from google.appengine.ext import db

class User(db.Model):
 name = db.StringProperty()
 groups = db.ListProperty(db.Key)

class Group(db.Model):
 name = db.StringProperty()

user = User.get(user_key)
group_names = [group.name for group in db.get(user.groups)]
Datastore の設計 - 非正規化
After
from google.appengine.ext import db

class User(db.Model):
 name = db.StringProperty()
 groups = db.ListProperty(db.Key)
 group_names = db.StringListProperty()

class Group(db.Model):
 name = db.StringProperty()

user = User.get(user_key)
# group_names = user.group_names
Datastore の設計

  非正規化 を嫌がらない - No join
  必要な index のみ作成
  最小限の Entity Group
     トランザクションが必要な箇所のみ
  可能なら速い方を使う
     query より keys_only query が速い
     query より get が速い
     複数の get より batch get が速い
  kind の分割が有効なケース
     一部のプロパティだけ取得
  少しの失敗を受け入れる
     リトライ、deadline の指定
Datastore の設計 - 必要な index のみ作成
from google.appengine.ext import db

class MyModel(db.Model):
 name = db.StringProperty()
 total = db.IntegerProperty(indexed=False)




 index が増えると書き込み速度は遅くなります
Datastore の設計

  非正規化 を嫌がらない - No join
  必要な index のみ作成
  最小限の Entity Group
     トランザクションが必要な箇所のみ
  可能なら速い方を使う
     query より keys_only query が速い
     query より get が速い
     複数の get より batch get が速い
  kind の分割が有効なケース
     一部のプロパティだけ取得
  少しの失敗を受け入れる
     リトライ、deadline の指定
Datastore の設計 - Entity Group
Entity Group 作成の方法
class MyModel(db.Model):
 # ...

# 単純にエンティティを作成すると、それ自体新しい Entity Group になる

my_entity = MyModel()
my_entity.put()

# 親を指定すると、親と同じ Entity Group に属する

my_second_entity = MyModel(parent=my_entity)
my_second_entity.put()

# 同じ kind である必要はない

my_third_entity = MyOtherModel(parent=my_second_entity)
my_third_entity.put()
Datastore の設計 - Entity Group
Entity Group 作成の方法
class MyModel(db.Model):
 # ...

# 単純にエンティティを作成すると、それ自体新しい Entity Group になる

my_entity = MyModel()
my_entity.put()

# 親を指定すると、親と同じ Entity Group に属する

my_second_entity = MyModel(parent=my_entity)
my_second_entity.put()

# 同じ kind である必要はない

my_third_entity = MyOtherModel(parent=my_second_entity)
my_third_entity.put()
Datastore の設計 - Entity Group
  親子関係全てに Entity Group を使用するべきではない
    例えば BlogEntry と Comment には不適切
  基本的にはトランザクションが必要な箇所に使う
  検索用インデックスを自前で作る時などにも有効
Datastore の設計 - Entity Group の使用例
検索用インデックス - Before

class Sentence(db.Model):
 body = db.TextProperty()
 indexes = db.StringListProperty()

query = Sentence.all().filter(
  "indexes =", search_word
)
search_result = query.fetch(20)

fetch 時に不必要な indexes のデシリアライズが発生
Datastore の設計 - Entity Group の使用例
検索用インデックス - After
class Sentence(db.Model):
 body = db.TextProperty()

class SearchIndex(db.Model):
 indexes = db.StringListProperty()

query = SearchIndex.all(keys_only=True).
 filter("indexes =", search_word)
search_result = db.get(
 [key.parent() for key in query.fetch(20)])

保存時に Entity Group を形成
Datastore の設計

  非正規化 を嫌がらない - No join
  必要な index のみ作成
  最小限の Entity Group
     トランザクションが必要な箇所のみ
  可能なら速い方を使う
     query より keys_only query が速い
     query より get が速い
     複数の get より batch get が速い
  kind の分割が有効なケース
     一部のプロパティだけ取得
  少しの失敗を受け入れる
     リトライ、deadline の指定
Datastore の設計

  非正規化 を嫌がらない - No join
  必要な index のみ作成
  最小限の Entity Group
     トランザクションが必要な箇所のみ
  可能なら速い方を使う
     query より keys_only query が速い
     query より get が速い
     複数の get より batch get が速い
  kind の分割が有効なケース
     一部のプロパティだけ取得
  少しの失敗を受け入れる
     リトライ、deadline の指定
Datastore の設計 - Entity 分割

例: ファイルシェアリングサービス
   ファイル自身
   メタデータ

  ファイルの一覧ページで必要なのはメタデータだけ
  ファイル自身とメタデータを別エンティティに分割
  一覧ページでの処理が高速で無駄がなくなる
Datastore の設計

  非正規化 を嫌がらない - No join
  必要な index のみ作成
  最小限の Entity Group
     トランザクションが必要な箇所のみ
  可能なら速い方を使う
     query より keys_only query が速い
     query より get が速い
     複数の get より batch get が速い
  kind の分割が有効なケース
     一部のプロパティだけ取得
  少しの失敗を受け入れる
     リトライ、deadline の指定
Datastore の設計 - リトライ、deadline

from google.appengine.ext import db
from google.appengine.runtime.apiproxy_errors import DeadlineExceededError
from google.appengine.runtime import DeadlineExceededError as D

def somefunction(somevalue):
 handmaid_key = db.Key.from_path("MyModel", 1)
 new_id = db.allocate_ids(handmaid_key, 1)
 new_key = db.Key.from_path("MyModel", new_id)
 my_entity = MyModel(key=new_key, name=somevalue)
 try:
   while True:
   sleep_ms = 100
   try:
     db.put(my_entity, config=db.create_config(deadline=5))
     break
   except (DeadlineExceededError, db.Timeout):
     time.sleep(sleep_ms)
     sleep_ms *= 2
 except D:
   # Ran out of retry. Return an error to users.




自動リトライのレシピ
最小限の仕事をする

大事な用語
 ユーザー向けリクエスト(User Facing Request)
   インターネット側から来るもの全て
 バックグラウンドリクエスト(Background Request)
   TaskQueue, Cron 等

大事なポイント
ユーザー向けリクエストは 1000 ms 以内で返すこと
最小限の仕事をする - より速い方を使用

 query より keys_only query
 query より get
 datastore より memcache
 memcache より global 変数(static 変数)
最小限の仕事をする - Task Queue を使用

  時間のかかる処理は Task Queue に投げる。
  Channel API を使えば結果を簡単にプッシュ
Datastore Contention をさける

Entity または Entity Group に対しての書き込み

目安:  1 秒に 1 度程度
対策:
 Entity Group はなるべく小さくする
 シャーディングカウンターなどのテクニックを使用する
シャーディングカウンター - シンプルな実装例
from google.appengine.ext import db
import random                                                    http://goo.gl/8dGO
class SimpleCounterShard(db.Model):
   """Shards for the counter"""
   count = db.IntegerProperty(required=True, default=0)

NUM_SHARDS = 20

def get_count():
  """Retrieve the value for a given sharded counter."""
  total = 0
  for counter in SimpleCounterShard.all():
     total += counter.count
  return total

def increment():
  """Increment the value for a given sharded counter."""
  def txn():
      index = random.randint(0, NUM_SHARDS - 1)
      shard_name = "shard" + str(index)
      counter = SimpleCounterShard.get_by_key_name(shard_name)
      if counter is None:
          counter = SimpleCounterShard(key_name=shard_name)
      counter.count += 1
      counter.put()
  db.run_in_transaction(txn)
Fork-join queue

Building high-throughput data pipelines with Google App Engine
http://goo.gl/ntlH

シンプルなカウンターの例
http://paste.shehas.net/show/137/
memcache を効果的に使用する

使用例
 人気のあるページを html 丸ごとキャッシュ
 memcache.incr を使用したカウンター
   高速だが 100% 正確とは保証できない
 頻繁に使用される entity や query 結果をキャッシュ
memcache を効果的に使用する

基本パターン

results = memcache.get('results')
if results is None:
  results = some_complex_work()
  memcache.set('results', results)

# use results

古いキャッシュを破棄する
AppStats - パフォーマンス測定用ツール
Before




After




http://goo.gl/eYdhD
さらなる高みへ

 Request 数の Fixed Quota はデフォルト 500 QPS 程度
    それ以上必要な場合は、Quota Increase のフォームから
    申請するか私宛に相談してください
 Tablet Server の Split を抑制
    頻繁に(数百/秒) Entity の作成などを行う場合
        自動採番の id では Split を引き起こしやすい
            uuid などを key_name として使用する
        key_name を使用する場合でも、頭文字が類似だと
        Split が起きやすい
            Hash 値を前置する
まとめ

 Datastore に適した設計
 最小限の仕事をする
 Datastore Contention を避ける
 Memcache を効果的に使用する
 AppStats を使用してパフォーマンス測定する
 Fixed Quota を増やす
 Tablet Server の Split を抑制
無限の彼方へ
Takashi Matsuo
Google, Inc.
e-mail: tmatsuo@google.com
twitter: @tmatsuo

【18-C-4】Google App Engine - 無限の彼方へ

  • 1.
  • 2.
    App Engine to infinity and beyond Takashi Matsuo Google, Inc. Feb 18, 2011
  • 3.
    自己紹介 松尾貴史 @tmatsuo App Engine Developer Advocate Kay's Daddy http://code.google.com/p/kay-framework/
  • 4.
    > 10万 Developers/ Month > 15万 Apps / Week > 10億 Page Views / Day
  • 5.
    App Engine のこれまで3 年 - 進化を続けるプラットフォーム Apr 2008 Python launch May 2008 Memcache API, Images API Jul 2008 Logs export Aug 2008 Batch write/delete Oct 2008 HTTPS support Dec 2008 Status dashboard, quota details Feb 2009 Billing, Remote API, Larger HTTP request/response size limits (10MB) Apr 2009 Java launch, Bulkloader (DB import), Cron jobs, SDC May 2009 Key-only queries, Quota API Jun 2009 Task queue API, Django 1.0 support Sep 2009 XMPP API, Remote API shell, Django 1.1 support Oct 2009 Incoming email Dec 2009 Blobstore API Feb 2010 Datastore cursors, Async URLfetch, App stats Mar 2010 Denial-of-Service filtering, eventual consistency support May 2010 OpenID, OAuth, App Engine for Business, new bulkloader Aug 2010 Namespaces, increased quotas, high perf image serving Oct 2010 Instances console, datastore admin & bulk entity deletes Dec 2010 Channel API, 10-minute tasks & cron jobs, AlwaysOn & Warmup Jan 2011 High Replication datastore, entity copy b/w apps, 10-minute URLfetch Feb 2011 Improved XMPP and Task Queue, Django 1.2 support
  • 6.
    ロードマップ SSL accesson non-appspot.com domains Full-text Search over Datastore Support for Python 2.7 Background servers capable of running for longer than 30s Support for running MapReduce jobs across App Engine datasets Bulk Datastore Import and Export tool Improved monitoring and alerting of application serving Logging system improvements to remove limits on size and storage Raise HTTP request and response size limits Integration with Google Storage for Developers Programmatic Blob creation in Blobstore Quota and presence improvements for Channel API
  • 7.
    日本での事例 たくさんのミクシィアプリ the Actress - 60万ユーザー Mixi Xmas 2010 - 200万ユーザー 朝日新聞 - メディア配信サービス ソニー - Chan-Toru 業務用アプリケーションも多数
  • 8.
    App Engine forBusiness とは? App Engine プラットフォームに加え 99.9% SLA クラウド SQL 有償サポート ドメインコンソール Hosted SSL
  • 9.
    本あります プログラミング Google AppEngine http://www.oreilly.co.jp/books/9784873114750/ オープンソース徹底活用 Slim3 on Google App Engine for Java ISBN-10: 4798026999
  • 10.
    アジェンダ App Engine で開発する際の注意点 Datastore の設計 最小限の仕事をする Datastore Contention を避ける シャーディングカウンター Fork-join queue Memcache を効果的に使用する さらなる高みへ
  • 11.
    Datastore の設計 非正規化 を嫌がらない - No join 必要な index のみ作成 最小限の Entity Group トランザクションが必要な箇所のみ 可能なら速い方を使う query より keys_only query が速い query より get が速い 複数の get より batch get が速い kind の分割が有効なケース 一部のプロパティだけ取得 少しの失敗を受け入れる リトライ、deadline の指定
  • 12.
    Datastore の設計 非正規化 を嫌がらない - No join 必要な index のみ作成 最小限の Entity Group トランザクションが必要な箇所のみ 可能なら速い方を使う query より keys_only query が速い query より get が速い 複数の get より batch get が速い kind の分割が有効なケース 一部のプロパティだけ取得 少しの失敗を受け入れる リトライ、deadline の指定
  • 13.
    Datastore の設計 -非正規化 Before from google.appengine.ext import db class User(db.Model): name = db.StringProperty() groups = db.ListProperty(db.Key) class Group(db.Model): name = db.StringProperty() user = User.get(user_key) group_names = [group.name for group in db.get(user.groups)]
  • 14.
    Datastore の設計 -非正規化 After from google.appengine.ext import db class User(db.Model): name = db.StringProperty() groups = db.ListProperty(db.Key) group_names = db.StringListProperty() class Group(db.Model): name = db.StringProperty() user = User.get(user_key) # group_names = user.group_names
  • 15.
    Datastore の設計 非正規化 を嫌がらない - No join 必要な index のみ作成 最小限の Entity Group トランザクションが必要な箇所のみ 可能なら速い方を使う query より keys_only query が速い query より get が速い 複数の get より batch get が速い kind の分割が有効なケース 一部のプロパティだけ取得 少しの失敗を受け入れる リトライ、deadline の指定
  • 16.
    Datastore の設計 -必要な index のみ作成 from google.appengine.ext import db class MyModel(db.Model): name = db.StringProperty() total = db.IntegerProperty(indexed=False) index が増えると書き込み速度は遅くなります
  • 17.
    Datastore の設計 非正規化 を嫌がらない - No join 必要な index のみ作成 最小限の Entity Group トランザクションが必要な箇所のみ 可能なら速い方を使う query より keys_only query が速い query より get が速い 複数の get より batch get が速い kind の分割が有効なケース 一部のプロパティだけ取得 少しの失敗を受け入れる リトライ、deadline の指定
  • 18.
    Datastore の設計 -Entity Group Entity Group 作成の方法 class MyModel(db.Model): # ... # 単純にエンティティを作成すると、それ自体新しい Entity Group になる my_entity = MyModel() my_entity.put() # 親を指定すると、親と同じ Entity Group に属する my_second_entity = MyModel(parent=my_entity) my_second_entity.put() # 同じ kind である必要はない my_third_entity = MyOtherModel(parent=my_second_entity) my_third_entity.put()
  • 19.
    Datastore の設計 -Entity Group Entity Group 作成の方法 class MyModel(db.Model): # ... # 単純にエンティティを作成すると、それ自体新しい Entity Group になる my_entity = MyModel() my_entity.put() # 親を指定すると、親と同じ Entity Group に属する my_second_entity = MyModel(parent=my_entity) my_second_entity.put() # 同じ kind である必要はない my_third_entity = MyOtherModel(parent=my_second_entity) my_third_entity.put()
  • 20.
    Datastore の設計 -Entity Group 親子関係全てに Entity Group を使用するべきではない 例えば BlogEntry と Comment には不適切 基本的にはトランザクションが必要な箇所に使う 検索用インデックスを自前で作る時などにも有効
  • 21.
    Datastore の設計 -Entity Group の使用例 検索用インデックス - Before class Sentence(db.Model): body = db.TextProperty() indexes = db.StringListProperty() query = Sentence.all().filter( "indexes =", search_word ) search_result = query.fetch(20) fetch 時に不必要な indexes のデシリアライズが発生
  • 22.
    Datastore の設計 -Entity Group の使用例 検索用インデックス - After class Sentence(db.Model): body = db.TextProperty() class SearchIndex(db.Model): indexes = db.StringListProperty() query = SearchIndex.all(keys_only=True). filter("indexes =", search_word) search_result = db.get( [key.parent() for key in query.fetch(20)]) 保存時に Entity Group を形成
  • 23.
    Datastore の設計 非正規化 を嫌がらない - No join 必要な index のみ作成 最小限の Entity Group トランザクションが必要な箇所のみ 可能なら速い方を使う query より keys_only query が速い query より get が速い 複数の get より batch get が速い kind の分割が有効なケース 一部のプロパティだけ取得 少しの失敗を受け入れる リトライ、deadline の指定
  • 24.
    Datastore の設計 非正規化 を嫌がらない - No join 必要な index のみ作成 最小限の Entity Group トランザクションが必要な箇所のみ 可能なら速い方を使う query より keys_only query が速い query より get が速い 複数の get より batch get が速い kind の分割が有効なケース 一部のプロパティだけ取得 少しの失敗を受け入れる リトライ、deadline の指定
  • 25.
    Datastore の設計 - Entity分割 例: ファイルシェアリングサービス ファイル自身 メタデータ ファイルの一覧ページで必要なのはメタデータだけ ファイル自身とメタデータを別エンティティに分割 一覧ページでの処理が高速で無駄がなくなる
  • 26.
    Datastore の設計 非正規化 を嫌がらない - No join 必要な index のみ作成 最小限の Entity Group トランザクションが必要な箇所のみ 可能なら速い方を使う query より keys_only query が速い query より get が速い 複数の get より batch get が速い kind の分割が有効なケース 一部のプロパティだけ取得 少しの失敗を受け入れる リトライ、deadline の指定
  • 27.
    Datastore の設計 -リトライ、deadline from google.appengine.ext import db from google.appengine.runtime.apiproxy_errors import DeadlineExceededError from google.appengine.runtime import DeadlineExceededError as D def somefunction(somevalue): handmaid_key = db.Key.from_path("MyModel", 1) new_id = db.allocate_ids(handmaid_key, 1) new_key = db.Key.from_path("MyModel", new_id) my_entity = MyModel(key=new_key, name=somevalue) try: while True: sleep_ms = 100 try: db.put(my_entity, config=db.create_config(deadline=5)) break except (DeadlineExceededError, db.Timeout): time.sleep(sleep_ms) sleep_ms *= 2 except D: # Ran out of retry. Return an error to users. 自動リトライのレシピ
  • 28.
    最小限の仕事をする 大事な用語 ユーザー向けリクエスト(User FacingRequest) インターネット側から来るもの全て バックグラウンドリクエスト(Background Request) TaskQueue, Cron 等 大事なポイント ユーザー向けリクエストは 1000 ms 以内で返すこと
  • 29.
    最小限の仕事をする - より速い方を使用 queryより keys_only query query より get datastore より memcache memcache より global 変数(static 変数)
  • 30.
    最小限の仕事をする - Task Queueを使用 時間のかかる処理は Task Queue に投げる。 Channel API を使えば結果を簡単にプッシュ
  • 31.
    Datastore Contention をさける Entityまたは Entity Group に対しての書き込み 目安:  1 秒に 1 度程度 対策: Entity Group はなるべく小さくする シャーディングカウンターなどのテクニックを使用する
  • 32.
    シャーディングカウンター - シンプルな実装例 fromgoogle.appengine.ext import db import random http://goo.gl/8dGO class SimpleCounterShard(db.Model): """Shards for the counter""" count = db.IntegerProperty(required=True, default=0) NUM_SHARDS = 20 def get_count(): """Retrieve the value for a given sharded counter.""" total = 0 for counter in SimpleCounterShard.all(): total += counter.count return total def increment(): """Increment the value for a given sharded counter.""" def txn(): index = random.randint(0, NUM_SHARDS - 1) shard_name = "shard" + str(index) counter = SimpleCounterShard.get_by_key_name(shard_name) if counter is None: counter = SimpleCounterShard(key_name=shard_name) counter.count += 1 counter.put() db.run_in_transaction(txn)
  • 33.
    Fork-join queue Building high-throughputdata pipelines with Google App Engine http://goo.gl/ntlH シンプルなカウンターの例 http://paste.shehas.net/show/137/
  • 34.
    memcache を効果的に使用する 使用例 人気のあるページをhtml 丸ごとキャッシュ memcache.incr を使用したカウンター 高速だが 100% 正確とは保証できない 頻繁に使用される entity や query 結果をキャッシュ
  • 35.
    memcache を効果的に使用する 基本パターン results =memcache.get('results') if results is None: results = some_complex_work() memcache.set('results', results) # use results 古いキャッシュを破棄する
  • 36.
  • 37.
    さらなる高みへ Request 数のFixed Quota はデフォルト 500 QPS 程度 それ以上必要な場合は、Quota Increase のフォームから 申請するか私宛に相談してください Tablet Server の Split を抑制 頻繁に(数百/秒) Entity の作成などを行う場合 自動採番の id では Split を引き起こしやすい uuid などを key_name として使用する key_name を使用する場合でも、頭文字が類似だと Split が起きやすい Hash 値を前置する
  • 38.
    まとめ Datastore に適した設計 最小限の仕事をする Datastore Contention を避ける Memcache を効果的に使用する AppStats を使用してパフォーマンス測定する Fixed Quota を増やす Tablet Server の Split を抑制
  • 39.
  • 40.
    Takashi Matsuo Google, Inc. e-mail:tmatsuo@google.com twitter: @tmatsuo