HBase Scan 中 setCaching setMaxResultSize setBatch 解惑

0. 蜜汁参数

在做 HBase 客户端 scan 优化时,经常会碰到以下几个参数,总是让人迷惑 ,不知从何优化起。

  • .setCache (缓存大小? 字节数?行数?)
  • .setMaxResultSize (最大结果数?)
  • .setBatch (批量?)

造成这种困扰很大的原因是命名问题。
先说下结论,如果把名字改成如下,语义会清晰很多 。[1]

  • .setCaching => .setNumberOfRowsFetchSize (客户端每次 rpc fetch 的行数)
  • .setMaxResultSize => .setMaxResultByteSize (客户端缓存的最大字节数)
  • .setBatch => .setColumnsChunkSize (客户端每次获取的列数)

1. Client Scan 原理及相关源码解读

HBase 每次 scan 的数据量可能会比较大,客户端不会一次性全部把数据从服务端拉回来。而是通过多次 rpc 分批次的拉取。类似于 TCP 协议里面一段一段的传输,可以做到细粒度的流量控制。至于如何调优,控制每次 rpc 拉取的数据量,就可以通过以上三个比较蛋疼的参数来控制。

我们可以先看一段来自 HBase scan 里面的核心类 ClientScanner 里的读取逻辑,通过它来了解整个流程。

@Override
public Result next() throws IOException {
  // If the scanner is closed and there's nothing left in the cache, next is a no-op.
  if (cache.size() == 0 && this.closed) {
    return null;
  }

  // 缓冲中没有就 RPC 调用读取数据进缓存
  if (cache.size() == 0) {   
        loadCache();
  }
 
  // 缓冲中有直接从缓存中取
  if (cache.size() > 0) {
    return cache.poll();
  }
 
  // if we exhausted this scanner before calling close, write out the scan metrics
  writeScanMetrics();
  return null;
}

每次从缓存 cache 中读,缓存为空则 loadCache , 实际上 cache 是通过一个链表来实现的,定义如下:
protected final LinkedList<Result> cache = new LinkedList<Result>();

继续看 loadCache() ,为了弄清大体主流程,我删除了部分代码

 protected void loadCache() throws IOException {
    Result[] values = null;
    // 剩余最大容量
    long remainingResultSize = maxScannerResultSize;
    // 行数计数 为 setCaching 的值
    int countdown = this.caching;
    // 配置 rpc 请求的条数
    callable.setCaching(this.caching);
    boolean serverHasMoreResults = false;

    // do while 循环,循环次数即为 rpc 次数
    do {
      try {
        // rpc 从 server 拉数据,请求的条数为 this.caching 默认为 Integer.Max_VALUE
        values = call(callable, caller, scannerTimeout);
         } catch (DoNotRetryIOException | NeedUnmanagedConnectionException e) {
             // 异常处理 这里略过
       }
      // Groom the array of Results that we received back from the server before adding that
      // Results to the scanner's cache
      // 将数据放入缓存前,先对数据进行一些处理,主要是处理对于部分对调用这不可见的数据
      List<Result> resultsToAddToCache =
          getResultsToAddToCache(values, callable.isHeartbeatMessage());
      if (!resultsToAddToCache.isEmpty()) {
        // 遍历 results 写入 cache
        for (Result rs : resultsToAddToCache) {
          cache.add(rs);
          // We don't make Iterator here
          for (Cell cell : rs.rawCells()) {
            // 估算每个 cell 的大小,计算剩余 byte size 
            remainingResultSize -= CellUtil.estimatedHeapSizeOf(cell);
          }
          // 剩余行数 --
          countdown--;
          this.lastResult = rs;
        }
      }

      // We expect that the server won't have more results for us when we exhaust
      // the size (bytes or count) of the results returned. If the server *does* inform us that
      // there are more results, we want to avoid possiblyNextScanner(...). Only when we actually
      // get results is the moreResults context valid.
      if (null != values && values.length > 0 && callable.hasMoreResultsContext()) {
        serverHasMoreResults = callable.getServerHasMoreResults() & partialResults.isEmpty();
      }
      // Values == null means server-side filter has determined we must STOP
    } while (doneWithRegion(remainingResultSize, countdown, serverHasMoreResults)
        && (!partialResults.isEmpty() || possiblyNextScanner(countdown, values == null)));
     // 循环条件
  }

这里重点看下do while 循环的条件 doneWithRegion()

  /**
   * @param remainingResultSize
   * @param remainingRows
   * @param regionHasMoreResults
  */
  private boolean doneWithRegion(long remainingResultSize, int remainingRows,
      boolean regionHasMoreResults) {
    // 同时满足这些才行这里的
    // remainingResultSize 初始值即为配置的 setMaxResultSize 
   //  remainingRows 初始值为配置的 setCaching (实际为:行数)
    return remainingResultSize > 0 && remainingRows > 0 && !regionHasMoreResults;
  }

因为每次 while 循环会进行一次 rpc 调用,不同的参数配置组合配置导致 rpc 调用的次数不同。必须同时满足行数与字节数的的限制才行。

3. 官方配置与建议

这几个参数对应的配置如下

  • hbase.client.scanner.caching - (setCaching):HBase-0.98 默认值为为 100,HBase-1.2 默认值为 2147483647,即 Integer.MAX_VALUE。Scan.next() 的一次 RPC 请求 fetch 的记录条数。配置建议:这个参数与 下面的hbase.client.scanner.max.result.size - (setMaxResultSize) 配置使用,在网络状况良好的情况下,自定义设置不宜太小, 可以直接采用默认值,不配置。

  • hbase.client.scanner.max.result.size - (setMaxResultSize):HBase-0.98 无该项配置,HBase-1.2 默认值为 210241024,即 2M。Scan.next() 的一次 RPC 请求 fetch 的数据量大小,目前 HBase-1.2 在 Caching 为默认值(Integer Max)的时候,实际使用这个参数控制 RPC 次数和流量。配置建议:如果网络状况较好(万兆网卡),scan 的数据量非常大,可以将这个值配置高一点。如果配置过高:则可能 loadCache 速度比较慢,导致 scan timeout 异常

  • hbase.server.scanner.max.result.size:服务端配置。HBase-0.98 无该项配置,HBase-1.2 新增,默认值为 10010241024,即 100M。该参数表示当 Scan.next() 发起 RPC 后,服务端返回给客户端的最大字节数,防止 Server OOM。[2]

  • setBatch() 坑爹的命名,这个实际上是配置获取的列数,假如表有两个列簇 cf,info,每个列簇5个列。这样每行可能有10列了,setBatch() 可以控制每次获取的最大列数,进一步从列级别控制流量。配置建议:当列数很多,数据量大时考虑配置此参数,例如100列每次只获取50列。一般情况可以默认值(-1 不受限)。

参考:
[1] HBase client’s weird API names
[2] HBase Client配置参数说明

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • HBase那些事 @(大数据工程学院)[HBase, Hadoop, 优化, HadoopChen, hbase]...
    分痴阅读 3,925评论 3 17
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 该文档是用Hbase默认配置文件生成的,文件源是Hbase-default.xml hbase.rootdir 这...
    我是嘻哈大哥阅读 4,740评论 0 7
  • 摘自:http://debugo.com/hbase-params/ 通用和master配置hbase.rootd...
    wangliang938阅读 2,676评论 1 5
  • HBase扫描操作Scan 1 介绍 扫描操作的使用和get()方法类似。同样,和其他函数类似,这里也提供了Sca...
    yanzhelee阅读 8,972评论 0 5