結構定義設計最佳做法

有效的 Bigtable 結構定義可讓您盡可能提高讀寫處理量、避免熱點,並有效率地擴充儲存空間。開始之前,請先參閱 Bigtable 總覽,瞭解基礎架構和儲存模型。

基本概念

設計 Bigtable 結構定義與設計關聯式資料庫結構定義不同。Bigtable 結構定義是由應用程式邏輯定義,而非結構定義定義物件或檔案。您可以在建立或更新資料表時,將資料欄系列新增至資料表,但資料欄和資料列索引鍵模式是由您寫入資料表的資料定義。

在 Bigtable 中,結構定義是資料表的藍圖或模型,包含下列資料表元件的結構:

  • 資料列索引鍵
  • 資料欄系列,包括垃圾收集政策
  • 資料欄

在 Bigtable 中,結構定義設計主要取決於您打算傳送至資料表的查詢或讀取要求。讀取資料列範圍是讀取 Bigtable 資料最快的方式,因此本頁建議旨在協助您最佳化資料列範圍讀取。在多數情況下,這代表要根據資料列索引鍵前置字串傳送查詢。

次要考量是避免熱點。如要避免熱點,您必須考量寫入模式,以及如何避免在短時間內存取小型金鑰空間。

Bigtable 結構定義設計適用下列一般概念:

  • Bigtable 是鍵/值儲存庫,而非關聯式儲存庫。不支援彙整,且僅支援單一資料列內的交易。
  • 每個資料表都有一個索引:資料列索引鍵。每個資料列索引鍵都不得重複。如要建立次要索引,請使用持續性 materialized view。詳情請參閱「建立非同步次要索引」。
  • 資料列索引鍵會依字典順序排序資料列,從最低到最高的位元組字串。這個順序是大端序 (有時稱為網路位元組順序),相當於字母順序的二進位表示法。
  • 資料欄系列不會依任何特定順序儲存。
  • 資料欄會依資料欄系列分組,並在資料欄系列中依字典順序排序。舉例來說,在名為 SysMonitor 的資料欄系列中,資料欄限定詞為 ProcessName、User、%CPU、ID、Memory、DiskRead 和 Priority,Bigtable 會依下列順序儲存資料欄:
SysMonitor
%CPU DiskRead ID 記憶體 優先順序 ProcessName 使用者
  • 資料列與資料欄的交集可以包含多個時間戳記儲存格。每個儲存格都包含該資料列和資料欄的資料,並附有不重複的時間戳記版本。
  • 匯總資料欄系列包含匯總儲存格。您可以建立只包含匯總儲存格的資料欄系列。匯總可讓您將新資料與儲存格中既有的資料合併。
  • 所有作業在資料列層級都是不可部分完成的。作業會影響整列或完全不影響。
  • 理想情況下,讀取和寫入作業應平均分布在資料表的資料列空間中。
  • Bigtable 資料表空間稀疏。如果資料列未使用某個資料欄,該資料欄就不會占用該資料列的任何空間。

最佳做法

良好的結構定義可帶來出色的效能和擴充性,而設計不佳的結構定義則可能導致系統效能不佳。每個用途都不同,需要各自的設計,但下列最佳做法適用於大多數用途。如有例外狀況,我們會另外註明。

以下各節將從資料表層級開始,逐步說明資料列索引鍵層級的結構定義設計最佳做法:

設計所有資料表元素 (尤其是資料列索引鍵) 時,應考量預計的讀取要求。請參閱配額和限制,瞭解所有資料表元素的建議和硬性大小限制。

由於執行個體中的所有資料表都儲存在相同的資料表上,因此如果結構定義設計導致某個資料表出現熱點,可能會影響同一執行個體中其他資料表的延遲時間。熱點是由於在短時間內頻繁存取資料表的某個部分所造成。

Tables

將具有類似結構定義的資料集儲存在同一個資料表,而不是分開儲存。

