瞭解大規模的讀取與寫入

請參閱這份文件,瞭解如何為應用程式架構做出明智的決策,以便提升效能和可靠性。本文件包含進階 Cloud Firestore 主題。如果您是剛開始使用 Cloud Firestore,請改為參閱快速入門指南

Cloud Firestore 是一種充滿彈性且可擴充的資料庫,適用於 Firebase 和 Google Cloud 的行動裝置、網頁和伺服器開發。您可以輕鬆開始使用 Cloud Firestore,並撰寫功能豐富且強大的應用程式。

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

請先參閱下列最佳做法,再建構應用程式。

瞭解高階元件

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

高階元件

Cloud Firestore SDK 和用戶端程式庫

Cloud Firestore 支援不同平台的 SDK 和用戶端程式庫。雖然應用程式可以直接向 Cloud Firestore API 發出 HTTP 和 RPC 呼叫,但用戶端程式庫可提供一層抽象,簡化 API 使用方式並實作最佳做法。也可能提供離線存取、快取等其他功能。

Google Front End (GFE)

這是所有 Google Cloud 服務的共同基礎架構服務。GFE 會接受傳入的要求,並轉送至相關的 Google 服務 (在此情況下為 Cloud Firestore 服務)。同時提供其他重要功能,包括防範阻斷服務攻擊。

Cloud Firestore 項服務

Cloud Firestore 服務會對 API 要求執行檢查,包括驗證、授權、配額檢查和安全性規則,並管理交易。這個 Cloud Firestore 服務包含一個儲存空間用戶端,可與儲存空間層互動,用於讀取及寫入資料。

Cloud Firestore 儲存空間層

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

鍵範圍和分割

Cloud Firestore 是 NoSQL 文件資料庫。您可以將資料儲存在文件中,並以集合階層排序。系統會將集合階層和文件 ID 轉譯為每份文件的單一索引鍵。系統會根據這個單一鍵,以邏輯方式儲存文件並依字典順序排列。我們使用「鍵範圍」一詞,是指字典順序相鄰的鍵範圍。

一般 Cloud Firestore 資料庫太大,無法放入單一實體機器。也有一些情境是,資料上的工作負載太重,單一機器無法處理。為處理大量工作負載,Cloud Firestore 會將資料分割成片段,以便儲存在多部機器或儲存空間伺服器上,並從這些機器提供服務。這些分區是在資料庫資料表的索引鍵範圍區塊上建立,稱為「分割」。

同步複製

請務必注意,資料庫一律會以自動同步方式複製。資料分割作業會在不同的區域中建立備用資源,即使某個區域無法存取,資料仍可供使用。這樣一致地複製到分割的各個副本的作業是由 Paxos 演算法管理。每個分割區塊的一個備用資源會當選為 Paxos 領導者,負責處理該分割區塊的寫入作業。同步複製功能可讓您隨時從 Cloud Firestore 讀取最新版本的資料。

整體來說,這會產生可擴充且高可用性的系統,無論是讀取還是寫入,都能提供低延遲,即使在沉重的工作負載和非常大的規模下亦是如此。

資料版面配置

Cloud Firestore 是無結構定義的文書資料庫。不過,在內部,它會在儲存層中以兩個關聯資料庫樣式的資料表,主要排列資料,如下所示:

  • 文件資料表:文件會儲存在這個資料表中。
  • 索引資料表:這個資料表會儲存索引項目,方便您快速取得結果,並依索引值排序。

下圖顯示 Cloud Firestore 資料庫的資料表在分割後的樣貌。分割作業會在三個不同的區域中複製,且每個分割都會指派 Paxos 領導者。

資料版面配置

單一區域與多區域

建立資料庫時,您必須選取區域多區域

單一地區位置是特定的地理位置,例如 us-west1。如前文所述,Cloud Firestore 資料庫的資料分割作業會在所選區域的不同區域中建立複本。

多區域位置包含一組定義的區域,用於儲存資料庫的備用資源。在 Cloud Firestore 的多區域部署中,其中兩個區域擁有資料庫中完整備份的整個資料。第三個區域則有見證備用資源,該備用資源不會維護完整的資料,但會參與複製作業。透過在多個地區之間複製資料,即使整個地區遭到中斷,您仍可寫入及讀取資料。

如要進一步瞭解地區的據點,請參閱 Cloud Firestore 據點

單一區域與多區域

