NDB 快取

NDB 會替您管理快取。快取層級有兩種:內容內快取和 App Engine 標準快取服務 (Memcache) 的閘道。根據預設,系統會為所有實體類型啟用這兩種快取,但您可以視進階需求進行設定。此外,NDB 還實作了名為自動批次處理的功能,可嘗試將作業分組,以盡量減少伺服器來回傳輸。

簡介

快取對大多數類型的應用程式有益。NDB 會自動快取所寫入或讀取的資料 (除非應用程式已設定不快取)。從快取讀取資料的速度比從 Datastore 讀取資料快。

您可以傳遞內容選項引數,變更許多 NDB 函式的快取行為。舉例來說,您可以呼叫 key.get(use_cache=False, use_memcache=False) 來略過快取。您也可以變更 NDB 情境的預設快取政策,如下所述。

注意:如果您使用管理控制台的 Datastore 檢視器修改 Datastore 內容,系統不會更新快取值。因此,快取內容可能會不一致。對於內容內快取來說,這通常不是問題。針對 Memcache,建議您使用管理控制台清除快取。

Context 物件

快取管理會使用名為 Context 的類別:每個執行緒和每一筆交易都會在新的內容中執行。由於每收到一個傳入 HTTP 要求便會開始一個新的執行緒,因此也會 在新內容中執行每一項要求。如要存取目前的內容,請使用 ndb.get_context() 函式。

注意:在多個執行緒或要求之間共用 Context 物件是沒有意義的。請勿將內容儲存為全域變數! 將其儲存在本機或執行緒本機變數中即可。

Context 物件具有設定快取政策的方法,以及其他操控快取的方法。

內容快取

內容快取屬性只能在單一執行緒持續時間內存在。這表示每個傳入的 HTTP 要求都會獲得新的內容內快取,且只對處理該要求的程式碼「可見」。如果應用程式在處理要求時產生任何額外的執行緒,這些執行緒也會有新的、個別的內容內快取。

內容快取的速度很快;這種快取存在記憶體中。NDB 函式寫入 Datastore 時,也會寫入內容快取。NDB 函式讀取實體時,則 會先檢查內容快取。如果在該資料庫中找到實體,就不會進行資料儲存庫互動。

NDB 函式查詢 Datastore 時,會自 Datastore 擷取結果清單。不過,如果有任何個別結果位於內容內快取中,系統會使用該結果,而非從 Datastore 查詢擷取的值。如果快取政策允許,系統會將查詢結果寫回內容內快取 (但絕不會寫入 Memcache)。

若背景工作中執行長時間執行的查詢,內容快取有可能會 佔用相當大量的記憶體。原因在於不論是自目前內容擷取的實體,或是儲存在目前內容中的實體, 快取一律為每一個實體保留一個複本。如要避免長時間執行的工作產生記憶體例外狀況,您可以停用快取或設定政策,排除耗用最多記憶體的實體。

Memcache

Memcache 是 App Engine 的標準快取服務,速度比 Datastore 快上許多,但比內容快取慢 (毫秒 vs. 微秒)。

根據預設,非交易式情境會在 memcache 中快取所有實體。所有應用程式情境都會使用相同的 Memcache 伺服器,並查看一致的快取值組合。

Memcache 不支援交易。因此,如果更新內容適用於 Datastore 和 memcache,可能只會套用至其中一個。為在這種情況下維持一致性 (可能會犧牲效能),系統會從 memcache 刪除更新的實體,然後寫入 Datastore。後續的讀取作業會在 Memcache 中找出缺少的實體,從 Datastore 擷取該實體,然後在 Memcache 中更新該實體,做為讀取作業的副作用。此外,NDB 在交易中讀取資料時會忽略 Memcache。

在交易中寫入實體時,不會使用 memcache;確認交易時,交易內容會嘗試 刪除 memcache 中的所有此類實體。不過,請注意,某些失敗情況可能會導致刪除作業無法執行。

