HBase查詢優化

1.概述

HBase是一個實時的非關係型數據庫,用來存儲海量數據。可是,在實際使用場景中,在使用HBase API查詢HBase中的數據時,有時會發現數據查詢會很慢。本篇博客將從客戶端優化和服務端優化兩個方面來介紹,如何提升查詢HBase的效率。shell

2.內容

這裏,咱們先給你們介紹如何從客戶端優化查詢速度。數據庫

2.1 客戶端優化

客戶端查詢HBase,均經過HBase API的來獲取數據,若是在實現代碼邏輯時使用API不當,也會形成讀取耗時嚴重的狀況。apache

2.1.1 Scan優化

在使用HBase的Scan接口時,一次Scan會返回大量數據。客戶端向HBase發送一次Scan請求,實際上並不會將全部數據加載到本地,而是經過屢次RPC請求進行加載。這樣設計的好處在於避免大量數據請求會致使網絡帶寬負載太高影響其餘業務使用HBase,另外從客戶端的角度來講能夠避免數據量太大,從而本地機器發送OOM(內存溢出)。緩存

默認狀況下,HBase每次Scan會緩存100條,能夠經過屬性hbase.client.scanner.caching來設置。另外,最大值默認爲-1,表示沒有限制,具體實現見源代碼:網絡

/**
   * @return the maximum result size in bytes. See {@link #setMaxResultSize(long)}
   */
  public long getMaxResultSize() {
    return maxResultSize;
  }

  /**
   * Set the maximum result size. The default is -1; this means that no specific
   * maximum result size will be set for this scan, and the global configured
   * value will be used instead. (Defaults to unlimited).
   *
   * @param maxResultSize The maximum result size in bytes.
   */
  public Scan setMaxResultSize(long maxResultSize) {
    this.maxResultSize = maxResultSize;
    return this;
  }

通常狀況下,默認緩存100就能夠知足,若是數據量過大,能夠適當增大緩存值,來減小RPC次數,從而下降Scan的整體耗時。另外,在作報表呈現時,建議使用HBase分頁來返回Scan的數據。併發

2.1.2 Get優化

HBase系統提供了單條get數據和批量get數據,單條get一般是經過請求表名+rowkey,批量get一般是經過請求表名+rowkey集合來實現。客戶端在讀取HBase的數據時,實際是與RegionServer進行數據交互。在使用批量get時能夠有效的較少客戶端到各個RegionServer之間RPC鏈接數,從而來間接的提升讀取性能。批量get實現代碼見org.apache.hadoop.hbase.client.HTable類:負載均衡

public Result[] get(List<Get> gets) throws IOException {
    if (gets.size() == 1) {
      return new Result[]{get(gets.get(0))};
    }
    try {
      Object[] r1 = new Object[gets.size()];
      batch((List<? extends Row>)gets, r1, readRpcTimeoutMs);
      // Translate.
      Result [] results = new Result[r1.length];
      int i = 0;
      for (Object obj: r1) {
        // Batch ensures if there is a failure we get an exception instead
        results[i++] = (Result)obj;
      }
      return results;
    } catch (InterruptedException e) {
      throw (InterruptedIOException)new InterruptedIOException().initCause(e);
    }
  }

從實現的源代碼分析可知,批量get請求的結果,要麼所有返回,要麼拋出異常。ide

2.1.3 列簇和列優化

一般狀況下,HBase表設計咱們一個指定一個列簇就能夠知足需求,但也不排除特殊狀況,須要指定多個列簇(官方建議最多不超過3個),其實官方這樣建議也是有緣由的,HBase是基於列簇的非關係型數據庫,意味着相同的列簇數據會存放在一塊兒,而不一樣的列簇的數據會分開存儲在不一樣的目錄下。若是一個表設計多個列簇,在使用rowkey查詢而不限制列簇,這樣在檢索不一樣列簇的數據時,須要獨立進行檢索,查詢效率當然是比指定列簇查詢要低的,列簇越多,這樣影響越大。函數

而同一列簇下,可能涉及到多個列,在實際查詢數據時,若是一個表的列簇有上1000+的列,這樣一個大表,若是不指定列,這樣查詢效率也是會很低。一般狀況下,在查詢的時候,能夠查詢指定咱們須要返回結果的列,對於不須要的列,能夠不須要指定,這樣可以有效地的提升查詢效率,下降延時。oop

2.1.4 禁止緩存優化

