Datastoreに格納されたEntityの更新について

Datastoreに格納されたEntityの更新について – おきらく日記

Datastoreに格納されたEntityの更新について、興味深い記事を発見。

MyModelMeta meta = MyModelMeta.get();
MyModel model = Datastore.query(meta)
.filter(meta.field.equal("value"))
.asSingle();
if(model != null) {
model.setField("newValue");
Datastore.put(model);
}

これは、ダメだという事。どうするかというと、Keyでgetして、値を変えて、putする。

MyModel model = Datastore.get(MyModel.class, key.get(0));
model.setField("newValue");
Datastore.put(model);

queryで取得したEntityをなぜputしていけないのか、なかなか分からなかった。「検索して取得したものと実際に格納されてるものって別だもの」ってどういう事・・?

調べたところ、トランザクション内でqueryは使えないんじゃないかという結果にたどり着いた。

トランザクション – Google App Engine – Google Code

アプリケーションは、トランザクション実行中にクエリを実行できません。しかし、トランザクション中にキーを使用してデータストア エンティティを取得することは可能です。このとき、フェッチされたエンティティは他のトランザクションの内容と一致していることを保証します。トランザクション実行前にキーを準備するか、キー名または ID を使用してトランザクション内にキーを構築できます。

appengine java night #3に行ってきた。 #appengine #ajn3 – あおうさ@日記

transaction 開始後最初のget/putで そのときのタイムスタンプをどこかに取っておく.

commit のときに取っておいたタイムスタンプと同じかどうかを比較する.

ancestor query 以外のqueryではtransactionがかからない.keyに対するgetだとだいtransactionになる.

queryは本来複数の行を相手にしているので,そもそもどのentityグループにぞくしたqueryなのか定義できないから.

ancestor queryは entity groupに対するqueryなので,定義できるから.

Entityを更新する時は、keyでgetしてputする!

カテゴリー: GAE

BigTableの設計指針

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

カテゴリー: GAE

正規化するな、JOIN済みのでっかいテーブルを作れ

BigTableの設計に悩んでいる。joinによる検索が出来ない。

対策としては以下のものがあるようだ。

  • 非正規化する
  • クエリを複数回発行する
  • ListPropertyを使用(StringListProperty)

ただ、非正規化した場合、親テーブルの一覧取得はどうするんだろう。distinctはないようだし。全件取得してアプリで処理?よく分からん。集約用テーブルを用意か!?

via

カテゴリー: GAE