NDB 快取

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

簡介

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

您可以傳遞 Context Options 引數,變更許多 NDB 函式的快取行為。舉例來說,您可以呼叫 key.get(use_cache=False, use_memcache=False) 來略過快取。您也可以在 NDB 環境中變更預設快取政策,詳情請參閱下文。

注意: 使用管理控制台的 Datastore Viewer 修改 Datastore 內容時,快取值不會更新。 因此快取可能不一致。 對於情境內快取,這通常不是問題。 如果是 Memcache,建議使用管理控制台清除快取。

Context 物件

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

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

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

內容快取

內容快取屬性只能在單一執行緒持續時間內存在。也就是說,每個傳入的 HTTP 要求都會獲得新的情境內快取,且只會「顯示」給處理該要求的程式碼。如果應用程式在處理要求時產生任何額外執行緒,這些執行緒也會有新的獨立情境內快取。

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

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

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

Memcache

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

根據預設,非交易式環境會將所有實體快取到 memcache。 應用程式的所有環境都會使用相同的 Memcache 伺服器,並看到一致的快取值集。

Memcache 不支援交易。因此,原本要同時套用至 Datastore 和 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)

這項功能與內容快取和記憶體快取政策函式類似:如果 Datastore 政策函式針對指定鍵傳回 False,系統就不會將對應實體寫入 Datastore。(如果政策函式允許,可能會寫入程序內快取或 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 中儲存實體,並覆寫預設的 Datastore 政策。
_memcache_timeout int Memcache 中實體的存留時間上限,會覆寫預設的 Memcache 超時政策。

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

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