您的位置:首頁>正文

Hadoop系列五——HBase簡介

本文是對《 Hadoop系列四——HBase簡介 》一文的補充, 不過本文不會進行系統性介紹, 只是針對一個個獨立的點介紹, 並且會不斷更新, 有點類似於FAQ吧。

1. HBase的架構圖

簡易版架構圖:

複雜版架構圖:

架構講解見《 Hadoop系列四——HBase簡介 》。

2. HBase的資料模型

在《 Hadoop系列四——HBase簡介 》裡面已經介紹過HBase的資料模型了, 但這個的確非常重要, 這裡再做一些補充。 HBase是根據Google的Bigtable論文實現的開源版"Bigtable", 所以對Bigtable的描述同樣適用於HBase:

A Bigtable is a sparse,distributed, persistentmulti-dimensional sorted map. The map is indexed by a row key, column key, and a timestamp; each value in the map is anuninterpreted array of bytes.

這裡有幾個關鍵字我已經加粗了,

從這幾個關鍵字可以引出來多個問題。

2.1 HBase裡面的"NULL值"處理

第一個就是 sparse關鍵字。 首先NULL值是RDBMS裡面的概念, HBase裡面其實沒有這個概念, 這裡只是為了作對比, 介紹稀疏性這個特性。 我們把HBase想成一個二維矩陣(這樣不準確, 但有助於理解), 那麼其實就是一個疏鬆陣列。 在RDBMS裡面, 沒有值的地方一般用NULL表示, 雖然相比於其他類型, NULL占很小的空間(一般是1bit), 但仍然是占空間的, 在RDBMS裡面這沒有問題, 主要有兩個原因:(1)RDBMS裡面資料量不會很大, 目前RDBMS能承載的最大資料量一般也就是百萬級別的。 (2)如果把RDBMS也看成二維矩陣的話, 一般RDBMS都屬於稠密矩陣, 所以NULL值不會占大多數。

但HBase不一樣, 它正好與RDBMS的特性相反:資料量很大, 一般都大於百萬級別(如果小於這個量級,

那你該重新考慮是否有必要使用HBase了);NULL值可能占很大部分, 這個和HBase的設計使用有關係。 比如一個表裡面有1百萬個列, 有1千萬行資料, 每一行裡面可能只有一小部分列有值, 其他列都是“NULL值”(實質是沒有值)。 所以, 在HBase裡面, "NULL值"是不存儲的, 即不佔用任何存儲。 其實這個和疏鬆陣列的存儲原理上是一樣的, 只存儲有資料的cell的“座標”及值, 其它的默認就是NULL。 當我們在HBase裡面獲取一行資料的時候, 只會返回有資料的列, 不像RDBMS會返回所有列, 沒有值的用NULL占位。

更多資訊見本文“ HBase的邏輯存儲和物理存儲”部分。

2.2 分散式

想想如果因為性能或存儲等不夠了, 需要將RDBMS擴展成分散式的, 能否做到?也許可以做到, 但一般是受限的, 而且難度比較高,

比如像業內的GreenPlum, 如果還想要保證RDBMS的所有特性, 那就只能用更好的硬體做垂直擴展了。 但HBase本身就是分散式的, 擴展一個HBase節點基本上沒有什麼難度。 所以這也是很多企業選擇HBase的一個非常重要的考量。

2.3 多維有序map

多維的概念在我的《 Hadoop系列四——HBase簡介 》裡面已經講過了。

有序之前也提過, 在HBase裡面, 不論是內部存儲, 還是查詢返回的資料, 都是有序的: 依次按照row key、Column Family、Column qualifier、Timestamp四個維度排序, 時間戳記是按照從新到舊排的, 其它都是字典序。

2.4 HBase裡面的資料是否有類型

上面對於value的描述是“ uninterpreted array of bytes”, 也就是沒有類型, 預設按位元組流解析(我們知道任何類型其實都可以按位元組流其解析)。 其實在HBase裡面除了會被作為路徑的名稱之外, 其它都是作為位元組流的,

