大規模瞭解讀取和寫入作業

請參閱這份文件,根據相關資訊做出明智決策,為應用程式設計高效能和高可靠性的架構。本文涵蓋進階 Firestore 主題,如果您才剛開始使用 Firestore,請參閱快速入門指南

為確保應用程式在資料庫大小和流量增加時仍能正常運作,請瞭解 Firestore 後端的讀取和寫入機制。您也必須瞭解讀取和寫入作業與儲存層的互動,以及可能影響效能的基礎限制。

在設計應用程式架構前,請先參閱下列章節的最佳做法。

瞭解高階元件

下圖顯示 Firestore API 要求涉及的高階元件。

高階元件

SDK、用戶端程式庫和驅動程式

Firestore 支援不同平台的 SDK、用戶端程式庫和驅動程式。

Google Front End (GFE)

這是所有 Google Cloud 服務共用的基礎架構服務。GFE 會接受傳入要求,並將要求轉送至相關的 Google 服務 (本例為 Firestore 服務)。

Firestore 服務

Firestore 服務會檢查 API 要求,包括驗證、授權、配額檢查,以及管理交易。這項 Firestore 服務包含與儲存層互動的儲存用戶端,用於資料讀取和寫入作業。

Firestore 儲存層

Firestore 儲存層負責儲存資料和中繼資料,以及 Firestore 提供的相關資料庫功能。以下各節說明 Firestore 儲存層的資料結構,以及系統的擴充方式。瞭解資料的組織方式,有助於設計可擴充的資料模型,並進一步瞭解 Firestore 的最佳做法。

鍵範圍和分割

Firestore 是 NoSQL 文件導向資料庫,您可以在文件中儲存資料,並將文件整理成集合。集合名稱和文件 ID 會構成文件的專屬鍵。同一集合中的文件會一起儲存在鍵空間中。文件 ID 會在該鍵空間內經過雜湊處理。鍵範圍是指儲存空間中連續的鍵範圍。

Firestore 會自動將集合中的資料分割到多個儲存伺服器。這些分區稱為「分割」

文件可以產生按字典順序排序的索引項目,並與文件資料參與相同的分割和放置作業。

同步複製

系統會使用 Paxos,將每個寫入作業同步複製到大多數的備用資源。每個分割區都有一個副本會被視為領導者,並協調複製程序。如果主要節點發生故障,系統會選出新的主要節點。副本位於不同區域,可因應潛在的區域故障。整體而言,這是一個可擴充的高可用性系統,無論工作負載多重,或規模多大,都能提供低延遲的讀取和寫入作業。

單一區域與多區域

建立資料庫時,您必須選取單一區域位置多區域位置

單一地區位置是指特定地理位置,例如 us-west1。如先前所述,Firestore 資料庫的資料分割會在所選區域內的不同可用區中建立副本。

多區域位置包含一組已定義的區域,Firestore 會在這些區域中儲存資料庫副本。在 Firestore 的多區域部署中,有兩個區域會完整複製資料庫中的所有資料。第三個區域有見證備用資源,不會維護完整資料集,但會參與複製作業。即使整個區域都發生故障,您仍可寫入及讀取資料,因為 Firestore 會在多個區域之間複製資料。

如要進一步瞭解區域位置,請參閱「Firestore 位置」。

單一區域與多區域

瞭解寫入作業的生命週期

驅動程式可以建立、更新或刪除單一文件,藉此寫入資料。寫入單一文件時,必須在儲存層中,以原子方式更新文件和相關聯的索引項目。Firestore 也支援原子作業,可對一或多個文件執行多項讀取和寫入作業。

對於所有類型的寫入作業,Firestore 都提供關聯式資料庫的 ACID 屬性 (完整性、一致性、獨立性和耐用性)。Firestore 也提供可序列化功能,也就是所有交易看起來會像依序執行。

寫入交易的高階步驟

