瞭解大規模的讀取與寫入

請參閱這份文件,瞭解如何為應用程式架構做出明智的決策,以便提升效能和可靠性。本文件包含進階 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 中有些查詢的處理量或許較少,或是處理量較少 (例如印度查詢)。

從儲存層讀取資料時,系統會在內部使用資料庫交易,確保讀取作業一致性。不過,與用於寫入的交易不同,這些交易不會鎖定。而是選擇時間戳記,然後執行該時間戳記的所有讀取作業。因為這些物件不會取得鎖定,因此不會封鎖並行的讀寫交易。為了執行這項交易,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 診斷工具,可用於分析使用模式和排解熱點問題。

後續步驟