也就是沒有類型。 具體就是:表名、Column Family會作為路徑名, 所以名稱必須是合法的路徑名, 也就是必須是可列印字元;row key, Column qualifier、Cell都是沒有類型的, 也就是可以是任何資料。 實際中, 我們經常會在row key、Column qualifier中存儲資料, 而不光是cell裡面。

3. HBase的邏輯存儲和物理存儲

引用 HBase:The Definitive Guide上的一幅圖:

第一幅圖(左上角)是HBase裡面一個典型的表的邏輯圖, 裡面有 cf1和cf2兩個Column Family(以下簡稱CF), 每個CF裡面有兩列。 裡面的紅色和黃色小塊表示有資料, 其他地方都沒有資料(如前面所說, 是一個疏鬆陣列), 多個層疊的部分表示有多個版本的資料。

第二幅圖(右上角)也是一個邏輯圖, 主要為了說明以下幾個點:

不同CF裡面的資料是分開存儲的;同一個CF裡面的資料是按照row key順序存儲的;統一cell裡面有多個版本的資料的時候, 新版本資料在前,舊版本資料在後,這樣方便先取到新版本資料。

第三幅圖(右下角)是上面的邏輯存儲在物理檔上的存儲形式,需要注意以下幾個點:

不同CF的資料是存儲在不同的檔裡面的(Storefile或HFile),這就是為什麼我們要將要一起使用的資料欄位定義在同一個Column Family的原因;同一個檔裡面還是按照row key、CF、Column qualifier排序的;每一條資料裡面都要存儲Key(row key、Column Family、Column qualifier、Timestamp)和value。在RDBMS裡面我們設計欄位名時一般要求能夠“見名知意”,但在HBase裡面不推薦這樣做, Key的設計在保證功能的前提下,越短越好(比如僅用一個字母表示),至於其含義可以其它地方記錄,比如文檔裡面。

第四幅圖(左下角)是為了說明查詢時指定各個Key對性能的影響:

指定 row key可以大幅度提高查詢性能,因為根據row key可以確定在哪些region上面查(也就是說可以跳過那些不包含該row key的region)。在scan命令裡面,可以通過STARTROW和STOPROW指定row key範圍。指定 Column Family可以大幅度提高查詢性能,因為根據CF可以確定跳過哪些Storefile/HFile,一般查詢時都建議指定CF。指定 Timestamp也可以較大幅度提高查詢性能,因為每個Storefile會存儲它所保存的所有資料的時間區間,如果所指定的Timestamp不在該區間內,則直接跳過。指定 Column Qualifier和Value的過濾條件可以提高查詢性能,但提高的很少。因為必須把每個Cell的值讀出來和指定的條件做對比。

4. Tall-Narrow or Flat-Wide表

Tall-Narrow也就是我們所說的“窄表”,Flat-Wide是“寬表”。舉個例子,比如我們要存儲一個使用者的郵件資訊:使用者ID、郵件ID、郵件內容。如果按照Tall-Narrow的思想去設計,表結構可能是下面這樣:

# 將用戶id和郵件id拼接成row key,郵件內容作為一列資料userid-emailid, cf:emailbaody

如果按照 Tall-Narrow思路去設計,表結構可能是下面這樣:

# userid為row key,emailid和emailbody作為兩個列userid, cf:emailid, cf:emailbody

兩種設計各有利弊,使用寬表的好處主要在於HBase的ACID特性僅限於行內,所以如果把所有資料都放在一行,那可以很好的利用其ACID特性。而窄表在實際使用中更加常見一些,因為HBase裡面的的row key類似於RDBMS裡面的主鍵,所以我們盡可能將要經常查詢的維度放在row key裡面,可以提高查詢性能;另外一個表裡面不推薦有太多的Column Family,一般1個最好,最多也不要超過3個,具體見本文“Column Family的數量”部分。

5. HBase的表和Column Family能不能修改