批量讀取數據時會全表掃描一次業務表,這種提如今Scan操做場景。在Scan時,客戶端與RegionServer進行數據交互(RegionServer的實際數據時存儲在HDFS上),將數據加載到緩存,若是加載很大的數據到緩存時,會對緩存中的實時業務熱數據有影響,因爲緩存大小有限,加載的數據量過大,會將這些熱數據「擠壓」出去,這樣當其餘業務從緩存請求這些數據時,會從HDFS上從新加載數據,致使耗時嚴重。

在批量讀取(T+1)場景時,建議客戶端在請求是,在業務代碼中調用setCacheBlocks(false)函數來禁止緩存,默認狀況下,HBase是開啓這部分緩存的。源代碼實現爲:

/**
   * Set whether blocks should be cached for this Get.
   * <p>
   * This is true by default.  When true, default settings of the table and
   * family are used (this will never override caching blocks if the block
   * cache is disabled for that family or entirely).
   *
   * @param cacheBlocks if false, default settings are overridden and blocks
   * will not be cached
   */
  public Get setCacheBlocks(boolean cacheBlocks) {
    this.cacheBlocks = cacheBlocks;
    return this;
  }

  /**
   * Get whether blocks should be cached for this Get.
   * @return true if default caching should be used, false if blocks should not
   * be cached
   */
  public boolean getCacheBlocks() {
    return cacheBlocks;
  }

2.2 服務端優化

HBase服務端配置或集羣有問題,也會致使客戶端讀取耗時較大,集羣出現問題,影響的是整個集羣的業務應用。

2.2.1 負載均衡優化

客戶端的請求其實是與HBase集羣的每一個RegionServer進行數據交互,在細分一下,就是與每一個RegionServer上的某些Region進行數據交互,每一個RegionServer上的Region個數上的狀況下,可能這種耗時狀況影響不大,體現不夠明顯。可是,若是每一個RegionServer上的Region個數較大的話,這種影響就會很嚴重。筆者這裏作過統計的數據統計,當每一個RegionServer上的Region個數超過800+,若是發生負載不均衡,這樣的影響就會很嚴重。

可能有同窗會有疑問,爲何會發送負載不均衡?負載不均衡爲何會形成這樣耗時嚴重的影響?

1.爲何會發生負載不均衡?

負載不均衡的影響一般由如下幾個因素形成:

  • 沒有開啓自動負載均衡
  • 集羣維護,擴容或者縮減RegionServer節點
  • 集羣有RegionServer節點發生宕機或者進程中止,隨後守護進程又自動拉起宕機的RegionServer進程

針對這些因素,能夠經過如下解決方案來解決:

  • 開啓自動負載均衡,執行命令:echo "balance_switch true" | hbase shell
  • 在維護集羣,或者守護進程拉起中止的RegionServer進程時,定時調度執行負載均衡命令:echo "balancer" | hbase shell

2.負載不均衡爲何會形成這樣耗時嚴重的影響?

這裏筆者用一個例子來講,集羣每一個RegionServer包含由800+的Region數,可是,因爲集羣維護,有幾臺RegionServer節點的Region所有集中到一臺RegionServer,分佈以下圖所示:

這樣以前請求在RegionServer2和RegionServer3上的,都會集中到RegionServer1上去請求。這樣就不能發揮整個集羣的併發處理能力,另外,RegionServer1上的資源使用將會翻倍(好比網絡、磁盤IO、HBase RPC的Handle數等)。而原先其餘正常業務到RegionServer1的請求也會所以受到很大的影響。所以,讀取請求不均衡不只會形成自己業務性能很長,還會嚴重影響其餘正常業務的查詢。同理,寫請求不均衡,也會形成相似的影響。故HBase負載均衡是HBase集羣性能的重要體現。

2.2.2 BlockCache優化

BlockCache做爲讀緩存,合理設置對於提升讀性能很是重要。默認狀況下,BlockCache和Memstore的配置各站40%,能夠經過在hbase-site.xml配置如下屬性來實現:

  • hfile.block.cache.size,默認0.4,用來提升讀性能
  • hbase.regionserver.global.memstore.size,默認0.4,用來提升寫性能

本篇博客主要介紹提升讀性能,這裏咱們能夠將BlockCache的佔比設置大一些,Memstore的佔比設置小一些(總佔比保持在0.8便可)。另外,BlockCache的策略選擇也是很重要的,不一樣的策略對於讀性能來講影響不大,可是對於GC的影響卻比較明顯,在設置hbase.bucketcache.ioengine屬性爲offheap時,GC表現的很優秀。緩存結構以下圖所示:

設置BlockCache能夠在hbase-site.xml文件中,配置以下屬性:

