HBase使用理論與實踐

午安我叫 Danil Lipovoy,我們 Sbertech 的團隊開始使用 HBase 作為營運資料的儲存。 在學習的過程中,累積了一些經驗,我想將其係統化和描述(希望對很多人有用)。 以下所有實驗均使用 HBase 版本 1.2.0-cdh5.14.2 和 2.0.0-cdh6.0.0-beta1 進行。

  1. 通用架構
  2. 將資料寫入HBASE
  3. 從HBASE讀取數據
  4. 數據緩存
  5. 批次資料處理MultiGet/MultiPut
  6. 將表拆分為區域的策略(拆分)
  7. 容錯、壓縮和資料局部性
  8. 設定和性能
  9. 壓力測試
  10. 發現

1. 總體架構

HBase使用理論與實踐
備份Master監聽ZooKeeper節點上活動Master的心跳,並在主節點消失的情況下接手Master的功能。

2.將資料寫入HBASE

首先,讓我們來看看最簡單的情況 - 使用 put(rowkey) 將鍵值物件寫入表。 客戶端首先必須找出儲存 hbase:meta 表的根區域伺服器 (RRS) 所在的位置。 他從 ZooKeeper 接收此訊息。 之後,它存取 RRS 並讀取 hbase:meta 表,從中提取有關哪個 RegionServer (RS) 負責儲存感興趣表中給定行鍵的資料的資訊。 為了將來使用,元表由客戶端緩存,因此後續調用速度更快,直接到 RS。

接下來,RS 收到請求後,首先將其寫入 WriteAheadLog (WAL),這是崩潰時恢復所必需的。 然後將資料儲存到MemStore。 這是記憶體中的一個緩衝區,其中包含給定區域的一組排序鍵。 一個表可以分為多個區域(分區),每個區域都包含一組不相交的鍵。 這允許您將區域放置在不同的伺服器上以獲得更高的效能。 然而,儘管這個聲明很明顯,我們稍後會看到這並不適用於所有情況。

將條目放入 MemStore 後,將向用戶端傳回條目已成功儲存的回應。 然而,實際上它只儲存在緩衝區中,並且只有在經過一定時間或充滿新資料後才會到達磁碟。

HBase使用理論與實踐
執行「刪除」操作時,資料並未被實體刪除。 它們被簡單地標記為已刪除,並且銷毀本身發生在呼叫主要緊湊函數時,這在第 7 段中有更詳細的描述。

HFile 格式的檔案在 HDFS 中累積,並且不時啟動較小的壓縮進程,該進程只是將小文件合併為大文件,而不刪除任何內容。 隨著時間的推移,這變成了一個僅在讀取資料時出現的問題(我們稍後會回到這個問題)。

除了上述載入過程之外,還有一個更有效的過程,這可能是該資料庫最強大的一面 - BulkLoad。 它在於我們獨立地形成 HFile 並將它們放在磁碟上,這使我們能夠完美地擴展並獲得非常好的速度。 事實上,這裡的限制不是HBase,而是硬體的能力。 以下是由 16 個 RegionServer 和 16 個 NodeManager YARN(CPU Xeon E5-2680 v4 @ 2.40GHz * 64 執行緒)、HBase 版本 1.2.0-cdh5.14.2 組成的叢集上的啟動結果。

HBase使用理論與實踐

在這裡您可以看到,透過增加表中分區(區域)的數量以及 Spark 執行器,我們獲得了下載速度的提高。 此外,速度取決於錄音音量。 在其他條件相同的情況下,大塊會增加 MB/秒,小塊會增加單位時間插入的記錄數。

您也可以同時開始載入到兩個表中並獲得雙倍的速度。 下面您可以看到,同時寫入兩個表 10 KB 區塊的速度約為 600 MB/秒(總計 1275 MB/秒),這與寫入一個表 623 MB/秒的速度一致(請參閱上面第11 條)

HBase使用理論與實踐
但第二次運行50KB的記錄時,下載速度略有增長,這表示它已經接近極限值。 同時,您需要記住,HBASE 本身幾乎沒有創建任何負載,所需要做的只是首先從 hbase:meta 提供數據,然後在排列 HFiles 後,重置 BlockCache 數據並保存MemStore 緩衝區到磁盤,如果不為空。

3.從HBASE讀取數據

