Datastore 支援交易。交易是一項或一組不可分割的操作,這表示交易中的操作必須全部完成,或是全部不進行。應用程式可以在一次交易中執行多項操作與計算。
使用交易
「交易」是在一或多個實體上執行的一組 Datastore 作業。每次交易皆保證具有單元性,這代表交易絕不會部分完成。不是交易中的操作全部完成,就是全部不予進行。交易的最長持續時間為 60 秒,而在 30 秒後則有 10 秒的閒置到期時間。
如有以下情形,操作可能會失敗:
- 同一個實體群組上嘗試進行太多並行修改。
- 交易超過資源限制。
- 資料儲存庫發生內部錯誤。
在這些情況下,Datastore API 都會擲回例外狀況。
交易是 Datastore 的選用功能,您不一定要透過交易才能執行 Datastore 作業。
以下範例說明如何更新 Joe
類型 Employee
實體中的 vacationDays
欄位:
請注意,為了讓範例保持精簡,我們有時會省略在交易仍有效的情況下,執行復原的 finally
區塊。在實際執行程式碼中,請務必確認每個交易是否已明確修訂或復原。
實體群組
每個實體都屬於一個實體群組,也就是一或多個實體的集合,可在單一交易中進行操作。實體群組關係可讓 App Engine 將多個實體儲存在分散式網路中的相同部分。交易會為實體群組設定 Datastore 作業,並將所有作業一併套用,或在交易失敗時完全不套用。
應用程式建立實體時,可將其他實體指派為新實體的「父項」。為新實體指派父項會將新實體放在與父系實體相同的實體群組中。
沒有父項的實體則是「根」實體。實體如果是另一個實體的父項,也可以擁有父項。從實體到根的父系實體鏈結,即為該實體的路徑,而路徑成員則是實體的祖系。實體的父項是在實體建立時定義,而且之後無法進行變更。
以指定根實體做為祖系的每個實體位於相同的實體群組中。群組中的所有實體都會儲存在同一個 Datastore 節點中。單一交易可以修改單一群組中的多個實體,也可以透過將新實體的父項設定為群組中的現有實體,來將新實體新增至群組。以下的程式碼片段示範各種不同實體類型的交易:
在特定實體群組中建立實體
在應用程式建構新實體後,您可提供另一個實體的金鑰,如此即可將這個實體指派至實體群組。以下範例會建構 MessageBoard
實體的鍵,然後使用該鍵建立並保存與 MessageBoard
位於相同實體群組中的 Message
實體:
使用跨群組交易
跨群組交易 (也稱為 XG 交易) 會跨越多個實體群組運作,其行為類似於單一群組交易 (如上所述),但如果程式碼嘗試從一個以上的實體群組更新實體,則跨群組交易不會失敗。
跨群組交易和單一群組交易的使用方法類似,不過當您展開交易時,需要使用 TransactionOptions
來指定您要讓交易成為跨群組交易:
在交易中可執行的操作
Datastore 會限制單一交易中可執行的操作。
如果交易是單一群組交易,交易中的所有 Datastore 操作都必須在同一個實體群組中的實體上運作;如果交易是跨群組交易,則必須在最多二十五個實體群組中的實體上運作。這些操作包括使用祖系查詢實體、使用鍵擷取實體、更新實體以及刪除實體。請注意,每個根實體都屬於不同的實體群組,因此單一交易無法建立或操作多個根實體,除非是跨群組交易。
當兩個或多個交易同時嘗試修改一或多個共用實體群組中的實體時,只有提出修改的第一個交易才能成功;其他則會在提出時失敗。由於這項設計的緣故,使用實體群組會限制您可以對群組中任何實體執行的並行寫入次數。當交易開始時,Datastore 會檢查交易中所使用的實體群組上次更新的時間,以便使用開放式並行控制;為實體群組認可交易時,Datastore 會再次檢查交易中所使用的實體群組上次更新的時間,如果該值自首次檢查後有所變更,系統會擲回例外狀況。
應用程式只有在包含祖系篩選器的情況下,才能在交易期間執行查詢。應用程式也可以在交易時,藉由鍵來取得資料儲存庫實體。您可以在交易之前先準備好「金鑰」,或是可以在交易中藉由金鑰名稱或 ID 來建構「金鑰」。
隔離與一致性
在交易之外,Datastore 的隔離等級最接近已修訂的讀取作業;在交易內部,系統則會強制執行可序列化隔離,這代表另一項交易無法並行修改由這項交易讀取或修改的資料。
在交易中,所有讀取作業都會反映交易開始時 Datastore 的當前一致狀態。在交易開始時,交易內部的查詢和取得一定可看到 Datastore 的一致單一快照。交易實體群組中的實體和索引列會完全更新,因此查詢可傳回完整正確的結果實體組合,而不會如同在交易之外的查詢中發生誤判和漏判情形。
此一致性的快照檢視也擴充至交易內部的寫入後的讀取。與大部分資料庫不同,Datastore 交易內部的查詢和獲取「不會」看到先前在該交易中寫入的結果,尤其如果實體在交易內經過修改或刪除,查詢或將傳回交易開始時的「原始」版實體;如果當時該實體尚不存在,則不傳回任何實體。
交易用途
這個範例將說明交易的一種用途:將實體更新為新的屬性值,而不是其目前的值。由於 Datastore API 不會重試交易,因此我們可以新增邏輯,以便在其他要求同時更新相同 MessageBoard
或其任何 Messages
時重試交易。
這需要使用交易,因為在此程式碼擷取物件之後,其他使用者可能會在系統尚未儲存已修改物件的情況下更新值。如果沒有交易,則使用者的要求將採用其他使用者更新之前的 count
值,而儲存也將覆寫新的值。但是藉由交易,應用程式將得知其他使用者的更新。如果實體在交易期間更新,則交易會失敗,並顯示 ConcurrentModificationException
。應用程式可重複這個交易藉以使用新資料。
交易的另一個常見用途是擷取具有指定鍵的實體,或是建立這個鍵 (假如鍵不存在):
和先前一樣,如要處理另一個使用者嘗試建立或更新具有相同字串 ID 實體的情況,就必須使用交易。在沒有交易的情況下,假如實體不存在且兩個使用者嘗試建立該實體,則第二個實體會在不知情的情況下覆寫第一個實體。在交易中,第二次嘗試會以原子方式失敗。但是當這個操作可行時,應用程式則可重試以擷取實體並加以更新。
當交易失敗時,您可以讓應用程式重試交易,直到成功為止,也可以將交易傳播至應用程式的使用者介面層級,讓使用者處理錯誤。您不必為每一個交易建立重試迴圈。
最後,您可以使用交易讀取 Datastore 的一致快照。當需要多個讀取取得以轉譯網頁,或是匯出必須是一致的資料時,這項功能相當有用。由於這種交易不執行寫入,所以通常稱為「唯讀」交易。單一群組唯讀交易一律不會因並行修改而失敗,因此您不必在失敗時重試。不過,跨群組交易可能由於並行修改而失敗,因此必須進行重試。認可和回復唯讀交易都是免人工管理的。
交易工作排入佇列
您可以將工作排入佇列做為 Datastore 交易的一部分,以便只在成功修訂交易時才將工作排入佇列,並確保工作已順利新增。如果該交易修訂完成,則可保證將該工作新增至佇列。工作新增至佇列後,並不能保證立即執行該工作,而且在工作內執行的所有作業會在原始交易中個別執行。工作會持續重試,直到成功為止。所有在交易中加入佇列的工作都適用此原則。
交易工作非常實用,因為您可以在 Datastore 交易中列出非 Datastore 動作 (例如傳送電子郵件來確認購買交易)。您也可以將 Datastore 動作與交易連結,例如在交易成功時,將變更提交至交易以外的其他實體群組。
在單一交易期間,應用程式最多只能將五項交易工作插入工作佇列。交易工作不能有使用者指定的名稱。