<!-- 分配的內存大小盡量的多些,前提是不能超過 (機器實際物理內存-JVM內存) -->
<property>  
   <name>hbase.bucketcache.size</name>  
   <value>16384</value> 
</property>
<property>
 <name>hbase.bucketcache.ioengine</name>
 <value>offheap</value> 
</property>

設置塊內存大小,能夠參考入下表格:

標號 描述 計算公式或值 結果
A 物理內存選擇:on-heap(JVM)+off-heap(Direct) 單臺物理節點內存值,單位MB 262144
B HBASE_HEAPSIZE('-Xmx) 單位MB 20480
C -XX:MaxDirectMemorySize,off-heap容許的最大內存值 A-B 241664
Dp hfile.block.cache.size和hbase.regionserver.global.memstore.size總和不要超過0.8 讀取比例佔比*0.8 0.5*0.8=0.4
Dm JVM Heap容許的最大BlockCache(MB) B*Dp 20480*0.4=8192
Ep hbase.regionserver.global.memstore.size設置的最大JVM值 0.8-Dp 0.8-0.4=0.4
F 用於其餘用途的off-heap內存,例如DFSClient 推薦1024到2048 2048
G BucketCache容許的off-heap內存 C-F 241664-2048=239616

另外,BlockCache策略,可以有效的提升緩存命中率,這樣可以間接的提升熱數據覆蓋率,從而提高讀取性能。

2.2.3 HFile優化

HBase讀取數據時會先從BlockCache中進行檢索(熱數據),若是查詢不到,纔會到HDFS上去檢索。而HBase存儲在HDFS上的數據以HFile的形式存在的,文件若是越多,檢索所花費的IO次數也就必然增長,對應的讀取耗時也就增長了。文件數量取決於Compaction的執行策略,有如下2個屬性有關係:

  • hbase.hstore.compactionThreshold,默認爲3,表示store中文件數超過3個就開始進行合併操做
  • hbase.hstore.compaction.max.size,默認爲9223372036854775807,合併的文件最大閥值,超過這個閥值的文件不能進行合併

 另外,hbase.hstore.compaction.max.size值能夠經過實際的Region總數來計算,公式以下:

hbase.hstore.compaction.max.size = RegionTotal / hbase.hstore.compactionThreshold

2.2.4 Compaction優化

Compaction操做是將小文件合併爲大文件,提升後續業務隨機讀取的性能,可是在執行Compaction操做期間,節點IO、網絡帶寬等資源會佔用較多,那麼何時執行Compaction才最好?何時須要執行Compaction操做?

1.何時執行Compaction才最好?

實際應用場景中,會關閉Compaction自動執行策略,經過屬性hbase.hregion.majorcompaction來控制,將hbase.hregion.majorcompaction=0,就能夠禁止HBase自動執行Compaction操做。通常狀況下,選擇集羣負載較低,資源空閒的時間段來定時調度執行Compaction。

若是合併的文件較多,能夠經過設置以下屬性來提生Compaction的執行速度,配置以下:

<property>
    <name>hbase.regionserver.thread.compaction.large</name>
    <value>8</value>
    <description></description>
</property>
<property>
    <name>hbase.regionserver.thread.compaction.small</name>
    <value>5</value>
    <description></description>
</property>

2.何時須要執行Compaction操做?

通常維護HBase集羣后,因爲集羣發生太重啓,HBase數據本地性較低,經過HBase頁面能夠觀察,此時若是不執行Compaction操做,那麼客戶端查詢的時候,須要跨副本節點去查詢,這樣來回須要通過網絡帶寬,對比正常狀況下,從本地節點讀取數據,耗時是比較大的。在執行Compaction操做後,HBase數據本地性爲1,這樣可以有效的提升查詢效率。

3.總結

本篇博客HBase查詢優化從客戶端和服務端角度,列舉一些常見有效地優化手段。固然,優化還須要從本身實際應用場景出發,例如代碼實現邏輯、物理機的實際配置等方面來設置相關參數。你們能夠根據實際狀況來參考本篇博客進行優化。

4.結束語

這篇博客就和你們分享到這裏,若是你們在研究學習的過程中有什麼問題,能夠加羣進行討論或發送郵件給我,我會盡我所能爲您解答,與君共勉!

另外,博主出書了《Hadoop大數據挖掘從入門到進階實戰》,喜歡的朋友或同窗, 能夠在公告欄那裏點擊購買連接購買博主的書進行學習,在此感謝你們的支持。

相關文章
相關標籤/搜索