Hbase性能優化百科全書

file

本文集合了小編在日常學習和生產實踐中遇到的使用Hbase中的各種問題和優化方法,分別從表設計、rowkey設計、內存、讀寫、配置等各個領域對Hbase常用的調優方式進行了總結,希望能對讀者有幫助。本文參考結合自己實際優化經驗,參考了大量官網和各個前輩的經驗,生產環境中的Hbase集羣支撐了約50萬/s的讀和25萬/s的寫流量洪峯。感謝各位的經驗和付出。

HBase簡介

HBase是一個分佈式的、面向列的開源數據庫存儲系統,是對Google論文BigTable的實現,具有高可靠性、高性能和可伸縮性,它可以處理分佈在數千臺通用服務器上的PB級的海量數據。BigTable的底層是通過GFS(Google文件系統)來存儲數據,而HBase對應的則是通過HDFS(Hadoop分佈式文件系統)來存儲數據的。

HBase不同於一般的關係型數據庫,它是一個適合於非結構化數據存儲的數據庫。HBase不限制存儲的數據的種類,允許動態的、靈活的數據模型。HBase可以在一個服務器集羣上運行,並且能夠根據業務進行橫向擴展。

Hbase有以下優點:

  • 海量存儲:HBase適合存儲PB級別的海量數據,在PB級別的數據以及採用廉價PC存儲的情況下,能在幾十到百毫秒內返回數據。這與HBase的記憶擴展性息息相關。正是因爲HBase的良好擴展性,才爲海量數據的存儲提供了便利。
  • 列式存儲:列式存儲,HBase是根據列族來存儲數據的。列族下面可以有非常多的列,列族在創建表的時候就必須指定,而不用指定列。
  • 極易擴展:HBase的擴展性主要體現在兩個方面,一個是基於上層處理能力(RegionServer)的擴展,一個是基於存儲能力(HDFS)的擴展。
  • 高併發:目前大部分使用HBase的架構,都是採用廉價PC,因此單個IO的延遲其實並不小,一般在幾十到上百ms之間。這裏說的高併發,主要是在併發的情況下,HBase的單個IO延遲下降並不多。
  • 稀疏:稀疏主要是針對HBase列的靈活性,在列族中,可以指定任意多的列,在列數據爲空的情況下,是不會佔用存儲空間。

file

從我們使用Hbase開始,開發和調優將會一直伴隨在系統的整個生命週期。筆者將對自己熟悉和使用過的調優方式進行一一歸納和總結。

表的設計之預分區

HBase表在剛剛被創建時,只有1個分區(region),當一個region過大(達到hbase.hregion.max.filesize屬性中定義的閾值,默認10GB)時表將會進行split,分裂爲2個分區。表在進行split的時候,會耗費大量的資源,頻繁的分區對HBase的性能有巨大的影響。HBase提供了預分區功能,即用戶可以在創建表的時候對錶按照一定的規則分區。

如果業務要進行預分區,首先要明確rowkey的取值範圍或構成邏輯,假設我們的rowkey組成爲例:兩位隨機數+時間戳+客戶號,兩位隨機數的範圍從00-99,於是我劃分了10個region來存儲數據,每個region對應的rowkey範圍如下:-10,10-20,20-30,30-40,40-50,50-60,60-70,70-80,80-90,90-。

表的設計之rowkey

我們在之前的文章《HBase優化筆記》《設計HBase RowKey需要注意的二三事》中詳細講解過。

在HBase中,定位一條數據(即一個Cell)需要4個維度的限定:行鍵(RowKey)、列族(Column Family)、列限定符(Column Qualifier)、時間戳(Timestamp)。其中,RowKey是最容易出現問題的。除了根據業務和查詢需求來設計之外,還需要注意以下三點。

打散RowKey

HBase中的行是按照RowKey字典序排序的。
這對Scan操作非常友好,因爲RowKey相近的行總是存儲在相近的位置,順序讀的效率比隨機讀要高。
但是,如果大量的讀寫操作總是集中在某個RowKey範圍,那麼就會造成Region熱點,拖累RegionServer的性能。
因此,要適當地將RowKey打散。

加鹽(salting)+哈希(hashing)

這裏的“加鹽”與密碼學中的“加鹽”不是一回事。
它是指在RowKey的前面增加一些前綴。
加鹽的前綴種類越多,RowKey就被打得越散。
前綴不可以是隨機的,因爲必須要讓客戶端能夠完整地重構RowKey。
我們一般會拿原RowKey或其一部分計算hash值,然後再對hash值做運算作爲前綴。

