美文网首页
[HBase] - 一次由 Hive HBase 外部表引发的读

[HBase] - 一次由 Hive HBase 外部表引发的读

作者: 六层楼那么高 | 来源:发表于2022-05-17 20:57 被阅读0次

    1 问题

    近期业务反馈 HBase GET 读性能不佳,查看监控,每天有几个时间波段,GET P99 请求时延很高。

    2 问题原因

    定位有一个业务有 7 张 Hive HBase 外部表,每天有两个时间定时通过 sql 的方式,并发导出全表到 hive 。代码类似于:

     insert into table_hive select * from hive_hbase_external_table;
    

    相当于每天定时并发对 7 张大的 HBase 全表 ScanScan 默认会写缓存,这个会导致:

    1. Bucket Cache 被 scan block 写满, 缓存开始淘汰,出现 cache miss (缓存命中率降低) 和 cache eviction (缓存淘汰)。cache 命中率直接影响 HBase 读性能。
    2. 出现 I/O 风暴,读请求没有命中缓存需要读 hdfs,此时由于 I/O 瓶颈,读性能问题进一步被放大。

    3 问题分析

    查看这几个时间段 GET 请求没有明显变多(单节点 1k/s),而 READ 请求猛增,单节点 read request 达到 600-900k/s。

    推断是出现了大量 scan 或者大 scan。这里为什么要推理,因为 Ambari grafana 的 HBase scan 请求数无法显示。

    缓存命中率直接影响读性能,查看 BucketCache 命中率情况,这个时间段 cache miss 和 eviction 都陡增。 缓存

    推断是 scan 导致缓存写满,所以出现缓存被驱逐(eviction)。

    没有命中缓存,就会走 hdfs,查看 datanode 监控有大量的 block read,查看 datanode 磁盘监控,磁盘 I/O 这段时间被打满。 datanode 磁盘

    大量的读导致了磁盘瓶颈,读性能更差了

    到这里,剩下的就是揪出是谁在进行大 Scan 操作,HBase 上承载的业务众多,一个个问业务不太现实。
    于是通过热点 Regionserver 节点的日志看到该时间段,有一些 Scan 的报错,找到表名,拿去一问业务就问出来问题了。(这种方式着实有点原始)。
    业务每天定时并发对 7 张 HBase 大表进行全表 Scan,查询导出数据到 Hive。

    4 Hive HBase 外部表使用与坑

    创建 HBase 的 hive 外部表典型建表语句如下,rowkey 字段为 hbase 的 rowkey,label 为列族:

    CREATE EXTERNAL TABLE user_profile.mix_label_scene_tag_test_1(rowkey string COMMENT 'rowkey', label map<string,string> COMMENT 'cf')
    ROW FORMAT SERDE 'org.apache.hadoop.hive.hbase.HBaseSerDe'
    STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
    WITH SERDEPROPERTIES('hbase.columns.mapping'=':key,label:')
    TBLPROPERTIES('hbase.table.name'='profile:mix_label_scene_tag_pre_release')
    

    HBase 相关的参数可以直接通过 set 的方式配置进去,例如:

    set hbase.client.scanner.timeout.period=600000;
    

    但是这里有一个坑需要注意,默认 hive hbase 外部表 scan 是会缓存 block 的,这个不容易被开发注意到。如果有大量的 scan 类型的批处理、 mr 任务等,会对缓存是一种破坏性的写入。影响其他正常读请求。HBase 官方也有建议,针对批处理类型的 scan 操作,要设置 setCacheBlocks 为 false:

    Scan instances can be set to use the block cache in the RegionServer via the setCacheBlocks method. For input Scans to MapReduce jobs, this should be false. https://hbase.apache.org/book.html#perf.hbase.client.blockcache

    Hive 社区也有人提了这个问题 HIVE-20484,在 Hive 3 以上的版本进行了修复,将外部表 scan.setCacheBlocks 默认设为 false。hive 这块的代码如下:

    public static final String HBASE_SCAN_CACHEBLOCKS = "hbase.scan.cacheblock";
    
    ...
    
    String scanCacheBlocks = tableProperties.getProperty(HBaseSerDe.HBASE_SCAN_CACHEBLOCKS);
    if (scanCacheBlocks != null) {
      jobProperties.put(HBaseSerDe.HBASE_SCAN_CACHEBLOCKS, scanCacheBlocks);
    }
    
    ...
    
    String scanCacheBlocks = jobConf.get(HBaseSerDe.HBASE_SCAN_CACHEBLOCKS);
    if (scanCacheBlocks != null) {
      scan.setCacheBlocks(Boolean.parseBoolean(scanCacheBlocks));
    }
    

    如果没法升级 hive ,解决办法有两个:

    1. 重新创建外部表,SERDEPROPERTIES 里指定: 'hbase.scan.cacheblock'='false',这种方式可以屏蔽 scan 写缓存,又保留正常的 get 读缓存。
    CREATE EXTERNAL TABLE user_profile.mix_label_scene_tag(rowkey string COMMENT 'rowkey', label map<string,string> COMMENT 'cf')
    ROW FORMAT SERDE 'org.apache.hadoop.hive.hbase.HBaseSerDe'
    STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
    WITH SERDEPROPERTIES('hbase.columns.mapping'=':key,label:','hbase.scan.cacheblock'='false')
    TBLPROPERTIES('hbase.table.name'='profile:mix_label_scene_tag_pre_release')
    
    1. 修改表的属性,修改列族的缓存策略,这种方式全局屏蔽读缓存,会影响正常的 get 读写缓存
    alter 'profile:mix_label_scene_tag', {NAME => 'label', BLOCKCACHE => 'false'}
    

    值得一提的是,setCacheBlocks 是设置:是否把读取的数据写入到缓存中。所以不管怎么设定 setCacheBlocks 的 bool 值,HBase 默认的读请求都会先查缓存。

    4.1 Hive HBase 外部表相关参数

    附上其他的配置,源码

     public static final String HBASE_COLUMNS_MAPPING = "hbase.columns.mapping";
      public static final String HBASE_TABLE_NAME = "hbase.table.name";
      public static final String HBASE_TABLE_DEFAULT_STORAGE_TYPE = "hbase.table.default.storage.type";
      public static final String HBASE_KEY_COL = ":key";
      public static final String HBASE_TIMESTAMP_COL = ":timestamp";
      public static final String HBASE_PUT_TIMESTAMP = "hbase.put.timestamp";
      public static final String HBASE_COMPOSITE_KEY_CLASS = "hbase.composite.key.class";
      public static final String HBASE_COMPOSITE_KEY_TYPES = "hbase.composite.key.types";
      public static final String HBASE_COMPOSITE_KEY_FACTORY = "hbase.composite.key.factory";
      public static final String HBASE_STRUCT_SERIALIZER_CLASS = "hbase.struct.serialization.class";
      public static final String HBASE_SCAN_CACHE = "hbase.scan.cache";
      public static final String HBASE_SCAN_CACHEBLOCKS = "hbase.scan.cacheblock";
      public static final String HBASE_SCAN_BATCH = "hbase.scan.batch";
      public static final String HBASE_AUTOGENERATE_STRUCT = "hbase.struct.autogenerate";
      /**
       * Determines whether a regex matching should be done on the columns or not. Defaults to true.
       * <strong>WARNING: Note that currently this only supports the suffix wildcard .*</strong>
       */
      public static final String HBASE_COLUMNS_REGEX_MATCHING = "hbase.columns.mapping.regex.matching";
      /**
       * Defines the type for a column.
       **/
      public static final String SERIALIZATION_TYPE = "serialization.type";
    
      /**
       * Defines if the prefix column from hbase should be hidden.
       * It works only when @HBASE_COLUMNS_REGEX_MATCHING is true.
       * Default value of this parameter is false
       */
      public static final String HBASE_COLUMNS_PREFIX_HIDE = "hbase.columns.mapping.prefix.hide";
    

    5 todo

    HBase 怎么提升缓存命中率,除了缓存命中率,还有什么会影响读性能?
    HBase 如何监控大 Scan?
    HBase 如何进行表级别的监控?
    HBase Hive 外部表如何有效监控管理,避免用户随意 scan 操作?

    相关文章

      网友评论

          本文标题:[HBase] - 一次由 Hive HBase 外部表引发的读

          本文链接:https://www.haomeiwen.com/subject/cponurtx.html