先說結論: 可以修改。使用alter命令可以修改表和Column Family,具體語法可以help "alter"查看。這裡需要注意兩點:

0.92版本之前,表必須先disable後才可以修改。之後的版本增加了一個配置項 hbase.online.schema.update.enable,如果設為true,那可以直接修改,不需要disable。但官方推薦生產環境最好還是先disable再修改,線上修改可能會引發一些問題。修改動作並非立即生效,而是等待下一次 major compaction,Storefile重寫之後才會生效。

6. HBase的Compaction

我們知道當MemStore裡面的資料量達到一定值的時候,就會落盤形成StoreFile(HFile),這樣就會形成很多檔,而Compaction就是將這些檔合併成大檔。HBase裡面有兩種Compaction: Minor compactions和Major compactions,主要有如下區別:

Minor compactions一次只選取少量存儲在一起的檔做合併壓縮,其結果就是一個store(或Column Family)的資料被合併成了多個大一些的Storefile,而Major compactions合併之後的結果是一個store(或Column Family)的資料全部到一個Storefile裡面去了。Minor compactions合併時不刪除已經標記刪除或者過期版本的資料,而Major compactions會刪掉那些標記刪除或過期版本的資料。所以需要注意,HBase裡面的刪除是標記刪除,真正的物理刪除發生在Major compactions階段。

雖然Compaction合併檔是為了提高性能,但合併這個操作卻是消耗資源的,就跟Jvm的GC一樣。默認 Major compactions一週一次。

7. Column Family的數量

先說結論: 一個表內的Column Family最好1個,最多不要超過3個。原因主要有兩點:

現在HBase的flush(MemStore滿了之後就會flush)和Compaction操作都是基於region的,從前面的架構圖中可以看到一個region裡面是包含多個Column Family的,所以當region內的某個Column Family需要flush或Compaction的時候,和它處於同一region內的其它Column Family也會一起flush或Compaction,但它們可能只有少量新增資料,這就會浪費IO。假設有兩個Column Family:CF-A和CF-B,其中CF-A有一百萬條資料,CF-B有一千萬條資料。假設CF-A占了100個HFile,CF-B占了1000個HFile,因為資料寫入的先後順序,很可能CF-A的100個檔會被CF-B的1000個檔打散,本來可能一個RegionServer上面的region足夠存儲所有的CF-A的HFile了,現在可能被打散到多個RegionServer上面去了,這樣查詢CF-A的資料的時候效率就會降低。這種現象稱為“Cardinality of ColumnFamilies”。

8. Value的版本數和TTL

版本數指我們之前說的Timestamp,和這個特性相關的設置有兩個: max versions和min versions,其含義也很明確。max versions的預設值是1,min versions的預設值是0,表示不啟用多版本這個特性,即往一個Cell裡面重複寫資料會覆蓋,而不是保留多個版本。

min versions和HBase的TTL(time-to-live)一起使用。我們可以給Column Family設置一個TTL時間(單位為秒),時間到期後的row會自動被刪除。如果一個Storefile裡面的row全部是過期的,那麼在minor compaction階段這個Storefile會被刪除,我們可以通過把hbase.store.delete.expired.storefile設置為false或者把min versions設置為非0值來關閉刪除這個特性。

9. 選擇HBase還是RDBMS

一般來說,這個選擇是比較好做的。HBase、Impala等資料庫的誕生並不是為了替代傳統的RDBMS,只是為了解決新的RDBMS解決不了或者解決起來比較困難的問題。所以,如果你的資料量並不大(一般以百萬為分界線),那一般應該優先選擇RDBMS,畢竟RDBMS支持完善的ACID,SQL,多級索引,各種Join,完善的類型等豐富的特性,這些都是HBase所不具備的。

但如果你的資料量非常大,RDBMS已經無法支撐了,那就可以考慮HBase等分散式資料庫了。但如果你的業務離不開RDBMS的一些特性(比如各種Join、SQL、完善的ACID等),那可能就需要考慮類似於GreenPlum這種MPP資料庫了。