如果我們假設客戶端已經擁有來自 hbase:meta 的所有資訊(請參閱第 2 點),則請求將直接傳送到儲存所需金鑰的 RS。 首先,在 MemCache 中執行搜尋。 無論那裡是否有數據,都會在 BlockCache 緩衝區中進行搜索,如果需要,也會在 HFile 中進行搜索。 如果在文件中找到數據,則會將其放置在 BlockCache 中,並在下一次請求時更快地返回。 由於使用了布隆過濾器,在 HFile 中搜尋相對較快,即讀取少量資料後,它會立即判斷該檔案是否包含所需的金鑰,如果不包含,請轉到下一個。

HBase使用理論與實踐
從這三個來源接收到資料後,RS 產生回應。 特別是,如果客戶端請求版本控制,它可以一次傳輸物件的多個找到的版本。

4.資料緩存

MemStore 和 BlockCache 緩衝區最多佔用已分配堆上 RS 記憶體的 80%(其餘部分保留給 RS 服務任務)。 如果典型的使用模式是進程寫入並立即讀取相同的數據,那麼減少 BlockCache 並增加 MemStore 是有意義的,因為當寫入資料沒有進入快取進行讀取時,BlockCache 的使用頻率會降低。 BlockCache 緩衝區由兩部分組成:LruBlockCache(始終在堆上)和 BucketCache(通常在堆外或 SSD 上)。 當有大量讀取請求且無法放入 LruBlockCache 時,應使用 BucketCache,這會導致垃圾收集器主動運作。 同時,您不應期望透過使用讀取快取來大幅提高效能,但我們將在第 8 段中返回這一點

HBase使用理論與實踐
整個RS有一個BlockCache,每個表有一個MemStore(每個Column Family一個)。

描述 理論上,寫入時,資料不會進入緩存,實際上,表的 CACHE_DATA_ON_WRITE 參數和 RS 的「Cache DATA on Write」參數都設定為 false。 然而,在實踐中,如果我們將資料寫入MemStore,然後將其刷新到磁碟(從而清除它),然後刪除生成的文件,那麼透過執行get請求我們將成功接收資料。 此外,即使您完全停用 BlockCache 並用新資料填充表,然後將 MemStore 重置到磁碟,刪除它們並從另一個會話請求它們,它們仍然會從某個地方檢索。 所以HBase儲存的不只是數據,還有神秘的奧秘。

