Datastoreのtips
Datastoreのパフォーマンス
- Datastoreのパフォーマンスは、エンティティの数とは無関係
- 保存されているエンティティが1件でも、1000件でも、1000万件でも、パフォーマンスに変化はない
- Datastore performance doesn't depend on how many entities you have
- 個々のエンティティに対する更新処理のスピードは、1〜10回/秒程度
- 個々のエンティティの更新処理は遅い
- アプリケーションのパフォーマンスを決めるのは、更新処理の実装方法。参照処理は桁違いに速い
- low-level APIを使えば少し速くなるが、ドキュメントがあまりない
Datastoreにできないことと対応策
- テーブル間のjoinができない
- 非正規化して対処する
select * from PERSON p, ADDRESS a where a.person_id = p.id and p.age > 25 and a.country = “US” ↓ select from com.example.Person where age > 25 and country = “US”
-
- 複数のクエリに分割する
- List Propertyを使う
- 集約関数がない(group byできない)
- count()で全件カウントできない
- 毎回対象データをすべて取得してループで集計するのは非効率(また最大1000件の制限がある)
- 集約したい値は、集約用のエンティティを用意して集計
- sharding counter: 書き込みが集中しないように複数のエンティティに分散して書き込みし、後で集計
- memcache counter: memcacheに書き込みし、Task Queueでエンティティに保存
- max()/min()で最大値/最小値を得られない
- 対象プロパティで降順/昇順でソートして、1件目の値を得る
- count()で全件カウントできない
- 関数やストアドプロシージャはない
- toUpper/toLowerなどがない
- 別のプロパティを設け、toUpper済みの値を入れておく
- 書き込み時にできるだけ事前処理を行っておくことで、読み込みを高速化できる
- クエリの構文の制約
- 全文検索ができない
- LIKEによる部分一致検索はできない
- 前方一致なら可能: name >= 'a' AND name <= 'a<UTF-8コードポイントの最大値>'
- 検索対象の文字列を形態素解析し、ワードごとのインデックスを作成する
- Simple Full Text Search for App Engine (python)
- そのほかの制約(OR、!=など)
- メモリ上でフィルタするなどして対処
- 全文検索ができない
そのほかのtips
- インデックスの更新
- エンティティの更新処理ではインデックスも更新されるので、インデックスは最小限に抑えないと性能が落ちる
- GAEではクエリの利用を最小限に抑えた方がよい
- インデックス更新の回避方法
- 頻繁に変更されるデータに対し、範囲指定検索するにはどうすればよいか? 変更のたびにたくさんのindexを更新したくない。
- 検索用のデータを適宜計算して持たせる。
- オークションサイトの例:価格帯ごとのオークション一覧を表示したい。あるオークションの価格が変化したら、「0〜1000円のオークション」のフラグをそのオークションのLPに追加しておく。範囲指定検索が不要になり、LPに対するequality filterですばやく検索できる
- 以下のクエリの例では、N+1検索の問題が起きないか?
indexes = db.GqlQuery("SELECT __key__ FROM MessageIndex WHERE receivers = :1", me) keys = [k.parent() for k in indexes] messages = db.get(keys)
-
- 起きない。indexesを取得する際には1回のクエリが走るが、messagesの取得は個々のキーからentityをハッシュテーブルで直接取得する。個別のクエリは実行されず、高速に処理される。
- (でも個別にディスクアクセスしないか?)
- 起きない。indexesを取得する際には1回のクエリが走るが、messagesの取得は個々のキーからentityをハッシュテーブルで直接取得する。個別のクエリは実行されず、高速に処理される。
- そのほか
- データの物理的(地理的)位置はコントロールできない