10. FAQ

HBase是否支持ACID?

HBase裡面只支持受限的ACID(Atomicity, Consistency, Isolation, and Durability):僅支持行內的ACID,跨行不支持。也就是對於同一行的操作是可以保證ACID的,但是多行操作是不行的。更多資訊可參考: ACID in HBase 。

HBase和Hive如何選擇?

這兩個不是一個層級的東西,沒有可比性。如果你還在這二者之間糾結,那可能你對它們有些誤會,或者還不清楚你自己的需求。非要比的話,那就是HBase一般做即時查詢;而Hive一般作為離線資料倉庫,Hive後面是MapReduce/Spark,所以無法做到即時。

HBase支不支持join?

先說結論: 不支持。HBase讀取資料時支援Get和Scan(Get的後臺實現是Scan的一種特殊情況而已)操作,RDBMS裡面的join在HBase裡面是不支持的,但我們可以在表設計上支持一定程度上的"join"操作,比如將需要join的欄位拼接起來作為row key。

HBase支持SQL嗎?

HBase不支援SQL,只提供了各種API。但Apache下有個Phoenix專案,通過該專案可以使用SQL語句操作HBase。

HBase的一個region多大合適?

注意,這裡說的是region,不是RegionServer。一個region保持在10~50GB比較好。

HBase一個表包含多少個region比較好?

一般一個表包含50~100個region和1~2個Column Family比較好。

HBase的Cell裡面存儲的Value有大小限制嗎?

沒有,但一般不要超過10MB(對於MOB物件不要超過50MB)。如果超過了這個大小,可以將物件存到HDFS上面,然後再HBase裡面存儲HDFS路徑。

HBase裡面的daughter是什麼?

HBase的region分裂的時候,分裂出來的兩個新region稱為"daughter",原來的稱為"parent"。

關於HBase還有兩個核心的點:一個是row key的設計,另外一個就是HFile。目前計畫後續會分別寫兩篇文章來介紹這兩方面東西,有興趣的可持續關注。

References:

Apache HBase Reference Guide(Version3.0.0-SNAPSHOT).HBase:The Definitive Guide.

-->

2.1 HBase裡面的"NULL值"處理

2.4 HBase裡面的資料是否有類型

3. HBase的邏輯存儲和物理存儲

4. Tall-Narrow or Flat-Wide表

5. HBase的表和Column Family能不能修改

7. Column Family的數量

8. Value的版本數和TTL

9. 選擇HBase還是RDBMS

新版本資料在前,舊版本資料在後,這樣方便先取到新版本資料。

第三幅圖(右下角)是上面的邏輯存儲在物理檔上的存儲形式,需要注意以下幾個點:

不同CF的資料是存儲在不同的檔裡面的(Storefile或HFile),這就是為什麼我們要將要一起使用的資料欄位定義在同一個Column Family的原因;同一個檔裡面還是按照row key、CF、Column qualifier排序的;每一條資料裡面都要存儲Key(row key、Column Family、Column qualifier、Timestamp)和value。在RDBMS裡面我們設計欄位名時一般要求能夠“見名知意”,但在HBase裡面不推薦這樣做, Key的設計在保證功能的前提下,越短越好(比如僅用一個字母表示),至於其含義可以其它地方記錄,比如文檔裡面。

第四幅圖(左下角)是為了說明查詢時指定各個Key對性能的影響:

指定 row key可以大幅度提高查詢性能,因為根據row key可以確定在哪些region上面查(也就是說可以跳過那些不包含該row key的region)。在scan命令裡面,可以通過STARTROW和STOPROW指定row key範圍。指定 Column Family可以大幅度提高查詢性能,因為根據CF可以確定跳過哪些Storefile/HFile,一般查詢時都建議指定CF。指定 Timestamp也可以較大幅度提高查詢性能,因為每個Storefile會存儲它所保存的所有資料的時間區間,如果所指定的Timestamp不在該區間內,則直接跳過。指定 Column Qualifier和Value的過濾條件可以提高查詢性能,但提高的很少。因為必須把每個Cell的值讀出來和指定的條件做對比。