在其他資料庫系統中,您可能會選擇根據主題和資料欄數量,將資料儲存在多個資料表中。不過,在 Bigtable 中,通常最好將所有資料儲存在一個資料表中。您可以為每個資料集指派專屬的資料列索引鍵前置字串,這樣 Bigtable 就會將相關資料儲存在連續的資料列範圍內,您也可以依資料列索引鍵前置字串查詢。

Bigtable 每個執行個體最多可有 1,000 個資料表,但基於下列原因,建議您避免建立大量資料表:

  • 如果將要求傳送至許多不同的資料表,可能會增加後端連線負擔,導致尾端延遲時間增加。
  • 建立更多資料表並不會改善負載平衡,反而可能增加管理負擔。

如果其他用途需要不同的結構定義,您可能需要使用不同的資料表,但請勿針對類似資料使用不同的資料表。舉例來說,您不應因為新的一年到來或有新客戶,就建立新資料表。

資料欄系列

將相關資料欄放在同一個資料欄系列中。如果資料列包含多個彼此相關的值,建議將包含這些值的資料欄分組到同一個資料欄系列。盡可能將資料分組,避免需要設計複雜的篩選器,這樣您就能在最頻繁的讀取要求中,只取得所需的資訊。

每個資料表最多可建立約 100 個資料欄系列。建立超過 100 個資料欄系列可能會導致效能降低。

為資料欄系列選擇簡稱。每次要求傳輸的資料都會包含名稱。

將資料保留需求不同的資料欄,放在不同的資料欄系列中。如要限制儲存空間費用,這項做法相當重要。垃圾收集政策是在資料欄系列層級設定,而非資料欄層級。舉例來說,如果您只需要保留特定資料的最新版本,請勿將其儲存在設定為儲存 1,000 個其他版本的資料欄系列中。否則,您就必須支付 999 個不必要資料儲存格的費用。

資料欄

在表格中建立所需數量的資料欄。Bigtable 資料表空間稀疏,如果資料列中未使用某個資料欄,也不會佔用空間。資料表可以有數百萬個資料欄,只要每列不超過 256 MB 的上限即可。

避免在單一資料列中使用過多資料欄。雖然資料表可以有數百萬個資料欄,但資料列不應如此。這項最佳做法有幾個考量因素:

  • Bigtable 需要時間處理資料列中的每個儲存格。
  • 每個儲存格都會增加資料表儲存及透過網路傳送的資料量。舉例來說,如果您要儲存 1 KB (1,024 位元組) 的資料,將資料儲存在單一儲存格中,會比將資料分散在 1,024 個儲存格 (每個儲存格包含 1 個位元組) 中更節省空間。

如果資料集在邏輯上需要每列包含更多資料欄,超出 Bigtable 能有效處理的範圍,建議您將資料儲存為單一資料欄中的 protobuf。

您也可以選擇將資料欄限定詞視為資料。由於您必須為每個資料欄儲存資料欄限定詞,因此可以為資料欄命名一個值來節省空間。舉例來說,假設某個資料表在 Friends 資料欄系列中儲存好友關係資料。每一列代表一個人及其所有好友關係。每個資料欄限定詞都可以是好友的 ID。然後,該列中每個資料欄的值可以是朋友所在的社交圈。在這個範例中,資料列可能如下所示:

資料列索引鍵 Fred Gabriel Hiroshi 書允 Jakob
Jose book-club 公司 網球
索非亞 公司 school chess-club

請將這個結構定義與相同資料的結構定義進行比較,後者不會將資料欄限定詞視為資料,而是在每個資料列中都有相同的資料欄:

資料列索引鍵 好友 圓形
Jose#1 Fred book-club
Jose#2 Gabriel 公司
Jose#3 Hiroshi 網球
Sofia#1 Hiroshi 公司
Sofia#2 書允 school
Sofia#3 Jakob chess-club

