結構定義設計最佳做法

本頁提供 Bigtable 結構定義設計相關資訊。閱讀本頁之前,請先熟悉 Bigtable 總覽。本頁面涵蓋下列主題:

  • 一般概念:設計結構定義時應留意的基本概念。
  • 最佳做法:適用於大多數用途的設計指南,依資料表元件分類。
  • 特殊用途:針對特定用途和資料模式的建議。

基本概念

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

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

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

在 Bigtable 中,結構定義設計主要取決於您打算傳送至資料表的查詢或讀取要求。因為讀取資料列範圍是讀取 Bigtable 資料最快的方式,所以這個頁面的建議旨在協助您針對資料列範圍讀取作業進行最佳化。在大多數情況下,這表示要根據資料列鍵前置字元傳送查詢。

其次,請避免資源使用率不均現象。如要避免這種現象,您必須考量寫入模式,以及如何避免在短時間內存取一小範圍的鍵空間。

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

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

最佳做法

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

從表格層級開始,一路到資料列鍵層級,以下各節將說明結構定義設計的最佳做法:

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

由於執行個體中的所有資料表都儲存在同一個平板電腦上,因此如果結構定義導致某個資料表出現熱點,可能會影響同一執行個體中其他資料表的延遲。如果短時間內經常存取表格的某個部分,就會造成熱點。

資料表

將結構定義類似的資料集儲存在同一個資料表中,而不是個別的資料表。

在其他資料庫系統中,您可能會根據主題和欄數,選擇將資料儲存在多個資料表中。不過,在 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 Seo Yoon Jakob
Jose book-club 公司 網球
索非亞 公司 學校 chess-club

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

資料列索引鍵 好友 圓形
Jose#1 Fred book-club
Jose#2 Gabriel 公司
Jose#3 Hiroshi 網球
Sofia#1 Hiroshi 公司
Sofia#2 Seo Yoon 學校
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 存取 Bigtable 查詢的特定資料列鍵區隔。

建立連續具體化檢視區塊時,系統會自動建立結構化列鍵。如要為 Bigtable 資料表實作結構化資料列鍵,您可以建立資料列鍵結構定義,定義資料列鍵每個區段的資料類型和編碼。Bigtable 會將資料列索引鍵儲存為按字典順序排序的位元組,而資料列索引鍵結構定義會告訴 GoogleSQL for Bigtable 如何解碼及解讀這些位元組。

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

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

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

部分類型的資料列鍵可能會導致查詢資料時發生困難,並造成效能不佳。本節說明您應避免在 Bigtable 中使用的幾種資料列鍵。

以時間戳記開頭的資料列鍵。這種模式會導致循序寫入作業推送至單一節點,造成資源使用率不均。如果您在資料列鍵中加入時間戳記,請先加入高基數值 (例如使用者 ID),以免出現熱點。

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

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

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

經常更新的 ID。避免使用單一資料列鍵來識別必須經常更新的值。舉例來說,如果您每秒儲存多部裝置的記憶體用量資料,請「勿」為每部裝置使用單一資料列索引鍵,該索引鍵由裝置 ID 和儲存的指標組成,例如 4c410523#memusage,並重複更新該資料列。這類作業會過度負荷儲存常用資料列的平板電腦。此外,由於系統會在垃圾收集期間移除儲存格,因此欄的先前值會佔用空間,導致列超出大小限制。

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

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

雜湊值。雜湊處理資料列鍵後,您就無法再利用 Bigtable 的自然排序順序,也無法以最適合查詢的方式儲存資料列。基於相同原因,雜湊值會導致難以使用 Key Visualizer 工具排解 Bigtable 問題。請使用可讀取的值,不要使用雜湊值。

以原始位元組表示的值,而非人類可讀的字串。欄值使用原始位元組即可,但為了方便閱讀和進行疑難排解,請在資料列鍵中使用字串值。

特殊用途

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

以時間為準的資料

如果您經常根據資料記錄時間擷取資料,可以將時間戳記納入資料列鍵。

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

如果要在資料列索引鍵中加入時間戳記,請勿單獨使用時間戳記,或將時間戳記放在資料列索引鍵開頭。這種模式會導致循序寫入作業推送至單一節點,造成資源使用率不均。

如果您通常會在查詢中先擷取最近的記錄,可以考慮在資料列鍵中使用反向時間戳記。這種模式會導致資料列從最近到最舊排序,因此表格中較早出現的資料會是較新的資料。與任何時間戳記一樣,請避免以反向時間戳記做為資料列索引鍵的開頭,以免造成資源使用率不均。

您可以從程式設計語言的長整數最大值 (在 Java 中為 java.lang.Long.MAX_VALUE) 減去時間戳記,藉此反轉時間戳記。

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

多用戶群架構

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

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

舉例來說,如果您要儲存客戶的行動裝置資料 altostratexamplepetstore,可以建立如下所示的資料列鍵。然後,如果 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

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

網域名稱數量較少

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

查詢內容變更或不確定

如果您不一定會對資料執行相同的查詢,或不確定查詢內容,可以選擇將一列的所有資料儲存在一個資料欄中,而不是多個資料欄。採用這種做法時,您使用的格式會讓日後擷取個別值變得較為容易,例如 Protocol Buffers 二進位格式或 JSON 檔案。

資料列鍵的設計仍十分謹慎,確保您能擷取所需資料,但通常每個資料列只有一個資料欄,其中包含單一 protobuf 中該資料列的所有資料。

將資料儲存為單一資料欄中的 protobuf 訊息,而非將資料分散到多個資料欄,有其優缺點。優點包括:

  • 資料占用的空間較少,因此儲存費用也較低。
  • 您不必指定資料欄系列和資料欄限定詞,即可維持一定程度的彈性。
  • 您的讀取應用程式不需要「知道」資料表結構定義。

缺點如下:

  • 從 Bigtable 讀取 protobuf 訊息後,您必須將其還原序列化。
  • 您無法再使用篩選器查詢 protobuf 訊息中的資料。
  • 從 Bigtable 讀取 protobuf 訊息後,您無法使用 BigQuery 對訊息中的欄位執行聯合查詢。

後續步驟