4. Tall-Narrow or Flat-Wide表

Tall-Narrow也就是我們所說的“窄表”,Flat-Wide是“寬表”。舉個例子,比如我們要存儲一個使用者的郵件資訊:使用者ID、郵件ID、郵件內容。如果按照Tall-Narrow的思想去設計,表結構可能是下面這樣:

# 將用戶id和郵件id拼接成row key,郵件內容作為一列資料userid-emailid, cf:emailbaody

如果按照 Tall-Narrow思路去設計,表結構可能是下面這樣:

# userid為row key,emailid和emailbody作為兩個列userid, cf:emailid, cf:emailbody

兩種設計各有利弊,使用寬表的好處主要在於HBase的ACID特性僅限於行內,所以如果把所有資料都放在一行,那可以很好的利用其ACID特性。而窄表在實際使用中更加常見一些,因為HBase裡面的的row key類似於RDBMS裡面的主鍵,所以我們盡可能將要經常查詢的維度放在row key裡面,可以提高查詢性能;另外一個表裡面不推薦有太多的Column Family,一般1個最好,最多也不要超過3個,具體見本文“Column Family的數量”部分。

5. HBase的表和Column Family能不能修改

先說結論: 可以修改。使用alter命令可以修改表和Column Family,具體語法可以help "alter"查看。這裡需要注意兩點:

0.92版本之前,表必須先disable後才可以修改。之後的版本增加了一個配置項 hbase.online.schema.update.enable,如果設為true,那可以直接修改,不需要disable。但官方推薦生產環境最好還是先disable再修改,線上修改可能會引發一些問題。修改動作並非立即生效,而是等待下一次 major compaction,Storefile重寫之後才會生效。

6. HBase的Compaction

我們知道當MemStore裡面的資料量達到一定值的時候,就會落盤形成StoreFile(HFile),這樣就會形成很多檔,而Compaction就是將這些檔合併成大檔。HBase裡面有兩種Compaction: Minor compactions和Major compactions,主要有如下區別:

Minor compactions一次只選取少量存儲在一起的檔做合併壓縮,其結果就是一個store(或Column Family)的資料被合併成了多個大一些的Storefile,而Major compactions合併之後的結果是一個store(或Column Family)的資料全部到一個Storefile裡面去了。Minor compactions合併時不刪除已經標記刪除或者過期版本的資料,而Major compactions會刪掉那些標記刪除或過期版本的資料。所以需要注意,HBase裡面的刪除是標記刪除,真正的物理刪除發生在Major compactions階段。

雖然Compaction合併檔是為了提高性能,但合併這個操作卻是消耗資源的,就跟Jvm的GC一樣。默認 Major compactions一週一次。

7. Column Family的數量

先說結論: 一個表內的Column Family最好1個,最多不要超過3個。原因主要有兩點:

現在HBase的flush(MemStore滿了之後就會flush)和Compaction操作都是基於region的,從前面的架構圖中可以看到一個region裡面是包含多個Column Family的,所以當region內的某個Column Family需要flush或Compaction的時候,和它處於同一region內的其它Column Family也會一起flush或Compaction,但它們可能只有少量新增資料,這就會浪費IO。假設有兩個Column Family:CF-A和CF-B,其中CF-A有一百萬條資料,CF-B有一千萬條資料。假設CF-A占了100個HFile,CF-B占了1000個HFile,因為資料寫入的先後順序,很可能CF-A的100個檔會被CF-B的1000個檔打散,本來可能一個RegionServer上面的region足夠存儲所有的CF-A的HFile了,現在可能被打散到多個RegionServer上面去了,這樣查詢CF-A的資料的時候效率就會降低。這種現象稱為“Cardinality of ColumnFamilies”。

8. Value的版本數和TTL

