1 問題
近期業(yè)務反饋 HBase GET 讀性能不佳蹬挤,查看監(jiān)控,每天有幾個時間波段,GET P99 請求時延很高剂癌。
2 問題原因
定位有一個業(yè)務有 7 張 Hive HBase 外部表锌雀,每天有兩個時間定時通過 sql 的方式蚂夕,并發(fā)導出全表到 hive 。代碼類似于:
insert into table_hive select * from hive_hbase_external_table;
相當于每天定時并發(fā)對 7 張大的 HBase 全表 Scan腋逆,Scan 默認會寫緩存婿牍,這個會導致:
- Bucket Cache 被 scan block 寫滿, 緩存開始淘汰惩歉,出現(xiàn) cache miss (緩存命中率降低) 和 cache eviction (緩存淘汰)等脂。cache 命中率直接影響 HBase 讀性能。
- 出現(xiàn) I/O 風暴撑蚌,讀請求沒有命中緩存需要讀 hdfs上遥,此時由于 I/O 瓶頸,讀性能問題進一步被放大争涌。
3 問題分析
查看這幾個時間段 GET 請求沒有明顯變多(單節(jié)點 1k/s)粉楚,而 READ 請求猛增,單節(jié)點 read request 達到 600-900k/s亮垫。
緩存命中率直接影響讀性能燃异,查看 BucketCache 命中率情況,這個時間段 cache miss 和 eviction 都陡增继蜡。推斷是出現(xiàn)了大量 scan 或者大 scan模软。這里為什么要推理,因為 Ambari grafana 的 HBase scan 請求數(shù)無法顯示饮潦。
沒有命中緩存稀并,就會走 hdfs鲫剿,查看 datanode 監(jiān)控有大量的 block read,查看 datanode 磁盤監(jiān)控稻轨,磁盤 I/O 這段時間被打滿灵莲。推斷是 scan 導致緩存寫滿回俐,所以出現(xiàn)緩存被驅逐(eviction)。
大量的讀導致了磁盤瓶頸,讀性能更差了
到這里殴俱,剩下的就是揪出是誰在進行大 Scan 操作政冻,HBase 上承載的業(yè)務眾多枚抵,一個個問業(yè)務不太現(xiàn)實。
于是通過熱點 Regionserver 節(jié)點的日志看到該時間段明场,有一些 Scan 的報錯汽摹,找到表名,拿去一問業(yè)務就問出來問題了苦锨。(這種方式著實有點原始)逼泣。
業(yè)務每天定時并發(fā)對 7 張 HBase 大表進行全表 Scan,查詢導出數(shù)據(jù)到 Hive舟舒。
4 Hive HBase 外部表使用與坑
創(chuàng)建 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 相關的參數(shù)可以直接通過 set 的方式配置進去秃励,例如:
set hbase.client.scanner.timeout.period=600000;
但是這里有一個坑需要注意氏仗,默認 hive hbase 外部表 scan 是會緩存 block 的,這個不容易被開發(fā)注意到夺鲜。如果有大量的 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 社區(qū)也有人提了這個問題 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 浪感,解決辦法有兩個:
- 重新創(chuàng)建外部表昔头,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')
- 修改表的屬性莱革,修改列族的緩存策略,這種方式全局屏蔽讀緩存讹开,會影響正常的 get 讀寫緩存
alter 'profile:mix_label_scene_tag', {NAME => 'label', BLOCKCACHE => 'false'}
值得一提的是盅视,setCacheBlocks 是設置:是否把讀取的數(shù)據(jù)寫入到緩存中。所以不管怎么設定 setCacheBlocks 的 bool 值旦万,HBase 默認的讀請求都會先查緩存闹击。
4.1 Hive HBase 外部表相關參數(shù)
附上其他的配置,源碼:
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 如何監(jiān)控大 Scan?
HBase 如何進行表級別的監(jiān)控断箫?
HBase Hive 外部表如何有效監(jiān)控管理拂酣,避免用戶隨意 scan 操作?