政策函式

自動快取功能對大多數應用程式來說都很方便,但如果您的應用程式不尋常,您可能會想為部分或所有實體關閉自動快取功能。您可以設定政策函式來控管快取的行為。有一個用於處理程序內快取的政策函式,可透過

context = ndb.get_context()
context.set_cache_policy(func)

另一種政策函式則適用於 memcache,設定方式如下:

context = ndb.get_context()
context.set_memcache_policy(func)

每個政策函式都會接受一個鍵,並傳回布林值結果。如果傳回 False,則該金鑰所識別的實體不會儲存在對應的快取中。例如,如要略過所有 Account 實體的程序內快取,您可以寫入

context = ndb.get_context()
context.set_cache_policy(lambda key: key.kind() != 'Account')

(不過,請繼續閱讀,瞭解如何以更簡單的方式完成相同的操作。) 為方便起見,您可以傳遞 TrueFalse,而非一律傳回相同值的函式。預設政策會快取所有實體。

另有一個 Datastore 政策函式負責控管 應寫入 Datastore 本身的實體:

context = ndb.get_context()
context.set_datastore_policy(func)

這與內容快取和 memcache 政策函式類似:如果資料儲存庫政策函式針對指定金鑰傳回 False,則系統不會將對應實體寫入資料儲存庫。(如果政策函式允許,則可能會寫入程序內快取或 memcache)。在您有類似實體的資料,但不需要儲存在 Datastore 中,這項功能就很實用。就像快取政策一樣,您可以傳遞 TrueFalse,而非一律傳回相同值的函式。

當 Memcache 記憶體壓力過大時,會 自動讓項目到期。您可以設定 Memcache 逾時政策函式,以決定實體在快取中的最大生命週期:

context = ndb.get_context()
context.set_memcache_timeout_policy(func)

這個函式會使用鍵引數呼叫,並應傳回指定最大壽命的整數 (以秒為單位);0 或 None 表示無限 (只要 Memcache 伺服器有足夠的記憶體)。為方便起見,您可以直接傳遞整數常數,而非一律傳回相同值的函式。如需逾時的詳細資訊,請參閱 memcache 說明文件。

注意:內容快取沒有獨立的生命週期政策:這類快取的生命週期與其內容相同 (也就是單一傳入的 HTTP 要求)。不過,您可以呼叫以下方法來清除程序內快取:
context = ndb.get_context()
context.clear_cache()

全新內容會從空的同處理序快取開始。

政策函式的彈性相當大, 但大多數的政策均十分簡單。例如:

  • 請勿將屬於特定模型類別的實體快取。
  • 將這個模型類別中實體的 Memcache 逾時時間設為 30 秒。
  • 這個模型類別中的實體不需要寫入 Datastore。

為簡化寫入並持續更新瑣碎的 政策函式的流程 (更麻煩的作業是要覆寫每一個 使用內容選項之作業的政策),預設政策函式 會自傳遞至這類政策函式的金鑰中擷取模型類別, 然後在該模型類別中查詢特定的類別變數:

類別變數類型說明
_use_cache bool 指定是否要在程序內快取中儲存實體;覆寫預設的程序內快取政策。
_use_memcache bool 指定是否要在 Memcache 中儲存實體;覆寫預設的 Memcache 政策。
_use_datastore bool 指定是否要在資料儲存庫中儲存實體,覆寫預設的 Datastore 政策。
_memcache_timeout int Memcache 中實體的最長存留期,會覆寫預設的 Memcache 逾時政策。

注意:這是每項政策的預設政策函式功能。如果指定自己的政策函式,但也想要恢復預設政策,請明確呼叫預設政策函式做為 Context 類別的靜態方法:

  • default_cache_policy(key)
  • default_memcache_policy(key)
  • default_datastore_policy(key)
  • default_memcache_timeout_policy(key)