最佳做法

建構使用 Cloud Datastore 的應用程式時,您可以快速參考本文章所列的最佳做法;如果您剛開始使用 Cloud Datastore,或許不適合參考本頁面,因為這裡不說明使用 Cloud Datastore 的基本概念。我們建議新的使用者先參閱開始使用 Cloud Datastore 一文。

一般

  • 命名空間名稱、種類名稱、屬性名稱及自訂索引鍵名稱一律採用 UTF-8 字元。這類名稱如使用非 UTF-8 字元,可能會干擾 Cloud Datastore 的功能。例如,屬性名稱若含非 UTF-8 字元,可能無法建立使用該屬性的索引。
  • 種類名稱或自訂索引鍵名稱不可使用正斜線 (/)。這類名稱如使用正斜線,可能會干擾未來的功能。
  • 避免將機密資訊儲存在 Cloud 專案 ID 中。專案結束後,專案 ID 仍有可能保留下來。

API 呼叫

  • 讀取、寫入及刪除應採用批次作業而非單一作業。批次作業的效率較高,原因在於這類作業能夠執行多項和單一作業負擔相同的作業。
  • 交易失敗,請務必復原交易。復原作業能夠盡量避免重試交易中另一個競爭相同資源的要求時出現延遲情況。請注意,復原本身也有可能失敗,因此嘗試復原時只需盡力而為。
  • 盡可能以非同步呼叫取代同步呼叫。非同步呼叫可以盡可能降低延遲造成的衝擊。以需要同步 lookup() 及查詢結果才能顯示回應的應用程式為例。若 lookup() 及查詢不具資料相依關係,就不需要同步等待 lookup() 完成再開始查詢。

實體

  • 將高度相關資料歸類為實體群組。實體群組能夠進行祖系查詢,這類查詢會傳回具同步一致性的結果。祖系查詢也能在運用最少 I/O 的前提下快速掃描實體群組,因為實體群組中的實體均實際儲存在 Cloud Datastore 伺服器的附近。
  • 寫入實體群組的頻率避免超過每秒一次。若寫入頻率長時間高於這個限制,可能會強化最終一致性讀取的最終程度,導致同步一致性讀取發生逾時情形,使得應用程式的整體效能變慢。這項限制只將一次實體群組批次或交易寫入作業列為單一寫入。
  • 同一次修訂不要重複包含同一個實體 (以索引鍵為準)。同一次修訂如重複包含同一個實體多次,可能會影響 Cloud Datastore 延遲時間。

索引鍵

  • 建立實體時若未提供索引鍵名稱,會由系統自動產生。索引鍵名稱的分配方式是平均散佈於索引鍵空間。
  • 如使用自訂索引鍵名稱,請一律使用 UTF-8 字元,但勿使用正斜線 (/)。非 UTF-8 字元會干擾多項處理序,例如將 Cloud Datastore 備份匯入 Google BigQuery。正斜線可能會干擾未來的功能。
  • 索引鍵若使用數字 ID:
    • ID 不可使用負數。負數 ID 可能會干擾排序功能。
    • ID 不可使用 0 (零) 值,否則會採用自動分配的 ID。
    • 如要自行為您所建立的實體手動指派數字 ID,請使用 allocateIds() 方法設定讓應用程式取得 ID 區塊。這樣能夠避免 Cloud Datastore 將其中一個數字 ID 指派給另一個實體。
  • 若自行為您所建立的實體手動指派數字 ID 或自訂名稱,請勿使用單純增加的數值,例如:

    1, 2, 3, …,
    "Customer1", "Customer2", "Customer3", ….
    "Product 1", "Product 2", "Product 3", ….
    

    若應用程式產生的流量偏大,此類連續編號方式可能會導致資源使用率不均,因而影響 Cloud Datastore 延遲時間。為避免連續數字 ID 造成問題,請使用 allocateIds() 方法取得數字 ID。allocateIds() 方法會產生均勻分佈的連續數字 ID。

  • 之後,只要指定索引鍵或儲存系統產生的名稱,不需發出尋找實體的查詢亦可針對該實體執行一致的 lookup()