反轉固定格式的數值

以手機號爲例,手機號的前綴變化比較少(如152、185等),但後半部分變化很多。
如果將它反轉過來,可以有效地避免熱點。
不過其缺點就是失去了有序性。
反轉時間
這個操作嚴格來講不算“打散”,但可以調整數據的時間排序。
如果將時間按照字典序排列,最近產生的數據會排在舊數據後面。
如果用一個大值減去時間(比如用99999999減去yyyyMMdd,或者Long.MAX_VALUE減去時間戳),最新的數據就可以排在前面了。

控制RowKey長度

在HBase中,RowKey、列族、列名等都是以byte[]形式傳輸的。
RowKey的最大長度限制爲64KB,但在實際應用中最多不會超過100B。
設計短RowKey有以下兩方面考慮:

在HBase的底層存儲HFile中,RowKey是KeyValue結構中的一個域。假設RowKey長度100B,那麼1000萬條數據中,只算RowKey就佔用掉將近1G空間,會影響HFile的存儲效率。
file

HBase中設計有MemStore和BlockCache,分別對應列族/Store級別的寫入緩存,和RegionServer級別的讀取緩存。如果RowKey過長,緩存中存儲數據的密度就會降低,影響數據落地或查詢效率。
file

另外,我們目前使用的服務器操作系統都是64位系統,內存是按照8B對齊的,因此設計RowKey時一般做成8B的整數倍,如16B或者24B,可以提高尋址效率。
同樣地,列族、列名的命名在保證可讀的情況下也應儘量短。HBase官方不推薦使用3個以上列族,因此實際上列族命名幾乎都用一個字母,比如‘c’或‘f’。

保證RowKey唯一性

這個就是顯而易見的了,不再贅述。

JVM調優

這部分我們可以參考禪克大佬發表的一些關於Hbase 內存設置的東西。

合理配置 JVM 內存

首先涉及 HBase 服務的堆內存設置。一般剛部署的 HBase 集羣,默認配置只給 Master 和 RegionServer 分配了 1G 的內存,RegionServer 中 MemStore 默認佔 0.4 即 400MB 左右的空間,而一個 MemStore 刷寫閾值默認 128M,所以一個 RegionServer 也就能正常管理 3 個Region,多了就可能會產生小文件了,另外也容易發生 Full GC。因此建議合理調整 Master 和 RegionServer 的內存,比如:

export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -Xms8g -Xmx8g"
export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -Xms32g -Xmx32g"

這裏也要根據實際集羣資源進行配置,另外要牢記至少留 10% 的內存給操作系統使用。

選擇合適的GC策略

另一個重要方面是 HBase JVM 的 GC 優化,其實 HBase 讀寫路徑上的很多設計都是圍繞 GC 優化做的。選擇合適的 GC 策略非常重要,對於 HBase 而言通常有兩種可選 GC 方案:

  • ParallelGC 和 CMS 組合
  • G1

CMS 避免不了 Full GC,而且 Full GC 場景下會通過一次串行的完整垃圾收集來回收碎片化的內存,這個過程通常會比較長,應用線程會發生長時間的 STW 停頓,不響應任何請求;而 G1 適合大內存的場景,通過把堆內存劃分爲多個 Region(不是 HBase 中的 Region),然後對各個 Region 單獨進行 GC,這樣就具有了並行整理內存碎片的功能,可以最大限度的避免 Full GC 的到來,提供更加合理的停頓時間。

由於 Master 只是做一些管理操作,實際處理讀寫請求和存儲數據的都是 RegionServer 節點,所以一般內存問題都出在 RegionServer 上。

這裏給的建議是,小堆(4G及以下)選擇 CMS,大堆(32G及以上)考慮用 G1,如果堆內存介入 4~32G 之間,可自行測試下兩種方案。剩下來的就是 GC 參數調優了。

開啓 MSLAB 功能

這是 HBase 自己實現了一套以 MemStore 爲最小單元的內存管理機制,稱爲 MSLAB(MemStore-Local Allocation Buffer),主要作用是爲了減少內存碎片化,改善 Full GC 發生的情況。

MemStore 會在內部維護一個 2M 大小的 Chunk 數組,當寫入數據時會先申請 2M 的 Chunk,將實際數據寫入該 Chunk中,當該 Chunk 滿了以後會再申請一個新的 Chunk。這樣 MemStore Flush 後會達到粗粒度化的內存碎片效果,可以有效降低 Full GC 的觸發頻率。