當驅動程式使用上述任一方法發出寫入或提交交易時,內部會以儲存層中的資料庫讀寫交易執行。交易可讓 Firestore 提供先前所述的 ACID 屬性。

做為交易的第一步,Firestore 會讀取現有文件,並判斷要對文件中的資料進行哪些變更。

這也包括更新任何相關索引:

  • 要新增至文件的已建立索引欄位,必須在索引中插入對應的項目。
  • 從文件中移除的已編列索引欄位,必須在索引中一併刪除。
  • 如要修改文件中的已建立索引欄位,索引中必須同時刪除舊值並插入新值。

如要計算稍早提及的突變,Firestore 會讀取專案的索引設定。索引設定會儲存專案的索引相關資訊。

計算完變異後,Firestore 會將變異收集到交易中,然後提交交易。

瞭解儲存層中的寫入交易

如先前所述,在 Firestore 中寫入資料時,儲存空間層會進行讀寫交易。視資料的版面配置而定,寫入作業可能涉及一或多個分割。

在下圖中,Firestore 資料庫有八個分割 (標示為 1 到 8),託管在單一可用區的三個不同儲存伺服器上,且每個分割都會在 3 個(或更多) 不同可用區中複製。每個分割都有 Paxos 領導者,不同分割的領導者可能位於不同區域。

Firestore 資料庫分割

假設 Firestore 資料庫有 Restaurants 集合,如下所示:

餐廳集合

驅動程式會更新 priceCategory 欄位的值,要求對 Restaurant 集合中的文件進行下列變更。

變更集合中的文件

以下高階步驟說明寫入作業的過程:

  1. 建立讀寫交易。
  2. 讀取 Restaurants 集合中的 restaurant1 文件。
  3. 讀取文件的索引。
  4. 計算要對資料進行的突變。在本例中,有五項變動:
    • M1:更新 restaurant1 的資料列,反映 priceCategory 欄位值的變更。
    • M2 和 M3:刪除 priceCategory 的舊索引項目。
    • M4 和 M5:為 priceCategory 新增索引項目。
  5. 提交這些突變。

Firestore 服務中的儲存空間用戶端會尋找擁有要變更資料列索引鍵的分割。假設分割 3 提供 M1,分割 6 提供 M2 至 M5。這是一項分散式交易,所有分割都是參與者。參與者分割區也可能包含先前在讀寫交易中讀取資料的任何其他分割區。

以下步驟說明提交作業的過程:

  1. 儲存空間用戶端發出修訂。該修訂項目包含 M1 到 M5 的突變。
  2. 分割 3 和 6 是這筆交易的參與者。其中一位參與者會被選為「協調者」,例如「Split 3」。協調者的工作是確認所有參與者的交易皆以不可分割的形式修訂或取消。
    • 這些分割的領導者副本負責參與者和協調者進行的作業。
  3. 每位參與者和協調員都會使用各自的副本執行 Paxos 演算法。
    • 領導者會與副本執行 Paxos 演算法。如果大多數副本都以 ok to commit 回應回覆領導者,即達到法定人數。
    • 接著,每位參與者都會在準備就緒時通知協調者 (兩階段提交的第一階段)。如果有任何參與者無法修訂交易,整筆交易便會aborts
  4. 協調者得知所有參與者 (包括自己) 都已準備就緒後,就會將 accept 交易結果傳送給所有參與者 (兩階段修訂的第二階段)。在這個階段,每個參與者都會將修訂決策記錄到穩定的儲存空間,並修訂交易。
  5. 協調者在 Firestore 中回覆儲存空間用戶端,表示交易已修訂。同時,協調者和所有參與者都會將變異套用到資料。

提交生命週期

如果 Firestore 資料庫很小,單一分割可能會擁有 M1 到 M5 變異中的所有鍵。在這種情況下,交易只有一個參與者,不需要上述的兩階段提交,因此寫入速度會更快。

多區域寫入

在多區域部署中,副本分散在各區域可提高可用性,但會造成效能成本。不同區域的副本之間通訊時,往返時間會較長。因此,與單一區域部署相比,Firestore 作業的基準延遲時間會稍長。

