高基數列是指數據基本不重復或者均為唯一值的列。典型的高基數列有ID標識,電子郵件地址或者用戶名等。一個具有高基數的數據表列的例子是具有一個名為USER_ID的列的USERS表。
這一列包含1-n的唯一值。每次在USERS表中創建一個新用戶時,將在USER_ID列中創建一個新數字,以唯一地標識它們。由于USER_ID列中保存的值是唯一的,因此該列的基數類型被稱為高基數。
如果你的工作中用到了數據庫,特別是要處理時序數據,那么可能你就會面對處理高基數數據的挑戰。
特別是工業物聯網(如制造業、石油和天然氣、公用事業等)以及一些監控和事件數據工作負載中,時間序列高基數的處理是一個常見問題。高基數也是開發人員經常討論的一個話題,圍繞它經常會有很多問題。
這里要澄清一個常見的混淆點:高基數在時序數據世界中之所以成為如此大的問題,是因為一些流行的時序數據庫的局限性。如果選擇了正確的數據庫,高基數數據其實是一個已經解決了的問題。
讓我們回過頭來首先來定義什么是高基數。
什么是高基數?
廣義上講,基數是指一個集合中的值的數量。有時,集合的基數很小(低基數),有時可能很大(高基數)。例如,上圖中有很多(美味的)M&M,但該數據集的基數非常小(6):
在數據庫世界中,基數是指數據庫的特定列或字段中包含的唯一值的數量。
然而,對于時序數據來說,事情可能變得有些復雜了。時序數據往往與描述該數據的元數據(有時稱為“標簽”)配對。
通常,主時序數據或元數據會被索引以提高查詢性能,這樣就可以快速找到匹配所有指定標簽的值。時序數據集的基數最典型是定義方式為每個索引列的基數的交叉乘積。
如果有6種顏色的m&m巧克力豆以及5種類型的m&m巧克力豆(普通的、花生的、杏仁的、椒鹽脆餅的和脆的),那么我們的基數現在是6x5 = 30種m&m巧克力豆。有了正確的索引,我們就能高效地找到所有藍色的、酥脆的m&m巧克力豆(這是客觀上最好的)。
如果你有多個索引列,每個列都有大量的唯一值,那么叉乘的基數可能會變得非常大。這是軟件開發人員在談論具有“高基數”的時序數據集時,“高基數”的通常含義。
讓我們來看一個例子。
高基數示例:工業物聯網
想象一個物聯網場景,在某個采石場,有大型、沉重的設備在進行采礦、破碎巖石和分類巖石這三種作業。
假設有10000件設備,每件設備有100個傳感器,運行10個不同的固件版本,分布在100個地點:這個數據集的最大基數變成了10億[10,000 x 100 x 10 x 100]。現在,假設設備也可以移動,我們想要存儲精確的GPS位置(lat、long)(緯度,經度),并將其用作查詢的索引元數據。因為(lat, long)是一個連續的字段(而不是像equipment_id這樣的離散字段),通過對位置進行索引,這個數據集的最大基數現在是無限大的(無界)。
為時序設計的關系型數據庫如何處理高基數
不同的數據庫采用不同的方法來處理高基數。根本上說,在使用高基數數據集時,數據庫的性能表現如何可以追溯到它從一開始是如何設計的。
如果你正在處理大量的時序數據并使用關系數據庫,那么用于索引數據的一種經過驗證的數據結構是b樹。依賴b樹數據結構來索引數據對于高基數數據集有幾個好處:
- 你可以清晰地理解數據庫的執行方式。只要你希望查詢的數據集的索引和數據適合內存(這是可以調優的),基數就不是問題。
- 你可以控制索引哪些列,包括在多個列上創建聯合索引的能力。你還可以隨時添加或刪除索引,如果你的查詢工作負載發生了變化。
- 你可以在離散和連續字段上創建索引,特別是因為b樹可以很好地使用以下操作符進行比較:<,<=,=,>=,>,BETWEEN, IN, IS NULL, IS NOT NULL。我們的示例查詢(“SELECT * from sensor_data WHERE mem_free = 0”和“SELECT * from sensor_data WHERE temperature > 90”)將運行在對數或O(log n)的時間復雜度內。
雖然時序數據庫使用其他方法來實現高基數,但使用b樹結構已被證明是可靠的。如果你遇到有關高基數的數據問題,可以留言一起討論。
參考鏈接:https://dzone.com/articles/what-is-high-cardinality
譯者介紹
盧鑫旺,51CTO社區編輯,半路出家的九零后程序員。做過前端頁面,寫過業務接口,搞過爬蟲,研究過JS,有幸接觸Golang,參與微服務架構轉型。目前主寫Java,負責公司可定制化低代碼平臺的數據引擎層設計開發工作。