附註:我們強烈建議建構新應用程式的開發人員使用 NDB 用戶端程式庫,因為 NDB 用戶端程式庫與本用戶端程式庫相較之下有幾個優點,例如能透過 Memcache API 自動將實體加入快取。如果您目前使用的是舊版的 DB 用戶端程式庫,請參閱從 DB 至 NDB 的遷移指南。
Async Datastore API 可讓您以平行、無阻斷的方式呼叫資料儲存庫,並在稍後處理要求時擷取這些呼叫的結果。本說明文件提供 Async Datastore API 下列方面的說明:
使用非同步資料儲存庫服務
您可以透過 Async Datastore API 使用 *_async
格式的方法發出資料儲存庫呼叫 (例如 google.appengine.ext.db
套件中的 get_async())。下列程式碼範例展示了一些使用非同步方法的簡單資料儲存庫作業。
from google.appengine.ext import db get_future = db.get_async(key) put_future = db.put_async(model) delete_future = db.delete_async(key) allocate_ids_future = db.allocate_ids_async(key, 10)
除了會立刻傳回可在稍後用來取得實際結果的非同步物件之外,這些函式和同步的版本執行一樣的作業。下列程式碼範例示範如何使用 get_result() 取得非同步結果:
entity = get_future.get_result() key = put_future.get_result() range = allocate_ids_future.get_result() # Wait for the operation to complete without returning a value. # Exceptions that occurred in the call are thrown here. Calling # get_result() allows you to verify that the deletion succeeded. delete_future.get_result()
注意:在您呼叫 get_result() 之前,系統不會傳回例外狀況。呼叫這個方法之後,您就能確認非同步作業是否完成。
使用非同步交易
與同步呼叫相同,Async Datastore API 呼叫也能用於交易。以下函式的作用是調整 Employee
的薪資,並在含有 Employee
的同一個實體群組中寫入額外的 SalaryAdjustment
實體,而且這些作業都在一次交易中進行。
def adjust_salary(employee_key, raise_ammount): def runner(): # Async call to lookup the Employee entity employee_entity_future = db.get_async(employee_key) # Create and put a SalaryAdjustment entity in parallel with the lookup adjustment_entity = SalaryAdjustment(parent=employeeKey) adjustment_entity.adjustment = raise_amount db.put_async(adjustmentEntity) # Fetch the result of our lookup to make the salary adjustment employee_entity = employee_entity_future.get_result() employee_entity.salary += raise_amount # Re-put the Employee entity with the adjusted salary. db.put_async(employee_entity) db.run_in_transaction(runner)
此範例說明瞭「不使用交易的非同步呼叫」與「使用交易的非同步呼叫」之間的重要差異。若您不使用交易,確保獨立非同步呼叫已完成的唯一方法,便是擷取使用交易的非同步物件的結果。在交易中發出的所有非同步呼叫產出結果,會在該交易提交時遭到封鎖。
因此在上方範例中,即便插入 SalaryAdjustment
實體的非同步呼叫在 runner()
完成時仍未完成,但除非插入作業完成,否則系統不會進行修訂。
非同步查詢
我們目前不開放針對明確的非同步 API 進行查詢。不過,如果您叫用 Query.run(),查詢會立即傳回,並以非同步方式預先擷取結果。這樣一來,應用程式就能在擷取查詢結果時平行執行工作。
# ... q1 = Salesperson.all().filter('date_of_hire <', one_month_ago) # Returns instantly, query is executing in the background. recent_hires = q1.run() q2 = Customer.all().filter('last_contact >', one_year_ago) # Also returns instantly, query is executing in the background. needs_followup = q2.run() schedule_phone_call(recent_hires, needs_followUp)
很抱歉,Query.fetch() 的行為並非如此。
非同步資料儲存庫呼叫的使用時機
當您呼叫 db.get() 等同步的 google.ext.db
方法時,在呼叫資料儲存庫完成前,程式碼都會維持封鎖狀態。如果應用程式只需要在 HTML 中轉譯 get() 的結果,那麼等待呼叫完成再執行其他動作,是相當合理的做法。不過,如果應用程式需要結合 get() 和查詢的結果才能轉譯回應,且 get() 和查詢沒有任何資料相依關係,那麼等待 get() 完成再啟動查詢,就會浪費時間。下列提供可透過非同步 API 提升效能的程式碼範例:
# Read employee data from the Datastore employee = Employee.get_by_key_name('Max') # Blocking for no good reason! # Fetch payment history query = PaymentHistory.all().ancestor(employee.key()) history = query.fetch(10) render_html(employee, history)
您可以改用 db.get_async() 以非同步方式執行呼叫,如此一來,就無需等待 get() 完成:
employee_key = db.Key.from_path(Employee.kind(), 'Max') # Read employee data from the Datastore employee_future = db.get_async(employee_key) # Returns immediately! # Fetch payment history query = PaymentHistory.all().ancestor(employee_key) # Implicitly performs query asynchronously history_itr = query.run(config=datastore_query.QueryOptions(limit=10)) employee = employee_future.get_result() render_html(employee, history_itr)
這個程式碼的同步版和非同步版 CPU 用量相差無幾 (因為這兩個版本執行的工作量相同),但非同步版的程式碼允許並行執行兩項資料儲存庫作業,因此非同步版的延遲時間較短。一般來說,如果您需要執行沒有任何資料相依性的多項資料儲存庫作業,使用非同步 API 可以明顯改善延遲情形。