第二種結構定義設計會導致資料表成長速度更快。

如果您使用資料欄限定詞儲存資料,請為資料欄限定詞指定簡短但有意義的名稱。這種做法可減少每次要求傳輸的資料量。大小上限為 16 KB。

資料列

單一資料列中所有值的大小不得超過 100 MB。請確認單一資料列中的資料不超過 256 MB。如果資料列超過這個限制,讀取效能可能會降低。

將實體的所有資訊儲存在單一資料列。在大多數用途中,請避免將必須以不可分割方式 (或一次性) 讀取的資料儲存在多個資料列中,以免發生不一致的情況。舉例來說,如果您更新資料表中的兩個資料列,其中一個資料列可能會更新成功,另一個更新則會失敗。請確認結構定義不需要同時更新多個資料列,就能確保相關資料正確無誤。這麼做可確保如果部分寫入要求失敗或必須重新傳送,該資料不會暫時不完整。

例外狀況:如果將實體保留在單一資料列中,會導致資料列大小達到數百 MB,則應將資料分割到多個資料列。

將相關實體儲存在相鄰的資料列中,提高讀取效率。

生產單元

請勿在單一儲存格中儲存超過 10 MB 的資料。請注意,儲存格是為特定資料列和資料欄儲存的資料,具有不重複的時間戳記,且該資料列和資料欄的交集處可以儲存多個儲存格。資料欄中保留的儲存格數量,取決於您為包含該資料欄的資料欄系列設定的垃圾收集政策。

使用匯總儲存格儲存及更新匯總資料。如果您只關心實體的事件匯總值,例如零售商店每位員工的每月銷售總額,則可以使用匯總。詳情請參閱「在寫入時匯總值」。

資料列索引鍵

根據要用來擷取資料的查詢,設計資料列索引鍵。妥善設計資料列索引鍵,可讓 Bigtable 發揮最佳效能。在 Bigtable,最有效率的查詢會使用下列其中一種方法擷取資料:

  • 資料列索引鍵
  • 資料列索引鍵前置字串
  • 由起始和結束資料列索引鍵定義的資料列範圍

其他類型的查詢會觸發完整資料表掃描,效率會低得多。現在選擇正確的資料列索引鍵,日後就能避免痛苦的資料遷移程序。

資料列索引鍵應簡短。資料列索引鍵不得超過 4 KB。資料列索引鍵過長會占用額外的記憶體和儲存空間,並增加從 Bigtable 伺服器取得回應所需的時間。

在每個資料列索引鍵中儲存多個以分隔符號區隔的值。由於有效率查詢 Bigtable 的最佳方式是透過資料列索引鍵,因此在資料列索引鍵中加入多個 ID 通常很有用。如果資料列索引鍵包含多個值,請務必清楚瞭解資料的使用方式。

資料列索引鍵區隔通常會以分隔符號分隔,例如冒號、斜線或井字號。第一個區隔或一組連續區隔是資料列索引鍵前置字串,最後一個區隔或一組連續區隔則是資料列索引鍵後置字串。

資料列索引鍵範例

妥善規劃資料列索引鍵前置字元,即可利用 Bigtable 的內建排序順序,將相關資料儲存在連續的資料列中。將相關資料儲存在連續的資料列中,即可將相關資料做為資料列範圍存取,而不必執行效率不彰的資料表掃描。

如果資料包含要以數字形式儲存或排序的整數,請在整數前面補零。Bigtable 會依字典順序儲存資料。例如,按照字典順序,3 > 20,但 20 > 03。在 3 前面加上零,可確保數字依數值排序。如果時間戳記會用於範圍查詢,這項策略就相當重要。

請務必建立資料列索引鍵,以便擷取定義明確的資料列範圍。否則,查詢會需要掃描資料表,這比擷取特定資料列慢得多。

