了解大規模讀寫

閱讀本文檔,以便在建立應用程式以實現高效能和可靠性方面做出明智的決策。本文檔包含進階 Cloud Firestore 主題。如果您剛開始使用 Cloud Firestore,請參閱快速入門指南

Cloud Firestore 是一個靈活、可擴展的資料庫,適用於 Firebase 和 Google Cloud 的行動裝置、Web 和伺服器開發。開始使用 Cloud Firestore 並編寫豐富且強大的應用程式非常簡單。

為了確保您的應用程式在資料庫大小和流量增加時繼續保持良好效能,了解 Cloud Firestore 後端中的讀寫機制很有幫助。您還必須了解讀寫與儲存層的交互作用以及可能影響效能的底層約束。

在建立應用程式之前,請參閱以下部分以了解最佳實踐。

了解高層組件

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

進階組件

Cloud Firestore SDK 和客戶端程式庫

Cloud Firestore 支援不同平台的 SDK 和用戶端程式庫。雖然應用程式可以直接對 Cloud Firestore API 進行 HTTP 和 RPC 調用,但客戶端程式庫提供了一個抽象層來簡化 API 使用並實作最佳實踐。它們還可能提供額外的功能,例如離線存取、快取等。

Google前端 (GFE)

這是所有 Google 雲端服務通用的基礎架構服務。 GFE 接受傳入請求並將其轉發到相關的 Google 服務(在此上下文中為 Firestore 服務)。它還提供其他重要功能,包括防止拒絕服務攻擊。

雲端 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 會讀取現有文檔,並確定要對文檔表中的資料進行的變更。

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

  • 新增至文件中的欄位需要在索引表中進行對應的插入。
  • 從文件中刪除的欄位需要在索引表中進行相應的刪除。
  • 文件中正在修改的欄位需要在索引表中刪除(對於舊值)和插入(對於新值)。

為了計算前面提到的突變,Cloud Firestore 就會讀取專案的索引配置。索引配置儲存有關項目索引的資訊。 Cloud Firestore 使用兩種類型的索引:單一欄位索引和複合索引。有關在 Cloud Firestore 中建立的索引的詳細信息,請參閱Cloud Firestore 中的索引類型

計算突變後,Cloud Firestore 會將它們收集到事務中,然後提交。

了解儲存層的寫事務

如前所述,Cloud Firestore 中的寫入涉及儲存層中的讀寫事務。根據資料佈局,寫入可能涉及一個或多個分割,如資料佈局所示。

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

Cloud Firestore 資料庫拆分

考慮一個包含Restaurants集合的 Cloud Firestore 資料庫,如下所示:

餐廳系列

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

更改集合中的文檔

以下進階步驟描述了寫入過程中發生的情況:

  1. 建立讀寫事務。
  2. 從儲存層的Documents表中讀取Restaurants集合中的restaurant1文件。
  3. 索引表中讀取文件的索引。
  4. 計算要對數據進行的突變。在這種情況下,有五個突變:
    • M1:更新文件表中restaurant1的行以反映priceCategory欄位值的變更。
    • M2 和 M3:刪除Indexes表中降序和升序索引中priceCategory舊值的行。
    • M4 和 M5:在降序和升序索引的Indexes表中插入priceCategory新值的行。
  5. 進行這些突變。

Cloud Firestore 服務中的儲存用戶端會尋找擁有要變更的行的鍵的分割。讓我們考慮這樣一種情況:Split 3 服務於 M1,Split 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. 索引表進行單一範圍掃描
  2. 根據先前掃描的結果在文件表中進行點查找
Cloud Firestore 中可能存在某些需要較少處理或較多處理的查詢(例如 IN 查詢)。

儲存層的資料讀取是內部透過資料庫事務完成的,保證讀取的一致性。但是,與用於寫入的事務不同,這些事務不取得鎖。相反,它們的工作方式是選擇一個時間戳,然後在該時間戳記執行所有讀取。由於它們不獲取鎖,因此不會阻止並發讀寫事務。為了執行此事務,Cloud Firestore 中的儲存用戶端會指定時間戳綁定,該綁定告訴儲存層如何選擇讀取時間戳。 Cloud Firestore 中儲存用戶端選擇的時間戳綁定類型由讀取請求的讀取選項決定。

了解儲存層的讀取事務

本部分介紹讀取類型以及它們在 Cloud Firestore 的儲存層中的處理方式。

強讀

預設情況下,Cloud Firestore 讀取是強一致的。這種強一致性意味著 Cloud Firestore 讀取會傳回最新版本的數據,該版本反映了讀取開始之前已提交的所有寫入操作。

單一分割讀取

Cloud Firestore 中的儲存用戶端會尋找擁有要讀取的行的鍵的分割。我們假設它需要從前面部分的Split 3 中讀取。客戶端將讀取請求傳送到最近的副本以減少往返延遲。

此時,根據所選副本的不同,可能會發生以下情況:

  • 讀取請求傳送至領導副本(區域 A)。
    • 由於leader始終是最新的,因此讀取可以直接進行。
  • 讀取請求傳送至非領導副本(例如,B 區)
    • Split 3 可能透過其內部狀態知道它有足夠的資訊來服務讀取,而 split 會這樣做。
    • Split 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 預設會自動索引文件中的所有字段,因此也可以在包含順序遞增/遞減值(如時間戳記)的文件字段的索引空間上建立此類移動熱點。

請注意,透過遵循上述實踐,Firestore 可以擴展以服務任何大的工作負載,而無需調整任何配置。

故障排除

Firestore 提供Key Visualizer作為診斷工具,專門分析使用模式和解決熱點問題。

下一步是什麼