2009年8月10日 星期一

減輕資料查詢、更新的負擔

在 Google App Engine 上開發應用程式,很多人會面臨到使用額度(Quota)的問題,所以開發者在將應用程式上線後,必須不斷觀察應用程式的存取狀況,以便隨時調整應用程式,避免某一項使用額度過份衝高。

這篇文章要介紹的是--儘可能地減少資料查詢、更新的動作,因為 Datastore API 的呼叫次數有限制,而且一個 request 的處理時間也有 30 秒的限制,如果在操作資料時沒有注意到一些細節,可能就會碰到問題。

平行讀取、更新或刪除 data entities


在「避免資料寫入衝突」這篇文章中,提到可以使用 App Engine 所提供的 memcache API 來作資料快取,這樣也是一種減少呼叫 Datastore API 的策略。

除此之外,善加利用每個 data entity 的 key 也可以達到減少呼叫 Datastore API 的目標。例如以下這段程式碼:
...
# 修改許多 data entity
for product in products:
product.price = product.price * 1.1
product.put()
...

在這個例子中,products 中有多少個 entities,就會呼叫多少次 put(),但若是將上述的程式碼修改為:
...
from google.appengine.ext import db
# 修改許多 data entity
for product in products:
product.price = product.price * 1.1
db.put(products)

由於 db.put() 函式支援 list 資料型態的參數,所以這樣的作法僅會算作一次 Datastore API 的呼叫,比起上述的方式大大節省了 Datastore API 的呼叫次數。

讀取資料時也是以此類推,若原本的程式為:
...
products = []
# keys 為一個 key list
for key in keys:
products.append(db.get(key))

也可以改寫成:
...
from google.appengine.ext import db
# keys 為一個 key list
products = db.get(keys)

同理可證,若要同時刪除許多 entities,也可以使用 db.delete

有效率地使用 GQL


另外,若是要使用 GqlQuery 作資料查詢時,若是只需要取出 entities 的 key 值,在 GQL 查詢語句中僅需取出 __key__ 欄位即可:
...
from google.appengine.ext import db

product_keys = db.GqlQuery('SELECT __key__ FROM Product WHERE title = :title', title='...')
# 或是 product_keys = Product.gql('WHERE title = :title', title='...', keys_only=True)

如此一來,讀取的時間就會比讀取全部的欄位還要快許多(如果資料量夠多的話...)

另外,若是查詢語句會重複使用,可以將該語句建立成一個 GqlQuery 物件後,再利用 bind() 方法重新利用該查詢語句。例如本來的程式碼可能是:
...
from google.appengine.ext import db

conditions = [['x', 'y'], ['1', '2'], .....]
for cond in conditions:
query = db.GqlQuery('SELECT * FROM Foo WHERE first = :first, second = :second', first=cond[0], second=cond[1])
....

如此一來,迴圈每執行一次就會建立一個 GqlQuery 物件,資料查詢會變得很沒有效率,如果碰到這樣的情況,程式碼應該改寫為:
...
from google.appengine.ext import db

conditions = [['x', 'y'], ['1', '2'], .....]
prepared_query = db.GqlQuery('SELECT * FROM Foo WHERE first = :first, second = :second')
for cond in conditions:
query = prepared_query.bind(first=cond[0], second=cond[1])
....

如此便能重複利用 GqlQuery 這個物件了。

2 則留言:

  1. 感謝您這一系列的教學文章,相當深入淺出,是有料又能輕鬆閱讀的好文章。謝謝

    回覆刪除
  2. 请教一下,你的blogger中代码样式是如何实现的?

    回覆刪除