舉例來說,如果應用程式會追蹤行動裝置資料,您可以設定由裝置類型、裝置 ID 和資料記錄日期組成的資料列索引鍵。這項資料的資料列索引鍵可能如下所示:

        phone#4c410523#20200501
        phone#4c410523#20200502
        tablet#a0b81f74#20200501
        tablet#a0b81f74#20200502

這種資料列索引鍵設計可讓您透過單一要求擷取下列資料:

  • 裝置類型
  • 裝置類型和裝置 ID 的組合

如要擷取特定日期的所有資料,這種資料列索引鍵設計就不是最佳做法。由於日期儲存在第三個區隔 (即資料列索引鍵後置字串) 中,因此您無法只根據後置字串或資料列索引鍵的中間區隔要求資料列範圍。而是必須傳送讀取要求,並使用篩選器掃描整個資料表,尋找日期值。

盡可能在資料列索引鍵中使用人類可讀的字串值。這樣一來,您就能更輕鬆地使用 Key Visualizer 工具排解 Bigtable 問題。

通常,您應該設計以一般值開頭、以精細值結尾的資料列索引鍵。舉例來說,如果資料列索引鍵包含洲、國家/地區和城市,您可以建立類似下列的資料列索引鍵,讓系統自動先依基數較低的值排序:

        asia#india#bangalore
        asia#india#mumbai
        asia#japan#osaka
        asia#japan#sapporo
        southamerica#bolivia#cochabamba
        southamerica#bolivia#lapaz
        southamerica#chile#santiago
        southamerica#chile#temuco

結構化資料列索引鍵

如果您打算使用 SQL 查詢資料表,可以定義結構化資料列索引鍵,這樣就能使用多部分索引鍵存取 Bigtable 資料,類似於關聯式資料庫中的複合索引鍵。為資料表定義結構化資料列索引鍵後,您就能使用 GoogleSQL for Bigtable 查詢,存取資料列索引鍵的特定區隔。

建立連續 materialized view 時,系統會自動建立結構化資料列索引鍵。如要為 Bigtable 資料表實作結構化資料列索引鍵,您可以建立資料列索引鍵結構定義,定義資料列索引鍵各部分的資料類型和編碼。Bigtable 會將資料列索引鍵儲存為依字母順序排序的位元組,而資料列索引鍵結構定義會指示 GoogleSQL for Bigtable 如何解碼及解讀這些位元組。

使用 Bigtable Data API ReadRows 方法和 Bigtable 用戶端程式庫查詢資料表時,系統會忽略資料列索引鍵結構定義。

詳情請參閱「管理資料列索引鍵結構定義」和「結構化資料列索引鍵查詢」。

避免使用資料列索引鍵的情況

某些類型的資料列索引鍵可能會導致資料難以查詢,有些則會導致效能不佳。本節說明應避免在 Bigtable 中使用的資料列索引鍵類型。

以時間戳記開頭的資料列索引鍵。這種模式會導致依序寫入作業推送至單一節點,產生熱點。如果您在資料列索引鍵中加入時間戳記,請在時間戳記前面加上高基數值 (例如使用者 ID),以免發生熱點。

導致相關資料無法分組的資料列索引鍵。請避免使用會導致相關資料儲存在不連續資料列範圍的資料列索引鍵,因為這樣會降低讀取效率。

依序數字 ID。假設系統為應用程式的每位使用者指派一組數字 ID。您可能會想使用使用者的數字 ID 做為資料表的資料列索引鍵。不過,由於新使用者較可能成為活躍使用者,因此這種做法可能會將大部分流量推送至少數節點。

較安全的做法是使用反向版本的使用者數字 ID,這樣就能將流量更平均地分散到 Bigtable 資料表的所有節點。