索引

  • 若查詢不再需要某項屬性,可以將屬性排除於索引之外。若建立不需要的屬性索引,可能會拉長達成一致性的延遲時間,也可能會增加索引項目的儲存成本
  • 避免過多的複合式索引。過度使用複合式索引可能會拉長達到一致性的延遲時間,也可能會增加索引項目的儲存成本。如需臨時查詢大型資料集,但未事先定義索引,請使用 Google BigQuery
  • 不可為含單純增加值的屬性 (例如 NOW() 時間戳記) 建立索引。若應用程式的讀取率和寫入率較高,保留此類索引可能會造成資源使用率不均,因而影響 Cloud Datastore 延遲時間。如需有關處理單純屬性的進一步協助,請參閱下方的高速讀取/寫入小範圍索引鍵

屬性

  • 類型字串屬性一律使用 UTF-8 字元。類別字串屬性如使用非 UTF-8 字元,可能會干擾查詢。如需使用非 UTF-8 字元儲存資料,請改用位元組字串
  • 屬性名稱不可使用句點 (.)。屬性名稱若使用句點,未來建立結構化屬性的索引時可能會發生干擾。

查詢

  • 若只需存取查詢結果的索引鍵,請使用純索引鍵查詢。純索引鍵查詢傳回結果的延遲機率較低,成本也低於擷取整個實體。
  • 如只需存取實體的特定屬性,請使用投影查詢。投影查詢傳回結果的延遲機率較低,成本也低於擷取整個實體。
  • 同樣地,如只需存取包含在查詢篩選條件中的屬性 (例如列在 order by 子句中的屬性),請使用投影查詢
  • 不可使用位移,需改用游標。使用位移只能避免將略過的實體傳回應用程式中,但是系統仍會在內部擷取這些實體。略過的實體會影響查詢延遲時間,應用程式擷取這類實體時必須進行的讀取作業皆須計費。
  • 您的查詢若需維持同步一致性,請使用祖系查詢 (若要使用祖系查詢,需先建立能達到同步一致性的資料結構)。祖系查詢會傳回具同步一致性的結果。請注意,若在非祖系的純索引鍵查詢之後執行 lookup(),不會傳回同步一致性結果,這是因為非祖系純索引鍵查詢可以在查詢時並不一致的索引中得到結果。

資源調度設計

更新單一實體群組

Cloud Datastore 中單一實體群組的更新速度不宜過快。

若使用 Cloud Datastore,Google 建議您將應用程式設計為更新實體群組的速度不超過每秒一次。請記住,沒有父項及子項的實體自成一個實體群組。若更新實體群組的速度過快,Cloud Datastore 寫入作業會發生延遲、逾時以及其他錯誤類型的機率會更高。這就是所謂的爭用現象。

Cloud Datastore 寫入單一實體群組的速度有時可能會超過每秒一次的上限,因此,載入測試不見得能夠彰顯這個問題。如需關於設計應用程式時如何降低實體群組寫入速度的建議,請參閱 Cloud Datastore 爭用現象一文

高速讀取/寫入小範圍索引鍵

請避免高速讀取或寫入字母順序接近的 Cloud Datastore 索引鍵。

Cloud Datastore 的建構基礎是 Google 的 NoSQL 資料庫 Bigtable,必須以 Bigtable 的效能特性為準。Bigtable 會將資料列分割成獨立的子表,以此進行資源調度,而且資料列索引鍵的字母排列順序十分接近。

使用 Cloud Datastore 時若突然提高小範圍索引鍵的寫入速度,並且超出單一子表伺服器的產能,可能會導致子表資源使用率不均,寫入速度反而變慢。Bigtable 終究會為了支援高載入而分割索引鍵空間。

