建構使用 Cloud Firestore 的應用程式時,請參考這裡列出的最佳做法。
資料庫位置
建立資料庫執行個體時,請選取最靠近使用者和運算資源的資料庫位置。遠距離的網路躍點更容易出錯,且會增加查詢延遲時間。
如要盡可能提高應用程式的可用性和耐用性,請選取多區域位置,並將重要運算資源放在至少兩個區域。
如果應用程式對延遲時間較為敏感,請選取區域位置,以降低成本、縮短寫入延遲時間,或與其他 GCP 資源共置。
文件 ID
- 避免使用文件 ID
.和..。 - 請勿在文件 ID 中使用
/斜線。 請勿使用單調遞增的文件 ID,例如:
Customer1、Customer2、Customer3、…Product 1、Product 2、Product 3、…
這類依序 ID 可能會導致熱點,進而影響延遲時間。
欄位名稱
請避免在欄位名稱中使用下列字元,因為這些字元需要額外逸出:
.句號[左括號]右括號*星號`個倒引號
索引
縮短寫入延遲時間
造成寫入延遲的主要原因是索引展開。減少索引展開的最佳做法如下:
索引豁免項目
對於大多數應用程式,您可以依賴自動索引以及任何錯誤訊息連結來管理索引。不過,在下列情況下,您可能需要新增單一欄位豁免:
| 案件 | 說明 |
|---|---|
| 大型字串欄位 | 如果字串欄位經常包含長字串值,且您不會用於查詢,可以將該欄位設為不建立索引,藉此降低儲存空間成本。 |
| 對含有連續值文件的集合執行高寫入率 | 如果為集合中文件之間依序遞增或遞減的欄位 (例如時間戳記) 建立索引,則集合的最大寫入速率為每秒 500 次寫入。如果不是根據具有連續值的欄位進行查詢,可以將該欄位排除在索引之外,藉此略過這項限制。 舉例來說,在寫入率偏高的 IoT 用途,如果集合中的文件含有時間戳記欄位,寫入率可能會接近每秒 500 次的上限。 |
| 存留時間欄位 |
如果您使用存留時間 (TTL) 政策,請注意 TTL 欄位必須是時間戳記。系統預設會啟用存留時間欄位的索引功能,這可能會影響高流量時的效能。最佳做法是為存留時間欄位新增自動索引豁免項目。 |
| 大型陣列或對應欄位 | 大型陣列或對應欄位可能會接近每份文件 40,000 個索引項目的限制。如果不是根據大型陣列或對應欄位進行查詢,請將其排除在索引之外。 |
讀取和寫入作業
應用程式更新單一文件的確切頻率上限,主要取決於工作負載。詳情請參閱「更新單一文件」。
盡可能使用非同步呼叫,而非同步呼叫。非同步呼叫可盡量減少延遲時間的影響。舉例來說,假設某個應用程式需要文件查詢結果和查詢結果,才能轉譯回應。如果查詢和查詢之間沒有資料依賴關係,就不需要同步等待查詢完成,再啟動查詢。
不可使用位移,請改用游標。使用偏移量只能避免將略過的文件傳回應用程式,但這些文件仍會在內部擷取。略過的文件會影響查詢延遲時間,且應用程式會針對擷取文件所需的讀取作業付費。
交易重試
Cloud Firestore SDK 和用戶端程式庫會自動重試失敗的交易,以處理暫時性錯誤。如果應用程式是直接透過 REST 或 RPC API 存取 Cloud Firestore,而非透過 SDK,則應實作交易重試機制,提高可靠性。
即時更新
如需即時更新的最佳做法,請參閱「瞭解大規模即時查詢」。
資源調度設計
下列最佳做法說明如何避免發生爭用問題。
更新單一文件
設計應用程式時,請考量應用程式更新單一文件的速度。如要瞭解工作負載的效能,最好的方法是執行負載測試。應用程式更新單一文件的確切頻率上限,高度取決於工作負載。這些因素包括寫入率、要求之間的爭用情形,以及受影響的索引數量。
文件寫入作業會更新文件和任何相關聯的索引,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 專案 ID 中。專案結束後,專案 ID 仍有可能保留下來。
- 為遵循資料法規,建議您不要在文件名稱和文件欄位名稱中儲存機密資訊。
防範未經授權的存取行為
使用 Cloud Firestore Security Rules,防止未經授權的資料庫作業。舉例來說,使用規則可以避免惡意使用者重複下載整個資料庫。
進一步瞭解如何使用 Cloud Firestore Security Rules。