在構建使用 Cloud Firestore 的應用程序時,使用此處列出的最佳做法作為快速參考。
數據庫位置
創建數據庫實例時,請選擇離您的用戶和計算資源最近的數據庫位置。影響深遠的網絡躍點更容易出錯並增加查詢延遲。
要最大限度地提高應用程序的可用性和持久性,請選擇一個多區域位置並將關鍵計算資源放置在至少兩個區域中。
如果您的應用程序對延遲敏感,或者與其他 GCP 資源共置,請選擇區域位置以降低成本、降低寫入延遲。
文檔 ID
- 避免文檔 ID
.
和..
- 避免在文檔 ID 中使用
/
正斜杠。 不要使用單調遞增的文檔 ID,例如:
-
Customer1
、Customer2
、Customer3
…… -
Product 1
,Product 2
,Product 3
,...
這樣的順序 ID 可能會導致影響延遲的熱點。
-
字段名稱
避免在字段名稱中使用以下字符,因為它們需要額外轉義:
-
.
時期 [
左括號]
右括號*
星號`
反引號
-
索引
指數豁免
對於大多數應用程序,您可以依靠自動索引以及任何錯誤消息鏈接來管理您的索引。但是,您可能希望在以下情況下添加單字段豁免:
案件 | 描述 |
---|---|
大字符串字段 | 如果您有一個字符串字段,該字段通常包含不用於查詢的長字符串值,則可以通過免除該字段的索引來降低存儲成本。 |
對包含具有順序值的文檔的集合的高寫入率 | 如果您索引一個在集合中的文檔之間按順序增加或減少的字段,例如時間戳,那麼對該集合的最大寫入速率是每秒 500 次寫入。如果您不基於具有順序值的字段進行查詢,則可以免除該字段的索引以繞過此限制。 例如,在具有高寫入率的 IoT 用例中,包含具有時間戳字段的文檔的集合可能接近每秒 500 次寫入限制。 |
TTL字段 | 如果您使用TTL(生存時間)策略,請注意 TTL 字段必須是時間戳。 TTL 字段的索引在默認情況下處於啟用狀態,並且會影響較高流量速率下的性能。作為最佳實踐,為您的 TTL 字段添加單字段豁免。 |
大數組或映射字段 | 大型數組或映射字段可能接近每個文檔 40,000 個索引條目的限制。如果您不是基於大型數組或映射字段進行查詢,則應將其免除索引。 |
讀寫操作
應用程序可以更新單個文檔的確切最大速率在很大程度上取決於工作負載。有關詳細信息,請參閱更新單個文檔。
在可用的情況下使用異步調用而不是同步調用。異步調用最小化延遲影響。例如,考慮一個在呈現響應之前需要文檔查找結果和查詢結果的應用程序。如果查找和查詢沒有數據依賴關係,則無需同步等待查找完成後再發起查詢。
不要使用偏移量。相反,使用游標。使用偏移量只會避免將跳過的文檔返回給您的應用程序,但這些文檔仍會在內部檢索。跳過的文檔會影響查詢的延遲,並且您的應用程序需要為檢索它們所需的讀取操作付費。
事務重試
Cloud Firestore SDK 和客戶端庫會自動重試失敗的事務以處理暫時性錯誤。如果您的應用程序直接通過REST或RPC API 而不是通過 SDK 訪問 Cloud Firestore,則您的應用程序應實施事務重試以提高可靠性。
實時更新
有關實時更新的最佳實踐,請參閱大規模了解實時查詢。
規模化設計
以下最佳實踐描述瞭如何避免產生爭用問題的情況。
更新單個文檔
在設計應用程序時,請考慮應用程序更新單個文檔的速度。表徵工作負載性能的最佳方法是執行負載測試。應用程序可以更新單個文檔的確切最大速率在很大程度上取決於工作負載。因素包括寫入速率、請求之間的爭用以及受影響的索引數。
文檔寫入操作會更新文檔和任何關聯的索引,而 Cloud Firestore 會在一定數量的副本中同步應用寫入操作。在足夠高的寫入速率下,數據庫將開始遇到爭用、更高的延遲或其他錯誤。
對狹窄文檔範圍的高讀取、寫入和刪除率
避免按字典順序關閉文檔的高讀取或寫入速率,否則您的應用程序將遇到爭用錯誤。此問題稱為熱點問題,如果您的應用程序執行以下任一操作,它可能會遇到熱點問題:
以非常高的速率創建新文檔並分配其自己的單調遞增 ID。
Cloud Firestore 使用分散算法分配文檔 ID。如果您使用自動文檔 ID 創建新文檔,則不應遇到寫熱點。
在文檔很少的集合中以高速率創建新文檔。
以非常高的速率創建具有單調遞增字段(如時間戳)的新文檔。
以高速率刪除集合中的文檔。
以非常高的速率寫入數據庫,而不會逐漸增加流量。
避免跳過已刪除的數據
避免跳過最近刪除的數據的查詢。如果最近刪除了早期查詢結果,則查詢可能不得不跳過大量索引條目。
可能必須跳過大量已刪除數據的工作負載的一個示例是嘗試查找最舊的排隊工作項。查詢可能類似於:
docs = db.collection('WorkItems').order_by('created').limit(100)
delete_batch = db.batch()
for doc in docs.stream():
finish_work(doc)
delete_batch.delete(doc.reference)
delete_batch.commit()
每次運行此查詢時,它都會掃描所有最近刪除的文檔上created
字段的索引條目。這會減慢查詢速度。
要提高性能,請使用start_at
方法找到最佳起點。例如:
completed_items = db.collection('CompletionStats').document('all stats').get()
docs = db.collection('WorkItems').start_at(
{'created': completed_items.get('last_completed')}).order_by(
'created').limit(100)
delete_batch = db.batch()
last_completed = None
for doc in docs.stream():
finish_work(doc)
delete_batch.delete(doc.reference)
last_completed = doc.get('created')
if last_completed:
delete_batch.update(completed_items.reference,
{'last_completed': last_completed})
delete_batch.commit()
注意:上面的示例使用單調遞增的字段,這是高寫入率的反模式。
增加流量
您應該逐漸增加新集合的流量或按字典順序關閉文檔,以便 Cloud Firestore 有足夠的時間為增加的流量準備文檔。我們建議從每秒最多 500 次操作開始到一個新集合,然後每 5 分鐘將流量增加 50%。您可以類似地增加寫入流量,但請記住Cloud Firestore 標準限制。確保操作在整個鍵範圍內相對均勻地分佈。這稱為“500/50/5”規則。
將流量遷移到新集合
如果您將應用程序流量從一個集合遷移到另一個集合,則逐漸增加尤為重要。處理這種遷移的一種簡單方法是從舊集合中讀取,如果文檔不存在,則從新集合中讀取。但是,這可能會導致按字典順序關閉新集合中的文檔的流量突然增加。 Cloud Firestore 可能無法有效地為增加的流量準備新集合,尤其是當它包含的文檔很少時。
如果更改同一集合中許多文檔的文檔 ID,則會出現類似的問題。
將流量遷移到新集合的最佳策略取決於您的數據模型。下面是一個稱為並行讀取的示例策略。您需要確定此策略對您的數據是否有效,一個重要的考慮因素是遷移期間並行操作的成本影響。
並行讀取
要在將流量遷移到新集合時實施並行讀取,請先從舊集合中讀取。如果文檔丟失,則從新集合中讀取。不存在文檔的高讀取率會導致熱點,因此請務必逐漸增加新集合的負載。更好的策略是將舊文檔複製到新集合,然後刪除舊文檔。逐漸增加並行讀取,以確保 Cloud Firestore 可以處理流向新集合的流量。
逐漸增加對新集合的讀取或寫入的一種可能策略是使用用戶 ID 的確定性哈希來選擇隨機百分比的嘗試寫入新文檔的用戶。確保用戶 ID 散列的結果不會因您的函數或用戶行為而發生偏差。
同時,運行批處理作業,將所有數據從舊文檔複製到新集合。您的批處理作業應避免寫入順序文檔 ID,以防止出現熱點。批處理作業完成後,您只能從新集合中讀取。
此策略的改進是一次遷移小批量用戶。向用戶文檔添加一個字段,用於跟踪該用戶的遷移狀態。根據用戶 ID 的哈希選擇一批要遷移的用戶。使用批處理作業為該批用戶遷移文檔,並在遷移過程中為用戶使用並行讀取。
請注意,除非您在遷移階段對新舊實體進行雙重寫入,否則您無法輕鬆回滾。這會增加產生的 Cloud Firestore 成本。
防止未經授權的訪問
使用 Cloud Firestore 安全規則防止對您的數據庫進行未經授權的操作。例如,使用規則可以避免惡意用戶重複下載整個數據庫的情況。
詳細了解如何使用 Cloud Firestore 安全規則。