假設 HBase 用於存儲客戶和訂單信息。有兩種核心記錄類型被攝取:客戶記錄類型和訂單記錄類型。
客戶記錄類型將包含您通常期望的所有內容:
- 客戶編號
- 客戶名稱
- 地址(例如,城市,州,郵編)
- 電話號碼等
訂單記錄類型將包含如下內容:
- 客戶編號
- 訂單編號
- 銷售日期
- 一系列用於裝運位置和訂單項的嵌套對象
假設客戶編號和銷售訂單的組合唯一地標識一個訂單,對於一個訂單(ORDER)表,這兩個屬性將組成 rowkey,特別是一個組合鍵,例如:
但是,還有更多的設計決策需要:原始值是 rowkeys 的最佳選擇嗎?
Log Data 用例中的相同設計問題在這裡面對我們。客戶編號的密鑰空間是什麼,以及格式是什麼(例如,數字或是字母數字?)由於在HBase中使用固定長度的密鑰以及可以在密鑰空間中支持合理分佈的密鑰是有利的,因此會出現類似的選項:
帶有哈希的複合 Rowkey:
- [客戶號碼的 MD5] = 16字節
- [訂單號的 MD5] = 16字節
複合數字/哈希組合 Rowkey:
- [代替客戶編號] = 8個字節
- [訂單號的 MD5] = 16字節
單個表/多個表
傳統的設計方法會為有單獨的 CUSTOMER 和 SALES 表格。另一種選擇是將多個記錄類型打包到一個表中(例如,CUSTOMER ++)。
客戶記錄類型 Rowkey:
- [customer-id]
- [type] = 表示客戶記錄類型為'1'的類型
訂單記錄類型Rowkey:
- [customer-id]
- [type] = 指示訂單記錄類型為'2'的類型
- [order]
這種特殊的 CUSTOMER ++ 方法的優點是通過客戶 ID 來組織許多不同的記錄類型(例如,一次掃描就可以得到關於該客戶的所有信息)。缺點是掃描特定的記錄類型並不容易。
HBase訂單對象設計
現在我們需要解決如何建模 Order 對象。假設類結構如下:
Order
Order 可以有多個 ShippingLocations
LineItem
一個 ShippingLocation 可以有多個 LineItems
存儲這些數據有多種選擇。
完全標準化
通過這種方法,ORDER,SHIPPING_LOCATION和LINE_ITEM 將會有單獨的表格。
上面描述了 ORDER 表的 rowkey:schema.casestudies.custorder
SHIPPING_LOCATION 的複合 rowkey 就像這樣:
- [order-rowkey]
- [shipping location number] (例如,第一地點,第二地點等)
LINE_ITEM 表的複合 rowkey 將如下所示:
- [order-rowkey]
- [shipping location number] (例如,第一地點,第二地點等)
- [line item number] (例如,第一條線,第二條等)
這樣的標準化模型很可能是 RDBMS 的方法,但這不是 HBase 唯一的選擇。這種做法的缺點是要檢索任何訂單的信息,您需要:
- 獲取訂單的訂單表
- 在 SHIPPING_LOCATION 表上掃描該訂單以獲取 ShippingLocation 實例
- 掃描每個 ShippingLocation 的 LINE_ITEM
這是一個 RDBMS 無論如何都會在封面下做的事情,但由於 HBase 中沒有加入,所以您只是更加意識到這一點。
具有記錄類型的單個表
採用這種方法,將會存在一個包含單個表的ORDER
Order rowkey 如上所述:schema.casestudies.custorder
- [order-rowkey]
- [ORDER record type]
ShippingLocation 複合 rowkey 將如下所示:
- [order-rowkey]
- [SHIPPING record type]
- [shipping location number] (例如,第一地點,第二地點等)
LineItem 複合 rowkey 將如下所示:
- [order-rowkey]
- [LINE record type]
- [shipping location number] (例如,第一地點,第二地點等)
- [line item number] (例如,第一條線,第二條等)
非規範化
具有記錄類型的單個表格的一種變體是對一些對象層次結構進行非規範化和扁平化,比如將 ShippingLocation 屬性摺疊到每個 LineItem 實例上。
LineItem 複合 rowkey 將如下所示:
- [order-rowkey]
- [LINE record type]
- [line item number] (例如,第一條線,第二條等,必須注意的是,在整個訂單中都是唯一的)
LineItem 列將是這樣的:
- 項目編號(itemNumber)
- 數量(quantity)
- 價錢(price)
- shipToLine1(從 ShippingLocation 非正規化)
- shipToLine2(從 ShippingLocation 非正規化)
- shipToCity(從 ShippingLocation 非正規化)
- shipToState(從 ShippingLocation 非正規化)
- shipToZip(從 ShippingLocation 非正規化)
這種方法的優點包括不太複雜的對象層次結構,但其中一個缺點是,如果這些信息發生變化,更新會變得更加複雜。
BLOB對象
通過這種方法,整個 Order 對象圖都以某種方式處理為 BLOB。例如,上面描述了 ORDER 表的 rowkey:schema.casestudies.custorder,而一個名為“order”的列將包含一個可以反序列化的對象,該對象包含一個容器 Order,ShippingLocations 和 LineItems。
這裡有很多選項:JSON,XML,Java 序列化,Avro,Hadoop Writable等等。所有這些都是相同方法的變體:將對象圖編碼為字節數組。應該注意這種方法,以確保在對象模型發生更改時保持向後兼容性,使舊的持久結構仍能從 HBase 中讀出。
優點是能夠以最少的 I/O 來管理複雜的對象圖(例如,在本例中每個 HBase Get 有 Order),但缺點包括前面提到的關於序列化的向後兼容性,序列化的語言依賴性(例如 Java 序列化只適用於 Java 客戶端),事實上你必須反序列化整個對象才能獲得 BLOB 中的任何信息,以及像 Hive 這樣的框架難以使用像這樣的自定義對象。
閱讀更多 會飛的魚go 的文章