瞭解 Cloud Firestore 中的寫入生命週期

Cloud Firestore 用戶端可以透過建立、更新或刪除單一文件來寫入資料。寫入單一文件時,必須在儲存層中以不可分割的方式更新文件及其相關索引項目。Cloud Firestore 也支援原子作業,包括對一或多個文件執行多項讀取和/或寫入作業。

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

寫入交易的高階步驟

Cloud Firestore 用戶端使用上述任何方法發出寫入或提交交易時,系統會在儲存層中以資料庫讀寫交易的形式在內部執行這項交易。這項交易可讓 Cloud Firestore 提供先前提到的 ACID 屬性。

Cloud Firestore 會讀取現有的文件,並判斷要對「Documents」表格中的資料進行哪些變異。

這也包括對索引表進行必要的更新,如下所示:

  • 新增至文件的欄位需要在索引資料表中對應插入。
  • 從文件中移除的欄位需要在「索引」表格中對應刪除。
  • 在文件中修改的欄位,需要在索引表中刪除舊值並插入新值。

為了計算先前提到的突變,Cloud Firestore 會讀取專案的索引設定。索引設定會儲存專案的索引資訊。Cloud Firestore 使用兩種索引:單一欄位和複合式。如要進一步瞭解在 Cloud Firestore 中建立的索引,請參閱「Cloud Firestore 中的索引類型」。

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

瞭解儲存層中的寫入交易

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

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

<span class=Cloud Firestore 資料庫分割作業">

請考慮以下 Cloud Firestore 資料庫,其中包含 Restaurants 集合:

餐廳集合

Cloud Firestore 用戶端會更新 priceCategory 欄位的值,要求對 Restaurant 集合中的文件進行以下變更。

變更至珍藏內容中的文件

以下大致步驟說明寫入作業的運作方式:

  1. 建立讀寫交易。
  2. 從儲存層的 Documents 表格讀取 Restaurants 集合中的 restaurant1 文件。
  3. 從「索引」表格讀取文件的索引。
  4. 計算要對資料進行的變異。在本例中,有五個突變:
    • M1:更新「文件」表格中 restaurant1 的資料列,反映 priceCategory 欄位的值變更。
    • M2 和 M3:刪除「Indexes」表格中,針對遞減和遞增索引號的 priceCategory 舊值資料列。
    • M4 和 M5:在「索引」資料表中,插入降冪和升冪索引的 priceCategory 新值資料列。
  5. 提交這些變異。

Cloud Firestore 服務中的儲存空間用戶端會查詢擁有要變更資料列金鑰的分割。請考慮以下情況:分割 3 提供 M1,分割 6 提供 M2 至 M5。這裡有一個分散式交易,其中包含所有分割項目的參與者。參與者分割作業也可能包含任何其他分割作業,其中的資料先前曾在讀寫交易中讀取。

下列步驟說明在提交過程中會發生的情況:

  1. 儲存空間用戶端會發出修訂作業。該次提交包含 M1 至 M5 的突變。
  2. 分割 3 和 6 是這筆交易的參與者。系統會選出其中一個參與者擔任協調者,例如 Split 3。協調者的任務是確保所有參與者的交易皆以不可分割的形式修訂或取消。
    • 這些分割的領導者備份會負責參與者和協調者執行的作業。
  3. 每個參與者和協調者都會使用各自的副本執行 Paxos 演算法。
    • 領導節點會使用複本執行 Paxos 演算法。如果大多數副本都以 ok to commit 回應回覆領導節點,就會達到法定人數。
    • 每個參與者都會在準備就緒時通知協調者 (兩階段提交的第一階段)。如果有任何參與者無法修訂交易,整筆交易就會 aborts
  4. 一旦協調者知道所有參與者 (包括自身) 都已準備就緒,就會將 accept 交易結果傳送給所有參與者 (兩階段提交的第二階段)。在此階段,每個參與者都會將修訂決策記錄到穩定的儲存空間,並修訂交易。
  5. 協調器會在 Cloud Firestore 回應儲存空間用戶端,表示交易已完成。同時,協調者和所有參與者都會將變異套用到資料。

提交生命週期

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

多區域寫入

在多地區部署中,複本會分散到各個區域,可提高可用性,但會影響效能。不同區域的備援機制之間的通訊,往返時間會較長。因此,與單一區域部署相比,Cloud Firestore 作業的基準延遲時間會稍微增加。