讀取上限通常比寫入上限高得多,除非您是以高速讀取單一索引鍵。Bigtable 最多只能將單一索引鍵分割成一個子表。

資源使用率不均的子表適用於實體索引鍵和索引所用的索引鍵範圍。

在某些情況下,Cloud Datastore 資源使用率不均現象對應用程式造成的影響可能不僅止於妨礙讀取或寫入小範圍索引鍵。例如,執行個體啟動時可能會寫入或讀取熱鍵,導致載入要求失敗。

根據預設,Cloud Datastore 會使用離散演算法分配索引鍵。因此,若使用預設 ID 配置規則以高速寫入方式建立新的實體,執行 Cloud Datastore 寫入作業時通常不會發生資源使用率不均現象。有些極端情況可能會發生這種問題:

  • 您使用舊版的依序配置 ID 政策,以極高的速度建立新實體。

  • 您以極高的速度建立新的實體,同時自行配置單純增加的 ID。

  • 您以極高的速度建立新的實體,其種類原本有非常少的現有實體。Bigtable 會先從同一個子表伺服器中的所有實體開始,花時間將某個範圍的索引鍵分割至不同的子表伺服器。

  • 若您高速建立含單純增加索引屬性 (如時間戳記) 的新實體,也會發生這種問題,因為這些屬性在 Bigtable 中是索引資料表的索引鍵。

  • Cloud Datastore 會在 Bigtable 資料列索引鍵之前加上根實體群組的名稱空間及種類。若開始寫入新的命名空間或種類,而未逐漸增加流量,可能會發生資源使用率不均現象。

若您的索引鍵或索引屬性將單純增加,您可以在前面加上隨機雜湊碼,確保能夠將索引鍵分割為多個子表。

同樣地,如需使用排序或篩選方式查詢單純增加 (或減少) 的屬性,可以改為新的屬性建立索引,再於單純增加的值之前加上一個在整個資料集均採用高基數的值 (但只要符合您要執行查詢的範圍,其中所有實體皆可共用這個屬性)。例如,若您想要依時間戳記查詢項目,但一次只需要傳回一位使用者的結果,就可以在使用者 ID 前加上時間戳記,然後改為這個新屬性建立索引。這樣您仍然能夠進行查詢,也能排序該名使用者的結果,但由於加上了使用者 ID,因此更能確定可以順利分割索引本身。

如需有關這個問題的詳細說明,請參閱 Ikai Lan 探討在 Cloud Datastore 中儲存單純增加值的網誌文章

增加流量

逐漸增加新 Cloud Datastore 種類或索引鍵空間部分的流量。

您應該逐漸增加新 Cloud Datastore 種類的流量,讓 Bigtable 有充裕的時間能夠隨著流量增加而分割子表。建議新 Cloud Datastore 種類的流量不要超過每秒 500 項作業,接著每 5 分鐘增加 50% 的流量。理論上,按照這樣的增加時間表執行 90 分鐘後,就能擴充到 740K 項作業。整個索引鍵範圍內的寫入作業一定要相當均勻。我們的 SRE 將這稱為「500/50/5」規則。

若您為了停止使用 A 種類並改用 B 種類而變更程式碼,這種逐漸增加的模式特別重要。要處理這樣的移轉作業,可以透過一種原始方式,那就是將程式碼改為讀取 B 種類,若 B 種類不存在,才讀取 A 種類。不過,這樣會導致局部索引鍵空間的新種類流量突然增加。若索引鍵空間稀疏,使用 Bigtable 分割子表可能效率不佳。

若您將實體移轉至同一個種類但不同範圍的索引鍵,可能也會發生一樣的問題。

您將實體移轉至新種類或索引鍵時所採用的策略,應取決於您的資料模型。以下所舉的策略範例稱為「平行讀取」。您需要判斷這項策略是否適用於您的資料。平行作業在移轉階段對成本造成的影響會是相當重要的考量因素。