hbase(main):001:0> create 'ns:magic', 'cf'
Created table ns:magic
Took 1.1533 seconds
hbase(main):002:0> put 'ns:magic', 'key1', 'cf:c', 'try_to_delete_me'
Took 0.2610 seconds
hbase(main):003:0> flush 'ns:magic'
Took 0.6161 seconds
hdfs dfs -mv /data/hbase/data/ns/magic/* /tmp/trash
hbase(main):002:0> get 'ns:magic', 'key1'
 cf:c      timestamp=1534440690218, value=try_to_delete_me

“讀取時快取資料”參數設定為 false。 如果你有什麼想法,歡迎在評論中討論。

5.批量資料處理MultiGet/MultiPut

處理單一請求(Get/Put/Delete)是一項相當昂貴的操作,因此如果可能的話,您應該將它們組合成一個 List 或 List,這可以讓您獲得顯著的效能提升。 對於寫入操作尤其如此,但是在讀取時存在以下陷阱。 下圖顯示了從 MemStore 讀取 50 筆記錄的時間。 讀取是在一個執行緒中執行的,橫軸顯示請求中的鍵數。 在這裡您可以看到,當一個請求中增加到一千個鍵時,執行時間會下降,即速度增加。 然而,在預設啟用MSLAB模式的情況下,在此閾值之後,效能開始急劇下降,並且記錄中的資料量越大,操作時間就越長。

HBase使用理論與實踐

測試在虛擬機器上進行,8核,版本HBase 2.0.0-cdh6.0.0-beta1。

MSLAB 模式旨在減少由於新舊代資料混合而產生的堆疊碎片。 作為一種解決方法,當啟用 MSLAB 時,資料會放入相對較小的單元(區塊)中並按區塊處理。 因此,當請求的資料包量超過分配的大小時,效能急劇下降。 另一方面,關閉此模式也是不可取的,因為它會導致在密集資料處理時由於 GC 而停止。 一個好的解決方案是在讀取的同時透過 put 主動寫入的情況下增加單元體積。 值得注意的是,如果在記錄後執行flush命令(將MemStore重置到磁碟)或使用BulkLoad加載,則不會出現該問題。 下表顯示從 MemStore 查詢較大(且相同數量)的資料會導致速度變慢。 然而,透過增加區塊大小,我們使處理時間恢復正常。

HBase使用理論與實踐
除了增加區塊大小之外,按區域分割資料也有幫助,即表分裂。 這會導致到達每個區域的請求減少,如果它們適合一個單元,則回應仍然良好。

6.表拆分為Region的策略(splitting)

由於HBase是鍵值存儲,且分區是按鍵進行的,因此將資料均勻地劃分到所有區域是極其重要的。 例如,將這樣的表格分為三個部分將導致資料分為三個區域:

HBase使用理論與實踐
如果稍後加載的資料看起來像長值,其中大多數以相同的數字開頭,那麼這會導致速度急劇下降,例如:

1000001
1000002
...
1100003

由於鍵儲存為位元組數組,因此它們都將以相同的方式開始,並且屬於儲存該範圍鍵的相同區域#1。 分區策略有以下幾種:

HexStringSplit – 將金鑰轉換為「00000000」=>「FFFFFFFF」範圍內的十六進位編碼字串,並用零填滿左側。

UniformSplit – 將金鑰轉換為十六進位編碼的位元組數組,範圍為“00”=>“FF”,並在右側填入零。

此外,您可以指定任何範圍或一組鍵進行拆分並配置自動拆分。 然而,最簡單且最有效的方法之一是 UniformSplit 和雜湊連接的使用,例如透過 CRC32(rowkey) 函數運行金鑰的最重要的位元組對和 rowkey 本身:

哈希+行鍵

然後所有數據將均勻分佈在各個區域。 讀取時,前兩個位元組被簡單地丟棄,原始密鑰保留下來。 RS 還控制該區域中的資料和金鑰量,如果超出限制,則會自動將其分解為多個部分。

7. 容錯與資料局部性

由於只有一個區域負責每組金鑰,因此與 RS 崩潰或退役相關的問題的解決方案是將所有必要的資料儲存在 HDFS 中。 當 RS 失效時,Master 透過 ZooKeeper 節點上沒有心跳來偵測到這一點。 然後它將服務區域分配給另一個RS,並且由於HFile儲存在分散式檔案系統中,新所有者讀取它們並繼續提供資料。 但是,由於部分資料可能在MemStore中,沒有時間進入HFiles,所以使用同樣儲存在HDFS中的WAL來恢復操作的歷史記錄。 應用變更後,RS 能夠回應請求,但此舉導致一些資料和為它們提供服務的進程最終位於不同的節點上,即局部性正在減弱。

問題的解決方案是主要壓縮 - 該過程將檔案移至負責它們的節點(它們的區域所在的位置),因此在此過程中網路和磁碟上的負載急劇增加。 然而,在未來,數據的存取速度將顯著加快。 此外,major_compaction 將一個區域內的所有 HFile 合併到一個檔案中,並根據表格設定清理資料。 例如,您可以指定必須保留的物件的版本數或實體刪除該物件的生命週期。

這個過程可以對HBase的運作產生非常正面的影響。 下圖顯示了活動資料記錄所導致的效能下降。 在這裡您可以看到 40 個執行緒如何寫入一張表以及 40 個執行緒如何同時讀取資料。 寫入執行緒會產生越來越多的 HFile,這些 HFile 會被其他執行緒讀取。 結果,越來越多的資料需要從記憶體中移除,最終 GC 開始工作,這幾乎癱瘓了所有工作。 大規模壓實的啟動清除了由此產生的碎片並恢復了生產力。

HBase使用理論與實踐
測試在 3 個 DataNode 和 4 個 RS(CPU Xeon E5-2680 v4 @ 2.40GHz * 64 執行緒)上進行。 HBase版本1.2.0-cdh5.14.2

值得注意的是,主要壓縮是在「即時」表上啟動的,資料是主動寫入和讀取的。 網路上有說法稱這可能會導致讀取資料時出現錯誤回應。 為了進行檢查,啟動了一個進程來產生新資料並將其寫入表中。 然後我立即閱讀並檢查結果值是否與記錄的值一致。 當這個過程運行時,主要壓縮運行了大約 200 次,沒有記錄到一次失敗。 也許這個問題很少出現,並且只在高負載期間出現,因此按計劃停止寫入和讀取進程並執行清理以防止此類 GC 下降會更安全。

此外,主要壓縮不會影響MemStore的狀態;若要將其刷新到磁碟並壓縮它,您需要使用flush (connection.getAdmin().flush(TableName.valueOf(tblName)))。

8. 設定與效能

正如已經提到的,HBase 在執行 BulkLoad 時不需要執行任何操作,從而顯示出其最大的成功。 然而,這適用於大多數系統和人。 然而,該工具更適合以大塊的方式批量存儲數據,而如果該過程需要多個競爭的讀寫請求,則使用上述的 Get 和 Put 命令。 為了確定最佳參數,使用表格參數和設定的各種組合進行了啟動:

  • 10 個執行緒連續同時啟動 3 次(我們稱之為執行緒區塊)。
  • 將一個區塊內所有執行緒的運行時間進行平均,即為該區塊運行的最終結果。
  • 所有線程都使用同一個表。
  • 在線程塊的每次啟動之前,都會執行一次主要壓縮。
  • 每個區塊僅執行以下操作之一:

-放
-得到
—取得+放置

  • 每個區塊執行 50 次迭代操作。
  • 記錄的區塊大小為 100 位元組、1000 位元組或 10000 位元組(隨機)。
  • 使用不同數量的請求金鑰(10 個金鑰或 XNUMX 個金鑰)啟動區塊。
  • 這些區塊在不同的表設定下運行。 參數更改:

— BlockCache = 開啟或關閉
— 區塊大小 = 65 KB 或 16 KB
— 分區 = 1、5 或 30
— MSLAB = 啟用或停用

所以該塊看起來像這樣:

A。 MSLAB 模式已開啟/關閉。
b. 建立了一個表,並為其設定了以下參數:BlockCache = true/none、BlockSize = 65/16 Kb、Partition = 1/5/30。
C。 壓縮設定為 GZ。
d. 同時啟動 10 個線程,對該記錄為 1/10/100 位元組的表執行 1000/10000 put/get/get+put 操作,連續執行 50 個查詢(隨機鍵)。
e. d點重複XNUMX次。
F。 對所有執行緒的運行時間進行平均。

測試了所有可能的組合。 可以預見的是,速度會隨著記錄大小的增加而下降,或者停用快取會導致速度變慢。 然而,我們的目標是了解每個參數影響的程度和顯著性,因此收集的資料被輸入到線性迴歸函數的輸入中,這使得可以使用 t 統計量評估顯著性。 以下是區塊執行 Put 操作的結果。 全套組合 2*2*3*2*3 = 144 個選項 + 72 個 tk。 有些做了兩次。 因此,總共有 216 次運行:

HBase使用理論與實踐
測試是在由 3 個 DataNode 和 4 個 RS(CPU Xeon E5-2680 v4 @ 2.40GHz * 64 執行緒)組成的迷你叢集上進行的。 HBase 版本 1.2.0-cdh5.14.2。

關閉MSLAB模式,在一個分區的表上,啟用BlockCache,BlockSize = 3.7,記錄16字節,每包100條,最高插入速度為10秒。
最低插入速度為 82.8 秒,啟用 MSLAB 模式,在一個分區的表上,啟用 BlockCache,BlockSize = 16,記錄 10000 字節,每條 1 個字節。

現在讓我們來看看模型。 我們看到基於 R2 的模型具有良好的質量,但絕對清楚的是,此處禁止外推。 參數變化時系統的實際行為不會是線性的;該模型不是用於預測,而是用於理解給定參數內發生的情況。 例如,這裡我們從 Student 的標準看到,BlockSize 和 BlockCache 參數對於 Put 操作並不重要(這通常是可以預測的):

HBase使用理論與實踐
但增加分區數量導致效能下降的事實有些出乎意料(我們已經看到了使用 BulkLoad 增加分區數量的積極影響),儘管可以理解。 首先,為了進行處理,您必須向 30 個區域(而不是 XNUMX 個)產生請求,且資料量不足以產生效益。 其次,總運行時間由最慢的RS決定,由於DataNode的數量小於RS的數量,因此某些區域的局部性為零。 好吧,讓我們看看前五名:

HBase使用理論與實踐
現在讓我們評估執行 Get 區塊的結果:

HBase使用理論與實踐
分區的數量已經失去了意義,這可能是因為資料快取良好且讀取快取是最重要的(統計上)參數。 當然,增加請求中的訊息數量對於效能也非常有用。 最高分:

HBase使用理論與實踐
好吧,最後我們來看看先執行get然後put的block的模型:

HBase使用理論與實踐
所有參數在這裡都很重要。 以及領導者的結果:

HBase使用理論與實踐

9.負載測試

好吧,最後我們將推出或多或少不錯的負載,但當你有東西可以比較時,它總是更有趣。 Cassandra的主要開發商DataStax的網站上有 結果 NT 的許多 NoSQL 存儲,包括 HBase 版本 0.98.6-1。 載入採用40個線程,資料大小100字節,SSD磁碟。 測試讀取-修改-寫入操作的結果顯示以下結果。

HBase使用理論與實踐
據我了解,讀取是以100筆記錄為單位進行的,對於16個HBase節點,DataStax測試顯示每秒10萬次操作的效能。

幸運的是,我們的叢集也有16 個節點,但不太「幸運」的是每個節點都有64 個核心(線程),而在DataStax 測試中只有4 個。另一方面,他們有SSD 驅動器,而我們有HDD或更多新版本的 HBase 和負載期間的 CPU 使用率實際上並沒有顯著增加(視覺上增加了 5-10%)。 不過,讓我們嘗試開始使用此配置。 預設表設置,讀取是在 0 到 50 萬的鍵範圍內隨機執行的(即每次本質上都是新的)。 表格包含 50 萬筆記錄,分為 64 個分區。 密鑰使用 crc32 進行哈希處理。 表設置為預設設置,MSLAB 已啟用。 啟動 40 個線程,每個線程讀取一組 100 個隨機金鑰,並立即將產生的 100 個位元組寫回這些金鑰。

HBase使用理論與實踐
機架:16 個 DataNode 和 16 個 RS(CPU Xeon E5-2680 v4 @ 2.40GHz * 64 執行緒)。 HBase 版本 1.2.0-cdh5.14.2。

平均結果接近每秒 40 萬次操作,明顯優於 DataStax 測試。 但是,出於實驗目的,您可以稍微更改條件。 所有工作不太可能只在一張表上執行,並且也僅在唯一鍵上執行。 我們假設有一組特定的“熱”鍵產生主要負載。 因此,讓我們嘗試在10 個不同的表中建立具有較大記錄(100 KB) 的負載,同樣以4 條為批次,並將請求的鍵的範圍限制為50。下圖顯示了啟動40 個線程,每個線程讀取一組 100 個密鑰,並立即在這些密鑰上寫入隨機 10 KB。

HBase使用理論與實踐
機架:16 個 DataNode 和 16 個 RS(CPU Xeon E5-2680 v4 @ 2.40GHz * 64 執行緒)。 HBase 版本 1.2.0-cdh5.14.2。

在載入過程中,會多次啟動Major Compaction,如上圖,如果沒有這個過程,效能會逐漸下降,但執行過程中也會產生額外的負載。 虧損是由多種原因造成的。 有時執行緒完成工作並在重新啟動時暫停,有時第三方應用程式在叢集上創建了負載。

閱讀和立即寫是HBase最困難的工作場景之一。 如果只發出小的 put 請求,例如 100 個字節,將它們組合成 10-50 萬塊的包,每秒可以獲得數十萬次操作,情況與唯讀請求類似。 值得注意的是,結果比 DataStax 獲得的結果要好得多,這主要是由於 50 萬個區塊的請求。

HBase使用理論與實踐
機架:16 個 DataNode 和 16 個 RS(CPU Xeon E5-2680 v4 @ 2.40GHz * 64 執行緒)。 HBase 版本 1.2.0-cdh5.14.2。

10。結論

此系統配置相當靈活,但大量參數的影響仍未知。 其中一些經過了測試,但未包含在最終的測試集中。 例如,初步實驗顯示 DATA_BLOCK_ENCODING 這樣的參數意義不大,它使用相鄰單元格的值對資訊進行編碼,這對於隨機產生的資料是可以理解的。 如果您使用大量重複對象,增益可能會很大。 總的來說,我們可以說HBase給人的印像是一個相當嚴肅且經過深思熟慮的資料庫,在對大數據塊執行操作時可以非常有效率。 特別是如果可以及時分離讀取和寫入過程。

如果您認為有什麼披露不夠的地方,我願意更詳細地告訴您。 如果您不同意某些內容,我們邀請您分享您的經驗或進行討論。

來源: www.habr.com

添加評論