由於這一陣子用 app engine 寫了一些玩具跟 myaudiocast, 所以來記錄一下心得。雖然 app engine 不用錢, 似乎不用怕資料庫 scale 的問題, 可是比起一般使用關聯式資料庫來說, 開始 run 之前得更仔細規劃, 因為有些資料在寫入後就無法更動了。
上面這張圖是 myaudiocast 簡單的示意圖, 接下來就用這張圖來說一下關於 datastore 使用上的心得
單筆資料 ( one-to-one )
像是 User 對 Podcast 在我的規劃上是 one-to-one, 所以如果是 one-to-one 這類的資料就給定 key_name, 直接用 key_name 來抓單筆資料, 而不是先用 filter 之後在用 get 來取得單筆資料。 因為用 appstats 就發現後面這個方法常常是花最多時間的, 然而因為之前的資料都沒給 key_name 現在也來不及了 …
多筆資料 ( one-to-many, many-to-many )
像是 Episode 這類 one-to-many, 會有多筆的資料, 沒事就不用給 key_name 了, 因為通常都是透過 filter 來取出多筆資料, 所以只需要寫入時指定 parent, 讓 user 的 episodes 可以成為一個 entity group, 這樣抓取資料比較快。
盡量使用 memcache
datastore 其實並沒有很快, 甚至 model api 沒有 query cache, 而 memcache 的 quota 其實滿大的, 所以要盡量用 memcache。
避免在 template 透過關連取資料
像是 podcast 跟 episodes 都可以透過關連, 像是 user.podcast 跟 user.episodes 來取得資料, 所以因為寫 django 的經驗, 我可能就直接丟 user 變數進 template, 然後都用 user.podcast, 跟 user.episodes 來取得資料, 可是這在 app engine 則是要避免的, 因為 app engine 的 model 並沒有 query cache, 所以可能會像下面這樣的作法, 就會對 podcast 做兩次 query。
{{ user.podcast.title }}
{{ user.podcast.description }}
所以得一開始就把 user 跟 podcast 丟進 template, 直接取得資料而不是透過物件關連。
不過有時候就是一定得透過關連來做, 那麼就請使用 django 1.0 以上的 template, 透過 with 這個 template tag 來避免多次 query, 或者是在 model 新增一個 property 然後用 memcache 來解決, 像是我的作法
class User(db.Model):
...
@property
def podcast(self):
cache_key = 'user-podcast-%s' % self.key()
p = memcache.get(cache_key)
if not p:
p = self.podcasts.ancestor(self).get()
memcache.add(cache_key, p)
return p
先前說過, 以前不懂忘記給 key_name, 現在只能這樣 query 了, 頂多加上個 ancestor (默)
結論
datastore 雖然不太需要擔心 scale 的問題, 可是在 run 之前卻得更仔細規劃, 否則效能低落, 花的只是更多錢阿 XD 而 memcache 則是一定得用, 否則跑起來的確是不夠快。
大概是這樣, 希望沒有哪裡有寫錯的, 如果有的話, 也請多多留言指指教, 謝謝 :p