請先讀取舊的實體或索引鍵。若沒有舊的實體或索引鍵,可以讀取新的實體或索引鍵。高速讀取不存在的實體可能會造成資源使用率不均現象,因此請務必逐漸增加負載。先將舊實體複製到新實體再刪除舊實體,會是比較好的策略。逐漸增加平行讀取,確定新的索引鍵空間分割情況良好。

要逐漸增加新種類的讀取或寫入,可行策略之一是使用決定性的使用者 ID 雜湊隨機取得寫入新實體的使用者百分比。一定要避免隨機函式或使用者行為扭曲使用者 ID 雜湊結果。

同時,請執行 Dataflow 工作,將舊實體或索引鍵中的資料全數複製到新實體或索引鍵。批次工作應避免寫入連續索引鍵,以利預防 Bigtable 資源使用率不均。批次工作完成後,您就只能讀取新的位置。

這個策略的進階版是一次只移轉一小批使用者。在使用者實體中新增一個欄位,用於追蹤該名使用者的移轉狀態。根據使用者 ID 的雜湊碼選取一批使用者。Mapreduce 或 Dataflow 工作會移轉這一批使用者的索引鍵。正在進行移轉的使用者將使用平行讀取。

請注意,除非您在移轉階段同時寫入新舊實體,否則無法輕易復原。這樣會增加 Cloud Datastore 成本。

刪除項目

避免刪除小範圍索引鍵內的大量 Datastore 實體。

Bigtable 會定期重新寫入其資料表,以利移除已經刪除的項目與重新排列資料,藉此提高讀取和寫入的效率。這個處理序稱為壓縮。

若您大量刪除小範圍索引鍵內的 Cloud Datastore 實體,則在壓縮完成之前,查詢此部分索引的速度就會變慢。在極罕見的情況下,查詢作業可能會在傳回結果之前逾時。

這種情況與在索引欄位使用時間戳記值代表實體到期時間背道而馳。為擷取過期的實體,您需要查詢這個索引欄位,而這個欄位可能存在於索引鍵空間與最近刪除實體之索引項目重疊的部分。

「資料分割查詢」會在時間戳記前加上固定長度的字串,您可以利用這類查詢提升效能。索引的依據整個字串進行排序,因此可以找到整個索引鍵範圍內屬於相同時間戳記的實體。您需同時執行多項查詢,以擷取每一項資料分割作業的結果。

要解決到期時間戳記問題,更全面的解決方案是使用「產生號碼」,這是一種會定期更新的全域計數器。系統會將產生號碼加在到期時間戳記之前,以利依產生號碼排列查詢順序,接著依序是分割資料和時間戳記。刪除舊實體的作業會在前一次產生時發生。凡未刪除的實體,產生號碼都要遞增。刪除完成後,就繼續進行下一次產生作業。若在完成壓縮前查詢舊的產生作業,效能會較差。您可能需要等待完成數次產生作業之後,才能查詢索引並取得待刪除實體的清單,這是為了降低最終不一致遺漏結果的風險。

資料分割與備用資源

使用資料分割或備用資源處理 Cloud Datastore 熱鍵。

讀取某個索引鍵範圍的速度如需超出 Bigtable 的限制,可以使用備用資源。這種策略能夠儲存同一個實體的 N 個複本,讀取速度也能比單一實體所支援的速度高出 N 倍。

寫入某個索引鍵範圍的速度如需超出 Bigtable 的限制,則可使用資料分割。資料分割會將實體分成數個較小的片段。原則說明請參閱資料分割計數器一文。

資料分割時的常見錯誤如下:

  • 以時間為字首進行資料分割。若時間前進到下一個字首,則新的未分割部分就會發生資源使用率不均現象。您應該改為逐漸為一部分寫入增加新的字首。

  • 只針對資源使用率不均現象最嚴重的實體進行資料分割。若分割實體總數的其中一小部分,資源使用率不均實體之間的資料列可能不足,因而無法確認這些實體是否屬於不同的分割資料。

後續步驟

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Cloud Datastore 說明文件