我們會以某種方式設定備援機制,讓分割區的領導權一律保留在主要區域。主要區域是指流量進入 Cloud Firestore 伺服器的區域。這個領導決定可減少 Cloud Firestore 中儲存空間用戶端與複本領導者 (或多分割交易的協調器) 之間通訊的往返延遲時間。

Cloud Firestore 中的每個寫入作業也涉及與 Cloud Firestore 中即時引擎的互動。如要進一步瞭解即時查詢,請參閱「瞭解大規模即時查詢」。

瞭解 Cloud Firestore 中的讀取生命週期

本節將深入探討 Cloud Firestore 中的獨立非即時讀取作業。在內部,Cloud Firestore 伺服器會在兩個主要階段處理大部分的查詢:

  1. Indexes 資料表執行單一範圍掃描
  2. 根據先前掃描的結果,在「文件」資料表中進行點對點查詢
Cloud Firestore 中可能有些查詢需要較少或較多處理 (例如 IN 查詢)。

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

瞭解儲存層中的讀取交易

本節將說明讀取類型,以及在 Cloud Firestore 的儲存層中如何處理這些類型。

強式讀取

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

單一分割讀取

Cloud Firestore 中的儲存空間用戶端會查詢擁有要讀取資料列索引鍵的分割。假設它需要從先前部分的 Split 3 進行讀取。用戶端會將讀取要求傳送至最近的備援機制,以縮短往返延遲時間。

此時,視所選備援機制而定,可能會發生下列情況:

  • 讀取要求會傳送至領導者備用資源 (區域 A)。
    • 由於領導者永遠處於最新狀態,讀取便可直接繼續進行。
  • 讀取要求會傳送至非主要備用資源 (例如區域 B)
    • Split 3 可能會從內部狀態得知,它有足夠的資訊可處理讀取作業,因此會執行讀取作業。
    • 分割 3 不確定是否已看到最新資料。它會傳送訊息給領導者,要求需要套用的最後一筆交易的時間戳記,以處理讀取作業。套用交易後,讀取作業便可繼續進行。

Cloud Firestore 接著將回應傳回給用戶端。

多重分割讀取

如果必須從多個區隔執行讀取作業,則會在所有區隔中執行相同的機制。所有分割作業傳回資料後,Cloud Firestore 中的儲存空間用戶端會合併結果。Cloud Firestore 接著會使用此資料回應其用戶端。

過時讀取

強式讀取是 Cloud Firestore 的預設模式。不過,由於可能需要與領導者進行通訊,因此延遲時間可能會較長。通常,Cloud Firestore 應用程式不需要讀取最新版本的資料,而且資料可能會延遲幾秒,但這項功能仍能正常運作。

在這種情況下,用戶端可以選擇使用 read_time 讀取選項,接收過時的讀取結果。在這種情況下,讀取作業會在資料位於 read_time 時完成,而最近的備用資源極有可能已驗證其在指定 read_time 的資料。如要明顯提升效能,15 秒是合理的過時程度值。即使是過時的讀取作業,產生的資料列也會彼此一致。

避免資源使用率不均

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

分割儲存空間和負載需要時間,如果流量增加過快,可能會導致延遲時間過長或超出期限的錯誤,也就是我們稱為熱點的服務調整情形。最佳做法是在索引鍵範圍內分散作業,同時提高資料庫中集合中的流量,以每秒 500 個作業為目標。逐步增加流量後,每五分鐘最多可增加 50% 的流量。這個程序稱為 500/50/5 規則,可讓資料庫以最佳規模調整,以符合工作負載需求。

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

當多個作業嘗試同時讀取和/或寫入相同文件時,就會發生競爭錯誤。

當以序遞增/遞減鍵做為 Cloud Firestore 中的文件 ID,且每秒的運算次數相當高時,就會發生另一種特殊的熱點情形。由於流量激增,新建立的版本就會移至新建立的版本,因此建立更多版本並無助於解決問題。由於 Cloud Firestore 預設會自動為文件中的所有欄位建立索引,因此如果文件欄位含有時間戳記等遞增/遞減值,系統可能也會在索引空間中建立此類移動熱點。

請注意,只要遵循上述做法,Cloud Firestore 就能擴充以處理任意大的工作負載,而您不必調整任何設定。

疑難排解

Cloud Firestore 提供 Key Visualizer 診斷工具,可用於分析使用模式和排解熱點問題。

後續步驟