我們會設定備用資源,確保分割區的領導權一律位於主要區域。主要區域是指流量傳入 Firestore 伺服器的區域。領導階層的這項決定,可減少 Firestore 儲存空間用戶端與副本領導者 (或多重分割交易的協調器) 之間的通訊往返延遲。

瞭解讀取作業的生命週期

本節將深入探討 Firestore 中的讀取作業。查詢作業特別包含文件讀取和索引項目讀取。

系統會使用資料庫交易,從儲存層內部讀取資料,確保讀取作業一致。不過,與用於寫入作業的交易不同,這些交易不會鎖定作業。而是選擇時間戳記,然後在該時間戳記執行所有讀取作業。由於不會取得鎖定,因此不會封鎖並行的讀寫交易。如要執行這項交易,Firestore 中的儲存空間用戶端會指定時間戳記範圍,告訴儲存空間層如何選擇讀取時間戳記。Firestore 儲存空間用戶端選擇的時間戳記範圍類型,取決於 Read 請求的讀取選項。

瞭解儲存層中的讀取交易

本節說明讀取作業的類型,以及 Firestore 儲存層如何處理這些作業。

強式讀取

根據預設,Firestore 讀取作業具有同步一致性。這種強式一致性表示 Firestore 讀取作業會傳回最新版本的資料,反映讀取作業開始前已提交的所有寫入作業。

單一分割讀取

Firestore 中的儲存空間用戶端會尋找擁有要讀取資料列索引鍵的分割。假設需要從先前章節的 Split 3 讀取資料。用戶端會將讀取要求傳送到最近的副本,以縮短來回延遲時間。

此時,視所選副本而定,可能會發生下列情況:

  • 讀取要求會傳送至領導者副本 (區域 A)。
    • 由於領導者永遠處於最新狀態,讀取便可直接繼續進行。
  • 讀取要求送往非主要備用資源 (例如 Zone B)
    • 分割 3 可能會根據內部狀態判斷自己有足夠資訊可處理讀取作業,然後進行處理。
    • Split 3 不確定它看到是不是最新的資料。它會向領導者傳送訊息,要求需要套用的最後一筆交易的時間戳記,以處理讀取作業。一旦套用交易,便可繼續讀取作業。

Firestore 隨後會將回應傳回給用戶端。

多重分割讀取

如果必須從多個分割區讀取資料,所有分割區都會採用相同機制。所有分割作業都傳回資料後,Firestore 中的儲存空間用戶端會合併結果。接著,Firestore 會將這項資料傳送給用戶端。

避免熱點

Firestore 中的分割會自動分成較小的片段,以便在需要或索引鍵空間擴充時,將流量服務工作分配給更多儲存伺服器。即使流量消失,為處理過多流量而建立的拆分也會保留約 24 小時。因此,如果流量尖峰期反覆出現,系統會維持分割,並在必要時導入更多分割。這些機制可協助 Firestore 資料庫在流量負載或資料庫大小增加時自動擴充。不過,請注意以下限制。

分割儲存空間和負載需要時間,且流量增加過快可能會導致延遲時間過長或超出期限錯誤 (通常稱為「熱點」),而服務會進行調整。最佳做法是在鍵值範圍內分配作業,同時逐步增加資料庫中集合的流量。

雖然系統會隨著負載增加自動建立分割,但 Firestore 只能分割鍵範圍,直到系統使用專屬的一組複製儲存伺服器,提供單一文件為止。因此,如果對單一文件執行大量並行作業,可能會導致該文件出現熱點。如果單一文件持續出現高延遲,建議修改資料模型,將資料分割或複製到多個文件。

如果多項作業嘗試同時讀取及寫入同一份文件,就會發生爭用錯誤。

請注意,只要遵循本頁列出的做法,Firestore 就能擴充規模,處理任意大小的工作負載,您不必調整任何設定。