舊版套裝組合服務的 App Engine Datastore API

附註:我們強烈建議建構新應用程式的開發人員使用 NDB 用戶端程式庫,因為 NDB 用戶端程式庫與本用戶端程式庫相較之下有幾個優點,例如能透過 Memcache API 自動將實體加入快取。如果您目前使用的是舊版的 DB 用戶端程式庫,請參閱從 DB 至 NDB 的遷移指南

本文件說明儲存在 Datastore 中之物件的資料模型、使用 API 建構查詢的方法,以及處理交易的方法。

實體

Datastore 中的物件稱為「實體」。實體具有一或多個命名的「屬性」,每個屬性可以有一或多個值。屬性值可以屬於各種資料類型,包括整數、浮點數、字串、日期和二進位資料等。查詢具有多個值的屬性,可測試是否有值符合查詢條件。這可讓這類屬性適合用於成員資格測試。

種類、金鑰與 ID

每個 Datastore 實體都屬於特定「種類」,可將實體分門別類,方便查詢。舉例來說,人力資源應用程式可能會用 Employee 種類的實體來代表公司的每位員工。此外,每個實體都有專門用來識別該實體的專屬「金鑰」。金鑰是由以下元件組成:

  • 實體的種類
  • ID,可能是以下兩者之一:
    • 「索引鍵名稱」字串
    • 整數 ID
  • 選用的祖系路徑,可將實體置於 Datastore 階層之中

ID 會在實體建立時指派。由於 ID 是實體金鑰的一部分,因此與實體之間具有永久關聯,無法變更。ID 可利用兩種方式指派:

  • 您的應用程式可以指定實體專屬的金鑰名稱字串。
  • 您可以讓 Datastore 自動指派整數的數字 ID 給實體。

祖系路徑

Cloud Datastore 中的實體會形成階層結構空間,與檔案系統的目錄結構相似。建立實體時,可選擇將其他實體指定為「父項」;新實體則為父系實體的「子項」(請注意,不同於檔案系統,父項實體不需要實際存在)。沒有父項的實體則是「根實體」。實體與父項實體之間具有永久關聯性,一旦實體建立後就無法變更。Cloud Datastore 絕對不會將相同的數字 ID 指派給父項相同的兩個實體,也不會指派給兩個根實體 (即沒有父項的實體)。

實體的父項、父項的父項等以此類推,全都是這個實體的「祖系」;實體的子項、子項的子項等等,則都是其「子系」。根實體及其所有子系都屬於相同的「實體群組」。從根實體開始,再從父項到子項,最後到指定實體的實體序列,即構成該實體的「祖系路徑」。識別實體的完整索引鍵,包含連串種類-ID 組合序列,其中指定了實體的祖系路徑,最後則以該實體本身的種類-ID 組合做為結尾:

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

根實體的祖系路徑是空白路徑,其金鑰只包含實體本身的種類與 ID。

[Person:GreatGrandpa]

本概念以下圖說明:

顯示實體群組中根實體與子實體的關係

查詢和索引

除了按實體金鑰直接從 Datastore 擷取實體之外,應用程式也可以執行查詢,以按照實體的屬性值來擷取實體。查詢會針對指定種類的實體執行作業,其可指定實體屬性值、索引鍵和祖系的篩選條件,而且傳回的「結果」可能會是零個或更多個實體。另外,查詢也能指定排序順序,按照結果的屬性值進行排序。結果包含的所有實體都有篩選器中每個具名屬性的至少一個值 (有可能是空值),並按排序順序列出,其屬性值符合所有指定的篩選條件。查詢可傳回完整實體、 投影實體,或是僅傳回實體 金鑰

典型的查詢包含以下幾項:

執行時,查詢會擷取符合所有指定篩選條件的特定種類實體,並按照指定的順序進行排序。查詢會以唯讀方式執行。

附註:為了節省記憶體並提升效能,查詢應盡可能指定傳回結果數目限制。