HBase 默認是開啓 MSLAB 功能的,和 MSLAB 相關的配置包括:

  • hbase.hregion.memstore.mslab.enabled:MSLAB 開關,默認爲 true,即打開 MSLAB。
  • hbase.hregion.memstore.mslab.chunksize:每個 Chunk 的大 小,默認爲 2MB,建議保持默認值。
  • hbase.hregion.memstore.chunkpool.maxsize:內部 Chunk Pool 功能,默認爲 0 ,即關閉 Chunk Pool 功能。設置爲大於 0 的值才能開啓,取值範圍爲 [0,1],表示 Chunk Pool 佔整個 MemStore 內存大小的比例。
  • hbase.hregion.memstore.chunkpool.initialsize:表示初始化時申請多少個 Chunk 放到 Chunk Pool 中,默認爲 0,即初始化時不申請 Chuck,只在寫入數據時才申請。
  • hbase.hregion.memstore.mslab.max.allocation:表示能放入 MSLAB 的最大單元格大小,默認爲 256KB,超過該大小的數據將從 JVM 堆分配空間而不是 MSLAB。

出於性能優化考慮,建議檢查相關配置,確保 MSLAB 處於開啓狀態。

考慮開啓 BucketCache

這塊涉及到讀緩存 BlockCache 的策略選擇。首先,BlockCache 是 RegionServer 級別的,一個 RegionServer 只有一個 BlockCache。BlockCache 的工作原理是讀請求會首先檢查 Block 是否存在於 BlockCache,存在就直接返回,如果不存在再去 HFile 和 MemStore 中獲取,返回數據時把 Block 緩存到 BlockCache 中,後續同一請求或臨近查詢可以直接從 BlockCache 中獲取,避免過多的昂貴 IO 操作。BlockCache 默認是開啓的。

目前 BlockCache 的實現方案有三種:

(1) LRUBlockCache
最早的 BlockCache 方案,也是 HBase 目前默認的方案。LRU 是 Least Recently Used 的縮寫,稱爲近期最少使用算法。LRUBlockCache 參考了 JVM 分代設計的思想,採用了緩存分層設計。

LRUBlockCache 將整個 BlockCache 分爲 single-access(單次讀取區)、multi-access(多次讀取區)和 in-memory 三部分,默認分別佔讀緩存的25%、50%、25%。其中設置 IN_MEMORY=true 的列族,Block 被讀取後纔會直接放到 in-memory 區,因此建議只給那些數據量少且訪問頻繁的列族設置 IN_MEMORY 屬性。另外,HBase 元數據比如 meta 表、namespace 表也都緩存在 in-memory 區。

(2) SlabCache
HBase 0.92 版本引入的一種方案,起初是爲了避免 Full GC 而引入的一種堆外內存方案,並與 LRUBlockCache 搭配使用,後來發現它對 Full GC 的改善很小,以至於這個方案基本被棄用了。

(3) BucketCache
HBase 0.96 版本引入的一種方案,它借鑑了 SlabCache 的設計思想,是一種非常高效的緩存方案。實際應用中,HBase 將 BucketCache 和 LRUBlockCache 搭配使用,稱爲組合模式(CombinedBlockCahce),具體地說就是把不同類型的 Block 分別放到 LRUBlockCache 和 BucketCache 中。

HBase 會把 Index Block 和 Bloom Block 放到 LRUBlockCache 中,將 Data Block 放到 BucketCache 中,所以讀取數據會去 LRUBlockCache 查詢一下 Index Block,然後再去 BucketCache 中查詢真正的數據。

BucketCache 涉及的常用參數有:

  • hbase.bucketcache.ioengine:使用的存儲介質,可設置爲 heap、offheap 或 file,其中 heap 表示空間從JVM堆中申請,offheap 表示使用 DirectByteBuffer 技術實現堆外內存管理,file 表示使用類似 SSD 等存儲介質緩存數據。默認值爲空,即關閉 BucketCache,一般建議開啓 BucketCache。此外,HBase 2.x 不再支持 heap 選型。
  • hbase.bucketcache.combinedcache.enabled:是否打開 CombinedBlockCache 組合模式,默認爲 true。此外,HBase 2.x 不再支持該參數。
  • hbase.bucketcache.size:BucketCache 大小,取值有兩種,一種是[0,1]之間的浮點數值,表示佔總內存的百分比,另一種是大於1的值,表示佔用內存大小,單位 MB。