經常更新的 ID。避免使用單一資料列索引鍵來識別必須經常更新的值。舉例來說,如果您每秒儲存一次多部裝置的記憶體用量資料,請「不要」為每部裝置使用由裝置 ID 和儲存指標組成的單一資料列索引鍵 (例如 4c410523#memusage),並重複更新資料列。這類作業會使儲存常用資料列的 Tablet 超載。此外,資料欄的先前值會占用空間,直到垃圾收集期間移除儲存格為止,因此也可能導致資料列超出大小限制。

請改為將每筆新讀數儲存在新資料列中。以記憶體用量為例,每個資料列索引鍵都可以包含裝置 ID、指標類型和時間戳記,因此資料列索引鍵類似於 4c410523#memusage#1423523569918。這項策略很有效率,因為在 Bigtable 中,建立新資料列所需的時間不會超過建立新儲存格的時間。此外,這項策略可讓您計算適當的開始和結束鍵,快速讀取特定日期範圍內的資料。

如果值經常變更 (例如計數器每分鐘更新數百次),建議您將資料保留在應用程式層的記憶體中,並定期將新資料列寫入 Bigtable。

雜湊值。對資料列索引鍵進行雜湊處理後,您就無法利用 Bigtable 的自然排列順序,因此無法以最適合查詢的方式儲存資料列。基於相同原因,雜湊值會導致難以使用 Key Visualizer 工具排解 Bigtable 問題。請使用人類可讀的值,而非雜湊值。

以原始位元組表示的值,而非人類可讀的字串。原始位元組適用於資料欄值,但為了提高可讀性和排解疑難,請在資料列索引鍵中使用字串值。

特殊用途

您可能擁有獨特的資料集,在設計結構定義以將其儲存在 Bigtable 時,需要特別考量。本節說明部分 (而非全部) Bigtable 資料類型,並提供一些建議策略,協助您以最佳方式儲存資料。

時間資料

如果您經常根據資料記錄時間擷取資料,可以在資料列索引鍵中加入時間戳記。

舉例來說,應用程式可能會每秒記錄一次多部機器的效能相關資料,例如 CPU 和記憶體用量。這項資料的資料列索引鍵可以結合機器 ID 和資料時間戳記 (例如 machine_4223421#1425330757685)。請注意,資料列索引鍵會依字典順序排序。

如果您在資料列索引鍵中加入時間戳記,請勿單獨使用時間戳記,或在資料列索引鍵開頭使用時間戳記。這種模式會導致依序寫入作業推送至單一節點,進而產生熱點。

如果您通常會在查詢中先擷取最新記錄,可以考慮在資料列索引鍵中使用反向時間戳記。這個模式會將資料列從最新到最舊排序,因此較新的資料會排在資料表的前面。與任何時間戳記一樣,請避免以反向時間戳記開頭的資料列索引鍵,以免造成熱點。

如要取得反向時間戳記,請從程式設計語言的長整數最大值 (在 Java 中為 java.lang.Long.MAX_VALUE) 減去時間戳記。

如要瞭解如何處理時間序列資料,請參閱「時間序列資料的結構定義設計」。

多用戶群架構

資料列索引鍵前置字元可為「多用戶群」用途提供可擴充的解決方案,在這種情況下,您會代表多個用戶端,使用相同的資料模型儲存類似的資料。為所有租戶使用單一資料表,是儲存及存取多租戶資料最有效率的方式。

舉例來說,假設您代表許多公司儲存及追蹤購買記錄。您可以將每間公司的專屬 ID 做為資料列索引鍵前置字元。租戶的所有資料都會儲存在同一個資料表的連續資料列中,您可以使用資料列索引鍵前置字串進行查詢或篩選。當某公司不再是您的客戶,而您需要刪除為該公司儲存的購買記錄資料時,可以捨棄使用該客戶資料列索引鍵前置字串的資料列範圍。

舉例來說,如果您要儲存客戶 altostrat 和 examplepetstore 的行動裝置資料,可以建立如下的資料列索引鍵。接著,如果 altostrat 不再是您的客戶,您會捨棄資料列索引鍵前置字元為 altostrat 的所有資料列。

        altostrat#phone#4c410523#20190501
        altostrat#phone#4c410523#20190502
        altostrat#tablet#a0b41f74#20190501
        examplepetstore#phone#4c410523#20190502
        examplepetstore#tablet#a6b81f79#20190501
        examplepetstore#tablet#a0b81f79#20190502

相反地,如果您代表每家公司將資料儲存在各自的資料表中,可能會遇到效能和擴充性問題。此外,您也更有可能不小心達到 Bigtable 的限制,即每個執行個體最多 1,000 個資料表。達到這個上限後,Bigtable 就會禁止您在執行個體中建立更多資料表。

隱私權

除非用途需要,否則請避免在資料列索引鍵或資料欄系列 ID 中使用個人識別資訊 (PII) 或使用者資料。資料列索引鍵和資料欄系列中的值,同時屬於客戶資料和服務資料,而使用這些資料的應用程式 (例如加密或記錄) 可能會不小心將這些資料,公開給不應存取私人資料的使用者。

如要進一步瞭解服務資料的處理方式,請參閱《Google Cloud隱私權聲明》。

網域名稱

您可以將網域名稱儲存為 Bigtable 資料。

多種網域名稱

如果您儲存的實體資料可以表示為網域名稱,建議使用反向網域名稱 (例如 com.company.product) 做為資料列索引鍵。如果每列資料往往會與相鄰列重疊,則特別適合使用反向網域名稱。在這種情況下,Bigtable 可以更有效地壓縮資料。

相較之下,未反轉的標準網域名稱可能會導致資料列排序方式不當,相關資料無法集中在同一處,進而降低壓縮和讀取效率。

如果資料分散在許多不同的反向網域名稱中,這個方法最有效。

為說明這一點,請考慮下列網域名稱,Bigtable 會自動依字典順序排序:

      drive.google.com
      en.wikipedia.org
      maps.google.com

如果想查詢 google.com 的所有資料列,這並非理想情況。相較之下,請考慮網域名稱反轉的相同資料列:

      com.google.drive
      com.google.maps
      org.wikipedia.en

在第二個範例中,相關資料列會自動排序,方便您以資料列範圍的形式擷取。

網域名稱

如果您預期只會為一個或少數幾個網域名稱儲存大量資料,請考慮為資料列索引鍵使用其他值。否則,您可能會將寫入作業推送至叢集中的單一節點,導致熱點,或資料列可能過大。

以 protobuf 格式儲存資料

如要降低儲存空間成本並提高讀寫效率,您可以將相關資料分組為通訊協定緩衝區 (protobuf) 訊息,並儲存在單一資料欄中,而不是將每個欄位儲存在各自的資料欄中。如果您通常會同時讀取一組欄位,將這些欄位儲存在單一資料欄中做為一個值會更有效率,因為讀取單一資料欄的速度比讀取多個個別資料欄更快。如果將資料儲存為 protobuf 訊息,您可以使用下列 Bigtable 功能:

  • 使用通訊協定緩衝區儲存彈性資料結構,可將資料保留在原處並避免重複,進而降低儲存成本。
  • 將 protobuf 結構定義以結構定義套件的形式上傳至 Bigtable,即可啟用伺服器端查詢功能。Bigtable 可透過結構定義套件,在執行查詢時瞭解及剖析序列化資料的結構。
  • 使用 GoogleSQL 直接在 Bigtable 查詢及篩選 protobuf 訊息中的個別欄位,既快速又有效率。在伺服器端反序列化資料,即可執行精細的查詢和投影,不必將整個 protobuf 物件轉移至用戶端。
  • 使用 BigQuery 外部資料表分析儲存在 Bigtable 資料儲存庫中的 protobuf 資料,可簡化數據分析作業。

詳情請參閱「建立及管理 protobuf 結構定義」和「建立及管理 Avro 結構定義」(預先發布版)。

後續步驟