查詢也可以包含祖系篩選器,將結果限制為來自指定祖系的實體群組。這種查詢稱為祖系查詢。根據預設,祖系查詢會傳回同步一致的結果,如此就能保證跟上最新的資料更新。相反地,非祖系查詢可以跨越整個 Datastore,而不只是單一實體群組,但這種查詢只是最終一致的,且可能傳回過時結果。如果您的應用程式非常需要發揮強大的一致性,您可能需要在建立資料結構時考慮這一點,將相關實體放在同一個實體群組中,以便透過祖系查詢 (而非非祖系查詢) 擷取這些實體。詳情請參閱「建立能達到同步一致性的資料結構」。

應用程式引擎會針對實體的每個屬性預先定義簡單的索引。App Engine 應用程式可在名為 index.yaml索引設定檔中進一步定義自訂索引。開發伺服器遇到無法使用現有索引執行的查詢時,即會自動在這個檔案中新增建議。您可在上載應用程式之前編輯設定檔,以手動調整索引。

附註:基於索引的查詢機制支援各種查詢,適用於大多數應用程式。不過,這種機制並不支援其他資料庫技術常見的某些查詢種類,Datastore 查詢引擎尤其不支援彙整與匯總查詢。如要瞭解 Datastore 查詢的限制,請參閱 Datastore 查詢頁面。

交易

每次嘗試插入、更新或刪除實體時,都是在交易的背景下進行。單一交易可包含的前述作業數量不受限制。為了維持資料一致性,交易一定會使其中包含的變動整組適用於 Datastore,其中如有任何變動失敗,則整組變動均不適用。

您可以對單一交易中的實體執行多次動作。例如,如要增加物件的計數器欄位,您需要讀取計數器的值、計算新值,然後將值儲存回欄位。若沒有交易,可能會有其他程序在您讀取值與更新值這段期間增加計數器,進而導致應用程式覆寫更新的值。在單一交易中進行讀取、計算與寫入,可確保沒有其他程序會對遞增量造成干擾。

交易和實體群組

交易中只允許祖系查詢:也就是說,每個交易查詢都必須限制為單一實體群組。交易本身可套用至多個實體,而實體可以屬於單一實體群組,或 (若為跨群組交易) 屬於多達二十五個不同的實體群組。

Datastore 使用最佳化並行管理交易。當兩個或多個交易同時嘗試變更相同實體群組 (更新現有實體或建立新實體) 時,修訂的第一個交易會成功,其他所有的交易都會在修訂時失敗,然後這些交易可在更新的資料上重試。請注意,這會限制您可以對指定實體群組中的任何實體執行的並行寫入次數。

跨群組交易

對屬於不同實體群組的實體進行的交易稱為「跨群組 (XG) 交易」。交易最多可套用至二十五個實體群組,並且只要沒有並行交易涉及其套用的任何實體群組,交易就會成功。這可讓您在整理資料時享有更大的彈性,因為您不必為了對資料執行完整寫入而被迫將資料的不同部分放在同一個祖系下。

就像單一群組交易一樣,您無法在 XG 交易中執行非祖系查詢。但是,您可以對個別實體群組執行祖系查詢。非交易 (非祖系) 查詢可能會看到先前修訂之交易的全部或部分結果,也可能看不到任何結果。(如要瞭解這個問題的背景,請參閱「Datastore 寫入功能與資料瀏覽權限」一文。) 但是,與部分修訂的單一群組交易相比,這種非交易查詢更有可能看到部分修訂的 XG 交易結果。

僅涉及單一實體群組之 XG 交易的效能與成本,與單一群組的非 XG 交易完全一致。在涉及多個實體群組的 XG 交易中,作業成本與在非 XG 交易中執行的成本相同,但延遲時間可能比較長。

Datastore 寫入功能與資料瀏覽權限

資料會分兩個階段寫入 Datastore:

  1. 在「修訂」階段,實體資料會記錄在大多數備用資源的交易記錄中,而未記錄資料的任何備用資源都會標示為無最新記錄。
  2. 「套用」階段會在每個備用資源中單獨進行,並且包含兩個平行執行的動作:
    • 實體資料會寫入這個備用資源。
    • 實體的索引列會寫入這個備用資源。(請注意,這個動作花費的時間比寫入資料本身花費的時間要長。)