根據上面的分析,一般建議開啓 BucketCache,綜合考慮成本和性能,建議比較合理的介質是:LRUBlockCache 使用內存,BuckectCache 使用SSD,HFile 使用機械磁盤。

合理配置讀寫緩存比例

HBase 爲了優化性能,在讀寫路徑上分別設置了讀緩存和寫緩存,參數分別是 hfile.block.cache.size 與 hbase.regionserver.global.memstore.size,默認值都是 0.4,表示讀寫緩存各佔 RegionServer 堆內存的 40%。

在一些場景下,我們可以適當調整兩部分比例,比如寫多讀少的場景下我們可以適當調大寫緩存,讓 HBase 更好的支持寫業務,相反類似,總之兩個參數要配合調整。

讀優化

我們在之前《HBase生產環境優化不完全指南》一文中也提到過,大家可以參考這裏。範欣欣前輩曾經就讀寫問題提過非常好的建議(http://hbasefly.com/),非常建議大家仔細讀一讀。

file

HBase客戶端優化

和大多數系統一樣,客戶端作爲業務讀寫的入口,姿勢使用不正確通常會導致本業務讀延遲較高實際上存在一些使用姿勢的推薦用法,這裏一般需要關注四個問題:

1. scan緩存是否設置合理?

優化原理:在解釋這個問題之前,首先需要解釋什麼是scan緩存,通常來講一次scan會返回大量數據,因此客戶端發起一次scan請求,實際並不會一次就將所有數據加載到本地,而是分成多次RPC請求進行加載,這樣設計一方面是因爲大量數據請求可能會導致網絡帶寬嚴重消耗進而影響其他業務,另一方面也有可能因爲數據量太大導致本地客戶端發生OOM。在這樣的設計體系下用戶會首先加載一部分數據到本地,然後遍歷處理,再加載下一部分數據到本地處理,如此往復,直至所有數據都加載完成。數據加載到本地就存放在scan緩存中,默認100條數據大小。

通常情況下,默認的scan緩存設置就可以正常工作的。但是在一些大scan(一次scan可能需要查詢幾萬甚至幾十萬行數據)來說,每次請求100條數據意味着一次scan需要幾百甚至幾千次RPC請求,這種交互的代價無疑是很大的。因此可以考慮將scan緩存設置增大,比如設爲500或者1000就可能更加合適。筆者之前做過一次試驗,在一次scan掃描10w+條數據量的條件下,將scan緩存從100增加到1000,可以有效降低scan請求的總體延遲,延遲基本降低了25%左右。

優化建議:大scan場景下將scan緩存從100增大到500或者1000,用以減少RPC次數

2. get請求是否可以使用批量請求?

優化原理:HBase分別提供了單條get以及批量get的API接口,使用批量get接口可以減少客戶端到RegionServer之間的RPC連接數,提高讀取性能。另外需要注意的是,批量get請求要麼成功返回所有請求數據,要麼拋出異常。

優化建議:使用批量get進行讀取請求

3. 請求是否可以顯示指定列族或者列?

優化原理:HBase是典型的列族數據庫,意味着同一列族的數據存儲在一起,不同列族的數據分開存儲在不同的目錄下。如果一個表有多個列族,只是根據Rowkey而不指定列族進行檢索的話不同列族的數據需要獨立進行檢索,性能必然會比指定列族的查詢差很多,很多情況下甚至會有2倍~3倍的性能損失。

優化建議:可以指定列族或者列進行精確查找的儘量指定查找

4. 離線批量讀取請求是否設置禁止緩存?

優化原理:通常離線批量讀取數據會進行一次性全表掃描,一方面數據量很大,另一方面請求只會執行一次。這種場景下如果使用scan默認設置,就會將數據從HDFS加載出來之後放到緩存。可想而知,大量數據進入緩存必將其他實時業務熱點數據擠出,其他業務不得不從HDFS加載,進而會造成明顯的讀延遲毛刺

優化建議:離線批量讀取請求設置禁用緩存,scan.setBlockCache(false)

HBase服務器端優化

一般服務端端問題一旦導致業務讀請求延遲較大的話,通常是集羣級別的,即整個集羣的業務都會反映讀延遲較大。可以從4個方面入手:

5. 讀請求是否均衡?

優化原理:極端情況下假如所有的讀請求都落在一臺RegionServer的某幾個Region上,這一方面不能發揮整個集羣的併發處理能力,另一方面勢必造成此臺RegionServer資源嚴重消耗(比如IO耗盡、handler耗盡等),落在該臺RegionServer上的其他業務會因此受到很大的波及。可見,讀請求不均衡不僅會造成本身業務性能很差,還會嚴重影響其他業務。當然,寫請求不均衡也會造成類似的問題,可見負載不均衡是HBase的大忌。

觀察確認:觀察所有RegionServer的讀請求QPS曲線,確認是否存在讀請求不均衡現象

優化建議:RowKey必須進行散列化處理(比如MD5散列),同時建表必須進行預分區處理

6. BlockCache是否設置合理?

優化原理:BlockCache作爲讀緩存,對於讀性能來說至關重要。默認情況下BlockCache和Memstore的配置相對比較均衡(各佔40%),可以根據集羣業務進行修正,比如讀多寫少業務可以將BlockCache佔比調大。另一方面,BlockCache的策略選擇也很重要,不同策略對讀性能來說影響並不是很大,但是對GC的影響卻相當顯著,尤其BucketCache的offheap模式下GC表現很優越。另外,HBase 2.0對offheap的改造(HBASE-11425)將會使HBase的讀性能得到2~4倍的提升,同時GC表現會更好!

觀察確認:觀察所有RegionServer的緩存未命中率、配置文件相關配置項一級GC日誌,確認BlockCache是否可以優化

優化建議:JVM內存配置量 < 20G,BlockCache策略選擇LRUBlockCache;否則選擇BucketCache策略的offheap模式;期待HBase 2.0的到來!

7. HFile文件是否太多?

優化原理:HBase讀取數據通常首先會到Memstore和BlockCache中檢索(讀取最近寫入數據&熱點數據),如果查找不到就會到文件中檢索。HBase的類LSM結構會導致每個store包含多數HFile文件,文件越多,檢索所需的IO次數必然越多,讀取延遲也就越高。文件數量通常取決於Compaction的執行策略,一般和兩個配置參數有關:hbase.hstore.compactionThreshold和hbase.hstore.compaction.max.size,前者表示一個store中的文件數超過多少就應該進行合併,後者表示參數合併的文件大小最大是多少,超過此大小的文件不能參與合併。這兩個參數不能設置太’松’(前者不能設置太大,後者不能設置太小),導致Compaction合併文件的實際效果不明顯,進而很多文件得不到合併。這樣就會導致HFile文件數變多。

觀察確認:觀察RegionServer級別以及Region級別的storefile數,確認HFile文件是否過多

優化建議:hbase.hstore.compactionThreshold設置不能太大,默認是3個;設置需要根據Region大小確定,通常可以簡單的認爲hbase.hstore.compaction.max.size = RegionSize / hbase.hstore.compactionThreshold

8. Compaction是否消耗系統資源過多?

優化原理:Compaction是將小文件合併爲大文件,提高後續業務隨機讀性能,但是也會帶來IO放大以及帶寬消耗問題(數據遠程讀取以及三副本寫入都會消耗系統帶寬)。正常配置情況下Minor Compaction並不會帶來很大的系統資源消耗,除非因爲配置不合理導致Minor Compaction太過頻繁,或者Region設置太大情況下發生Major Compaction。

觀察確認:觀察系統IO資源以及帶寬資源使用情況,再觀察Compaction隊列長度,確認是否由於Compaction導致系統資源消耗過多

優化建議:

(1)Minor Compaction設置:hbase.hstore.compactionThreshold設置不能太小,又不能設置太大,因此建議設置爲5~6;hbase.hstore.compaction.max.size = RegionSize / hbase.hstore.compactionThreshold

(2)Major Compaction設置:大Region讀延遲敏感業務( 100G以上)通常不建議開啓自動Major Compaction,手動低峯期觸發。小Region或者延遲不敏感業務可以開啓Major Compaction,但建議限制流量;

(3)期待更多的優秀Compaction策略,類似於stripe-compaction儘早提供穩定服務

HBase列族設計優化

HBase列族設計對讀性能影響也至關重要,其特點是隻影響單個業務,並不會對整個集羣產生太大影響。列族設計主要從兩個方面檢查:

9. Bloomfilter是否設置?是否設置合理?

優化原理:Bloomfilter主要用來過濾不存在待檢索RowKey或者Row-Col的HFile文件,避免無用的IO操作。它會告訴你在這個HFile文件中是否可能存在待檢索的KV,如果不存在,就可以不用消耗IO打開文件進行seek。很顯然,通過設置Bloomfilter可以提升隨機讀寫的性能。

Bloomfilter取值有兩個,row以及rowcol,需要根據業務來確定具體使用哪種。如果業務大多數隨機查詢僅僅使用row作爲查詢條件,Bloomfilter一定要設置爲row,否則如果大多數隨機查詢使用row+cf作爲查詢條件,Bloomfilter需要設置爲rowcol。如果不確定業務查詢類型,設置爲row。

優化建議:任何業務都應該設置Bloomfilter,通常設置爲row就可以,除非確認業務隨機查詢類型爲row+cf,可以設置爲rowcol

HDFS相關優化

HDFS作爲HBase最終數據存儲系統,通常會使用三副本策略存儲HBase數據文件以及日誌文件。從HDFS的角度望上層看,HBase即是它的客戶端,HBase通過調用它的客戶端進行數據讀寫操作,因此HDFS的相關優化也會影響HBase的讀寫性能。這裏主要關注如下三個方面:

10. Short-Circuit Local Read功能是否開啓?

優化原理:當前HDFS讀取數據都需要經過DataNode,客戶端會向DataNode發送讀取數據的請求,DataNode接受到請求之後從硬盤中將文件讀出來,再通過TPC發送給客戶端。Short Circuit策略允許客戶端繞過DataNode直接讀取本地數據。(具體原理參考此處)

優化建議:開啓Short Circuit Local Read功能。

11. Hedged Read功能是否開啓?

優化原理:HBase數據在HDFS中一般都會存儲三份,而且優先會通過Short-Circuit Local Read功能嘗試本地讀。但是在某些特殊情況下,有可能會出現因爲磁盤問題或者網絡問題引起的短時間本地讀取失敗,爲了應對這類問題,社區開發者提出了補償重試機制 – Hedged Read。該機制基本工作原理爲:客戶端發起一個本地讀,一旦一段時間之後還沒有返回,客戶端將會向其他DataNode發送相同數據的請求。哪一個請求先返回,另一個就會被丟棄。

優化建議:開啓Hedged Read功能。

12. 數據本地率是否太低?

數據本地率:HDFS數據通常存儲三份,假如當前RegionA處於Node1上,數據a寫入的時候三副本爲(Node1,Node2,Node3),數據b寫入三副本是(Node1,Node4,Node5),數據c寫入三副本(Node1,Node3,Node5),可以看出來所有數據寫入本地Node1肯定會寫一份,數據都在本地可以讀到,因此數據本地率是100%。現在假設RegionA被遷移到了Node2上,只有數據a在該節點上,其他數據(b和c)讀取只能遠程跨節點讀,本地率就爲33%(假設a,b和c的數據大小相同)。

優化原理:數據本地率太低很顯然會產生大量的跨網絡IO請求,必然會導致讀請求延遲較高,因此提高數據本地率可以有效優化隨機讀性能。數據本地率低的原因一般是因爲Region遷移(自動balance開啓、RegionServer宕機遷移、手動遷移等),因此一方面可以通過避免Region無故遷移來保持數據本地率,另一方面如果數據本地率很低,也可以通過執行major_compact提升數據本地率到100%。

優化建議:避免Region無故遷移,比如關閉自動balance、RS宕機及時拉起並遷回飄走的Region等;在業務低峯期執行major_compact提升數據本地率

HBase讀性能優化歸納

讀延遲較大無非三種常見的表象,單個業務慢、集羣隨機讀慢以及某個業務隨機讀之後其他業務受到影響導致隨機讀延遲很大。瞭解完常見的可能導致讀延遲較大的一些問題之後,我們將這些問題進行如下歸類,大家可以在看到現象之後在對應的問題列表中進行具體定位:

file
file
file

寫優化

和讀相比,HBase寫數據流程倒是顯得很簡單:數據先順序寫入HLog,再寫入對應的緩存Memstore,當Memstore中數據大小達到一定閾值(128M)之後,系統會異步將Memstore中數據flush到HDFS形成小文件。
HBase數據寫入通常會遇到兩類問題,一類是寫性能較差,另一類是數據根本寫不進去。這兩類問題的切入點也不盡相同,如下圖所示:
file

1. 是否需要寫WAL?WAL是否需要同步寫入?

優化原理:數據寫入流程可以理解爲一次順序寫WAL+一次寫緩存,通常情況下寫緩存延遲很低,因此提升寫性能就只能從WAL入手。WAL機制一方面是爲了確保數據即使寫入緩存丟失也可以恢復,另一方面是爲了集羣之間異步複製。默認WAL機制開啓且使用同步機制寫入WAL。首先考慮業務是否需要寫WAL,通常情況下大多數業務都會開啓WAL機制(默認),但是對於部分業務可能並不特別關心異常情況下部分數據的丟失,而更關心數據寫入吞吐量,比如某些推薦業務,這類業務即使丟失一部分用戶行爲數據可能對推薦結果並不構成很大影響,但是對於寫入吞吐量要求很高,不能造成數據隊列阻塞。這種場景下可以考慮關閉WAL寫入,寫入吞吐量可以提升2x~3x。退而求其次,有些業務不能接受不寫WAL,但可以接受WAL異步寫入,也是可以考慮優化的,通常也會帶來1x~2x的性能提升。

優化推薦:根據業務關注點在WAL機制與寫入吞吐量之間做出選擇

其他注意點:對於使用Increment操作的業務,WAL可以設置關閉,也可以設置異步寫入,方法同Put類似。相信大多數Increment操作業務對WAL可能都不是那麼敏感~

2. Put是否可以同步批量提交?

優化原理:HBase分別提供了單條put以及批量put的API接口,使用批量put接口可以減少客戶端到RegionServer之間的RPC連接數,提高寫入性能。另外需要注意的是,批量put請求要麼全部成功返回,要麼拋出異常。

優化建議:使用批量put進行寫入請求

3. Put是否可以異步批量提交?

優化原理:業務如果可以接受異常情況下少量數據丟失的話,還可以使用異步批量提交的方式提交請求。提交分爲兩階段執行:用戶提交寫請求之後,數據會寫入客戶端緩存,並返回用戶寫入成功;當客戶端緩存達到閾值(默認2M)之後批量提交給RegionServer。需要注意的是,在某些情況下客戶端異常的情況下緩存數據有可能丟失。

優化建議:在業務可以接受的情況下開啓異步批量提交

使用方式:setAutoFlush(false)

4. Region是否太少?

優化原理:當前集羣中表的Region個數如果小於RegionServer個數,即Num(Region of Table) < Num(RegionServer),可以考慮切分Region並儘可能分佈到不同RegionServer來提高系統請求併發度,如果Num(Region of Table) > Num(RegionServer),再增加Region個數效果並不明顯。

優化建議:在Num(Region of Table) < Num(RegionServer)的場景下切分部分請求負載高的Region並遷移到其他RegionServer;

5. 寫入請求是否不均衡?

優化原理:另一個需要考慮的問題是寫入請求是否均衡,如果不均衡,一方面會導致系統併發度較低,另一方面也有可能造成部分節點負載很高,進而影響其他業務。分佈式系統中特別害怕一個節點負載很高的情況,一個節點負載很高可能會拖慢整個集羣,這是因爲很多業務會使用Mutli批量提交讀寫請求,一旦其中一部分請求落到該節點無法得到及時響應,就會導致整個批量請求超時。因此不怕節點宕掉,就怕節點奄奄一息!

優化建議:檢查RowKey設計以及預分區策略,保證寫入請求均衡。

6. 寫入KeyValue數據是否太大?

KeyValue大小對寫入性能的影響巨大,一旦遇到寫入性能比較差的情況,需要考慮是否由於寫入KeyValue數據太大導致。KeyValue大小對寫入性能影響曲線圖如下:

file

圖中橫座標是寫入的一行數據(每行數據10列)大小,左縱座標是寫入吞吐量,右座標是寫入平均延遲(ms)。可以看出隨着單行數據大小不斷變大,寫入吞吐量急劇下降,寫入延遲在100K之後急劇增大。

說到這裏,有必要和大家分享兩起在生產線環境因爲業務KeyValue較大導致的嚴重問題,一起是因爲大字段業務寫入導致其他業務吞吐量急劇下降,另一起是因爲大字段業務scan導致RegionServer宕機。

寫異常問題檢查點

上述幾點主要針對寫性能優化進行了介紹,除此之外,在一些情況下還會出現寫異常,一旦發生需要考慮下面兩種情況(GC引起的不做介紹):
Memstore設置是否會觸發Region級別或者RegionServer級別flush操作?

問題解析:以RegionServer級別flush進行解析,HBase設定一旦整個RegionServer上所有Memstore佔用內存大小總和大於配置文件中upperlimit時,系統就會執行RegionServer級別flush,flush算法會首先按照Region大小進行排序,再按照該順序依次進行flush,直至總Memstore大小低至lowerlimit。這種flush通常會block較長時間,在日誌中會發現“Memstore is above high water mark and block 7452 ms”,表示這次flush將會阻塞7s左右。

問題檢查點:

Region規模與Memstore總大小設置是否合理?如果RegionServer上Region較多,而Memstore總大小設置的很小(JVM設置較小或者upper.limit設置較小),就會觸發RegionServer級別flush。

列族是否設置過多,通常情況下表列族建議設置在1~3個之間,最好一個。如果設置過多,會導致一個Region中包含很多Memstore,導致更容易觸到高水位upperlimit。

Store中HFile數量是否大於配置參數blockingStoreFile?

問題解析:對於數據寫入很快的集羣,還需要特別關注一個參數:hbase.hstore.blockingStoreFiles,此參數表示如果當前hstore中文件數大於該值,系統將會強制執行compaction操作進行文件合併,合併的過程會阻塞整個hstore的寫入。通常情況下該場景發生在數據寫入很快的情況下,在日誌中可以發現”Waited 3722ms on a compaction to clean up ‘too many store files“

問題檢查點:
參數設置是否合理?hbase.hstore.compactionThreshold表示啓動compaction的最低閾值,該值不能太大,否則會積累太多文件,一般建議設置爲5~8左右。hbase.hstore.blockingStoreFiles默認設置爲7,可以適當調大一些。

其他

最後我們講一講一些容易忽視的優化點:

使用壓縮

HBase支持大量的壓縮算法,可以從嫘祖級別上進行壓縮。因爲CPU壓縮和解壓縮消耗的時間往往比從磁盤讀取和寫入數據要快得多,所以使用壓縮通常會帶來很可觀的性能提升

file

大家可以自行百度關於壓縮算法在HBase中的集成安裝,HBase包含一個能夠測試壓縮設置是否正常的工具,我們可以輸入:

./bin/hbase org.apache.hadoop.hbase.util.CompresstionTest

進行檢測。

然後啓用檢查,因爲即使測試工具報告成功了,由於JNI需要先安裝好本地庫,如果缺失這一步將會在添加新服務器的時候出現問題,導致新的服務器使用本地庫打開含有壓縮列族的region失敗。

我們可以在服務器啓動的時候檢查壓縮庫是否已經正確安裝,如果沒有則不會啓動服務器:

<property>
    <name>hbase.regionserver.codecs</name>
    <value>snappy,lzo</value>
</property>

這樣一來region服務器在啓動的時候將會檢查Snappy和LZO壓縮庫是否已經正確安裝

我們可以通過shell創建表的時候指定列族的壓縮格式:

create 'testtable',{NAME => 'colfam1',COMPRESSION => 'GZ'}

需要注意的是,如果用戶要更改一個已經存在的表的壓縮格式,要先將該表disable才能修改之後再enable重新上線。並且更改後的region只有在刷寫存儲文件的時候纔會使用新的壓縮格式,沒有刷寫之前保持原樣,用戶可以通過shell的major_compact 來強制格式重寫,但是此操作會佔用大量資源。

使用掃描緩存

如果HBase作爲一個MapReduce作業的而輸入源,最好將MapReduce作業的輸入掃描器實例的緩存用setCaching()設置爲比1大的多的值。例如設置爲500的時候則一次可以傳送500行數據到客戶端進行處理。

限定掃描範圍

如果只處理少數列,則應當只有這些列被添加到Scan的輸入中,因爲如果沒有做篩選,則會掃描其他的數據存儲文件。

關閉ResultScanner

這不會帶來性能提升,但是會避免一些性能問題。所以一定要在try/catch中關閉ResultScanner。

參考文檔

https://www.cnblogs.com/panfeng412/archive/2012/03/08/hbase-performance-tuning-section1.html
https://zhuanlan.zhihu.com/p/75630694
https://www.jianshu.com/p/77545e359e1e
https://blog.csdn.net/wsdc0521/article/details/108536492
https://blog.csdn.net/lw_ghy/article/details/60779222
https://blog.csdn.net/ourpush/article/details/53558292
https://blog.csdn.net/qq1010885678/article/details/51957401

歡迎關注,《大數據成神之路》系列文章

歡迎關注,《大數據成神之路》系列文章

歡迎關注,《大數據成神之路》系列文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章