BigTableの設計に悩んでいる。どういう制約があって、何をしないといけないのか混乱してきた。

id:bluerabbitさんのページが、すごくまとまっているので、参考にさせてもらおう。

* 正規化しない。RDBMSでいうところのJOIN済みのでっかいテーブル作れ。

* SELECTはがんばらない。INSERT超がんばれ。

* PKはString型にしてgae.encoded-pkを使用する。

* PKに複合キーは使わない。(コンポジットインデックス使わない)

* Date型のプロパティは使わない。String型にする。

* プリミティブ型はnullを許容しないので使わない

* テーブル間のjoinができない。→正規化せずJOIN済みのでっかいテーブルを作る

* 集約関数がない(group byできない)、count()で全件カウントできない →集約したい値は、集約用のエンティティを用意してInsert時に集計値を保存する(最大値/最小値も同様)

o 毎回対象データをすべて取得してループで集計するのは非効率(また最大1000件の制限がある)集約したい値は、集約用のエンティティを用意して集計

+ sharding counter: 書き込みが集中しないように複数のエンティティに分散して書き込みし、後で集計

+ memcache counter: memcacheに書き込みし、Task Queueでエンティティに保存

* toUpper/toLowerなどがない。→別のプロパティを設け、toUpper済みの値を入れておく

* LIKEによる部分一致検索はできない。→部分一致が必要であれば専用のプロパティを設けてInsert時に値を保存しておく

* 範囲検索は範囲値をプロパティで保持しておく

o オークションサイトの例:価格帯ごとのオークション一覧を表示したい。あるオークションの価格が変化したら、「0~1000円のオークション」のフラグをそのオークションのLPに追加しておく。範囲指定検索が不要になり、LPに対するequality filterですばやく検索できる

* ORや!=の検索はできない。→Javaでフィルタリングする

* Whereにはイコール検索だけにする

* Where句と異なるキーでORDER BYは行えない。→Javaでソートする

* ORDER BYは使わない。Javaでソートする。(whereがないときだけORDER BY使ってもよし)

* 複数のエンティティグループにまたがるトランザクション処理を行う場合は、補償トランザクションを実装する。

* 排他制御は@Version(strategy = VersionStrategy.VERSION_NUMBER)を使用する

* 個々のエンティティに対する更新処理のスピードは、1~10回/秒程度。アプリケーションのパフォーマンスを決めるのは、更新処理の実装方法。参照処理は桁違いに速い。→エンティティグループ使用しない。エンティティグループが別の場合はパラレルで処理される。その代わり自前で補償トランザクションを実装する。

o なんだかエンティティグループいらない気がしてきた。

* MailAPIを使う時は直接メール送信は行わない。キュー方式にする。メールを送信したい時は、Mailテーブルを作成する。SendMail用のCronを作成する。SendMailのcronで実際のメール送信を行う。メール送信ログも残るし設計として実装が切り離されて楽。

他にもTips。

  • [テクニック1]Memcacheを使用する
  • [テクニック2]非正規化を積極的に行う
  • [テクニック3]親子関係はListPropertyを使用
  • [テクニック4]エンティティ・グループは小さく
  • [テクニック5]更新処理の分散を図る
  • [テクニック6]事前に計算しておく

via