「修訂」階段之後,寫入作業會立即傳回結果,然後「套用」階段會以非同步方式進行,可能在每個備用資源中進行的時間都不同,並且可能在「修訂」階段完成後延遲幾百毫秒或更長的時間再進行。如果在「修訂」階段發生失敗情形,系統會自動重試;但若持續失敗,Datastore 會傳回錯誤訊息,指示應用程式出現例外狀況。若特定備用資源的「修訂」階段成功,但「套用」失敗,只要發生以下任一情形,就會將這個備用資源的「套用」向前輪動至完成:

  • 定期執行的資料儲存庫清除作業會檢查未完成的「提交」工作,並予以套用。
  • 使用受影響實體群組的某些作業 (getputdelete 和祖系查詢) 會使已修訂但尚未套用的任何變更在執行作業的備用資源中完成,並且會先執行這些作業,然後再繼續執行新作業。

對於在「修訂」與「套用」階段的不同部分,這個寫入行為會對應用程式公開資料的方式和時間點產生若干影響:

  • 如果寫入作業報告逾時錯誤,則無法確定 (在不嘗試讀取資料的情況下) 作業成功還是失敗。
  • 由於 Datastore 的取得與祖系查詢作業會將任何未解決的修改套用至執行修改的備用資源,因此這些作業一律會看到先前所有成功交易的一致視圖。這表示,get 作業 (按實體金鑰查詢更新的實體) 一定會看到最新版本的實體。
  • 非祖系查詢可能會傳回過時結果,因為這種查詢可能會對尚未套用最新交易的備用資源執行。即使執行了保證套用未完成交易的作業,也會發生這種情況,因為查詢可能會在與前一個作業不同的備用資源上執行。
  • 並行變更的時間可能會影響非祖系查詢的結果。如果實體最初符合查詢,但後來又變成不再符合了,這個實體可能仍會包含在查詢的結果集中,但前提是變更尚未套用至執行查詢所在備用資源中的索引。

Datastore 統計資料

Datastore 會留存與應用程式預存資料相關的統計資料,例如有多少實體符合特定種類,或者特定類型的屬性值使用多少空間。您可以在Google Cloud 控制台的 Datastore 資訊主頁頁面查看這些統計資料。您也可以使用 Datastore API 查詢名稱特殊的實體,以程式輔助方式在應用程式內部存取這些值;詳情請參閱 Python 2 中的 Datastore 統計資料

Python 2 資料儲存庫範例

在 Python API 中,模型用於說明實體的類型,包括其屬性的類型和設定。應用程式使用 Python 類別定義模型,並使用類別屬性說明屬性。類別的名稱會變成實體種類的名稱。特定種類的實體由模型類別的執行個體表示,並由執行個體屬性表示屬性值。如要建立新實體,請建構所需類別的物件、設定其屬性,然後予以儲存 (透過呼叫 put() 之類的方法):

import datetime
from google.appengine.ext import db
from google.appengine.api import users


class Employee(db.Model):
  name = db.StringProperty(required=True)
  role = db.StringProperty(required=True,
                           choices=set(["executive", "manager", "producer"]))
  hire_date = db.DateProperty()
  new_hire_training_completed = db.BooleanProperty(indexed=False)
  email = db.StringProperty()


e = Employee(name="John",
             role="manager",
             email=users.get_current_user().email())
e.hire_date = datetime.datetime.now().date()
e.put()

Datastore API 提供兩種查詢介面:查詢物件介面與類似 SQL 的查詢語言 (稱為 GQL)。查詢會以模型類別執行個體的形式傳回實體:

training_registration_list = ["[email protected]",
                              "[email protected]",
                              "[email protected]"]
employees_trained = db.GqlQuery("SELECT * FROM Employee WHERE email IN :1",
                                training_registration_list)
for e in employees_trained:
  e.new_hire_training_completed = True
  db.put(e)