Hbase踩坑計

Hbase數據庫比較適用於寫多讀少的場景。其查詢能力並不突出。
在使用Scan掃描表的時候,很容易踩坑。數據庫

踩坑1:在表數據量偏大的狀況下(好比上千萬數據),執行scan提示60000ms的timeout,而且出現OOM。
分析:緩存

scan對象有幾個重要的參數:
  caching:該值表示一次從RPC請求Client能夠從Hbase服務器獲取的數據條數。
           默認值是-1。按照源碼,若是是-1,那麼發送給Hbase的值是:HConstants#DEFAULT_HBASE_CLIENT_SCANNER_CACHING,即:
           Integer.MAX_VALUE。 這是一個很是大的值。
           ![image.png](/img/bVcRFNj)

  maxResultSize:client從Hbase服務端獲取的數據在客戶端緩存的最大字節數。
                 默認是-1。若是使用的是默認值,則緩存大小限制是:2M(即 2 * 1024 * 1024)。   
                 ![image.png](/img/bVcRFNg)
                 
  limit:表示一次Scan掃描的行數,至關於MySql的limit。**須要注意的是:此參數值在2.x版本生效,1.x版本沒有此參數。**
         默認值是-1。若是是默認值,則不會使用該參數。

出現這個問題的緣由是Hbase-client採用的是2.x版本,可是Hbase服務端是1.x版本。使用limit參數對其無效。因此會一次去
服務端查詢Integer.MAX_VALUE條數據,致使OOM和timeout。           

在構造Hbase服務端的請求參數時,代碼分別以下(能夠看到2.x多了limitOfRows參數):
2.x版本:
public static ScanRequest buildScanRequest(byte[] regionName, Scan scan, int numberOfRows,
      boolean closeScanner) throws IOException {
    ScanRequest.Builder builder = ScanRequest.newBuilder();
    RegionSpecifier region = buildRegionSpecifier(RegionSpecifierType.REGION_NAME, regionName);
    builder.setNumberOfRows(numberOfRows);
    builder.setCloseScanner(closeScanner);
    builder.setRegion(region);
    builder.setScan(ProtobufUtil.toScan(scan));
    builder.setClientHandlesPartials(true);
    builder.setClientHandlesHeartbeats(true);
    builder.setTrackScanMetrics(scan.isScanMetricsEnabled());
    if (scan.getLimit() > 0) {
      builder.setLimitOfRows(scan.getLimit());
    }
    return builder.build();
  }

在1.x版本:服務器

public static ScanRequest buildScanRequest(final byte[] regionName, final Scan scan,
      final int numberOfRows, final boolean closeScanner) throws IOException {
    ScanRequest.Builder builder = ScanRequest.newBuilder();
    RegionSpecifier region = buildRegionSpecifier(
      RegionSpecifierType.REGION_NAME, regionName);
    builder.setNumberOfRows(numberOfRows);
    builder.setCloseScanner(closeScanner);
    builder.setRegion(region);
    builder.setScan(ProtobufUtil.toScan(scan));
    builder.setClientHandlesPartials(true);
    builder.setClientHandlesHeartbeats(true);
    builder.setTrackScanMetrics(scan.isScanMetricsEnabled());
    return builder.build();
  }

踩坑2:在進行Scan掃描的時候,隨着時間的推移,Scan的速度愈來愈慢。
分析:Scan有2個參數:ui

startRow:掃描的起始rowkey。
  filter:值過濾器,好比:RowFilter。 其處理代碼以下:
byte[] startRow = scan.getStartRow();
   if (startRow != null && startRow.length > 0) {
     scanBuilder.setStartRow(ByteStringer.wrap(startRow));
   }
   byte[] stopRow = scan.getStopRow();
   if (stopRow != null && stopRow.length > 0) {
     scanBuilder.setStopRow(ByteStringer.wrap(stopRow));
   }
   if (scan.hasFilter()) {
     scanBuilder.setFilter(ProtobufUtil.toFilter(scan.getFilter()));
   }

在使用scan進行全表掃描的時候,若是沒有指定startRow,那麼就會愈來愈慢。由於每次都是從頭開始掃描,因此會愈來愈慢。
因此startRow儘可能都要添加上。code

相關文章
相關標籤/搜索