版本數指我們之前說的Timestamp,和這個特性相關的設置有兩個: max versions和min versions,其含義也很明確。max versions的預設值是1,min versions的預設值是0,表示不啟用多版本這個特性,即往一個Cell裡面重複寫資料會覆蓋,而不是保留多個版本。

min versions和HBase的TTL(time-to-live)一起使用。我們可以給Column Family設置一個TTL時間(單位為秒),時間到期後的row會自動被刪除。如果一個Storefile裡面的row全部是過期的,那麼在minor compaction階段這個Storefile會被刪除,我們可以通過把hbase.store.delete.expired.storefile設置為false或者把min versions設置為非0值來關閉刪除這個特性。

9. 選擇HBase還是RDBMS

一般來說,這個選擇是比較好做的。HBase、Impala等資料庫的誕生並不是為了替代傳統的RDBMS,只是為了解決新的RDBMS解決不了或者解決起來比較困難的問題。所以,如果你的資料量並不大(一般以百萬為分界線),那一般應該優先選擇RDBMS,畢竟RDBMS支持完善的ACID,SQL,多級索引,各種Join,完善的類型等豐富的特性,這些都是HBase所不具備的。

但如果你的資料量非常大,RDBMS已經無法支撐了,那就可以考慮HBase等分散式資料庫了。但如果你的業務離不開RDBMS的一些特性(比如各種Join、SQL、完善的ACID等),那可能就需要考慮類似於GreenPlum這種MPP資料庫了。

10. FAQ

HBase是否支持ACID?

HBase裡面只支持受限的ACID(Atomicity, Consistency, Isolation, and Durability):僅支持行內的ACID,跨行不支持。也就是對於同一行的操作是可以保證ACID的,但是多行操作是不行的。更多資訊可參考: ACID in HBase 。

HBase和Hive如何選擇?

這兩個不是一個層級的東西,沒有可比性。如果你還在這二者之間糾結,那可能你對它們有些誤會,或者還不清楚你自己的需求。非要比的話,那就是HBase一般做即時查詢;而Hive一般作為離線資料倉庫,Hive後面是MapReduce/Spark,所以無法做到即時。

HBase支不支持join?

先說結論: 不支持。HBase讀取資料時支援Get和Scan(Get的後臺實現是Scan的一種特殊情況而已)操作,RDBMS裡面的join在HBase裡面是不支持的,但我們可以在表設計上支持一定程度上的"join"操作,比如將需要join的欄位拼接起來作為row key。

HBase支持SQL嗎?

HBase不支援SQL,只提供了各種API。但Apache下有個Phoenix專案,通過該專案可以使用SQL語句操作HBase。

HBase的一個region多大合適?

注意,這裡說的是region,不是RegionServer。一個region保持在10~50GB比較好。

HBase一個表包含多少個region比較好?

一般一個表包含50~100個region和1~2個Column Family比較好。

HBase的Cell裡面存儲的Value有大小限制嗎?

沒有,但一般不要超過10MB(對於MOB物件不要超過50MB)。如果超過了這個大小,可以將物件存到HDFS上面,然後再HBase裡面存儲HDFS路徑。

HBase裡面的daughter是什麼?

HBase的region分裂的時候,分裂出來的兩個新region稱為"daughter",原來的稱為"parent"。

關於HBase還有兩個核心的點:一個是row key的設計,另外一個就是HFile。目前計畫後續會分別寫兩篇文章來介紹這兩方面東西,有興趣的可持續關注。

References:

Apache HBase Reference Guide(Version3.0.0-SNAPSHOT).HBase:The Definitive Guide.

-->

2.1 HBase裡面的"NULL值"處理

2.4 HBase裡面的資料是否有類型

3. HBase的邏輯存儲和物理存儲

4. Tall-Narrow or Flat-Wide表

5. HBase的表和Column Family能不能修改

7. Column Family的數量

8. Value的版本數和TTL

9. 選擇HBase還是RDBMS

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示