记录一次 Hbase 线上问题的分析和解决
大家好,我是明哥!
本篇文章,我们回顾一次 hbase 线上问题的分析和解决 - KeyValue size too large,总结下背后的知识点,并分享一下查看开源组件不同版本差异点的方法。
希望大家有所收获,谢谢大家!
01 Hbase 简介
Hbase 作为 hadoop database, 是一款开源,分布式易扩展,面向大数据场景的,多版本,非关系型,数据库管理系统,是 Google Bigtable 的 JAVA 版开源实现。
Hbase 的底层存储引擎是 HDFS,可以在普通商业级服务器硬件(即常说的 x86 架构的服务器)的基础上,提供对超大表(表的数据量可以有百万行,每行的列数可以有百万级)的随机实时读写访问。
Hbase具有以下特征:
模块化的线性扩展性; 强一致性并发读写支持; 可配置的表的自动 sharding; 表分区在 RegionServer 间的自动 failover; 基于 Block cache 和 Bloom 过滤器的实时读取; 基于 server 端过滤器的查询谓词下推;
正是因为 Hbase 的上述特征,Hbase 在各行各业有许多线上应用案列,可以说是 NoSql 数据库的一个典型代表:
在各种超大数据量级 在需要实时并发读写支持 在表的结构比较灵活(即有很多稀疏列:有很多行和很多列,但每一行只有众多列中的少数列有值) 笔者就在车联网场景下重度使用过 Hbase
题外话:
Nosql 数据库有几大类,几个典型代表是:Hbase, ElasticSearch, MongoDb;
有个有趣的现象,笔者发现国内 Hbase 使用的多,而国外似乎 Cassandra 使用的多。
02 一次线上 Hbase 问题的问题现象
某线上应用使用了 Hive 到 Hbase 的映射表,在使用 insert overwrite 从 hive 表查询数据并插入 HBASE 表时,发生了错误。
通过查看 HIVE 背后 YARN 上的作业的日志,发现主要错误信息是 java.lang.记录IllegalArgumentException: KeyValue size too large,详细报错截屏和日志如下:
2020-04-08 09:34:38,120 ERROR [main] ExecReducer: org.apache.hadoop.hive.ql.metadata.HiveException: Hive Runtime Error while processing row (tag=0) {"key":{"_col0":"0","_col1":"","_col2":"2020-04-08","_col3":"joyshebaoBeiJing","_col4":"105","_col5":"北京,"},"value":null} at org.apache.hadoop.hive.ql.exec.mr.ExecReducer.reduce(ExecReducer.java:253) at org.apache.hadoop.mapred.ReduceTask.runOldReducer(ReduceTask.java:444) at org.apache.hadoop.mapred.ReduceTask.run(ReduceTask.java:392) at org.apache.hadoop.mapred.YarnChi一次ld$2.run(YarnChild.java:164) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.Subject.doAs(Subject.java:422) at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1924) at org.apache.hadoop.mapred.YarnChild.main(YarnChild.java:158) Caused by: org.apache.hadoop.hive.ql.metadata.HiveException: java.lang.IllegalArgumentException: KeyValue size too large at org.apache.hadoop.hive.ql.exec.GroupByOperator.processOp(GroupByOperator.java:763) at org.apache.hadoop.hive.ql.exec.mr.E HxecReducer.reduce(ExecReducer.java:244) ... 7 more Caused by: java.lang.IllegalArgumentException: KeyValue size too large at org.apache.hadoop.hbase.client.HTable.validatePut(HTable.java:1577) at org.apache.hadoop.hbase.client.BufferedMutatorImpl.validatePut(BufferedMutatorImpl.java:158) at org.apache.hadoop.hbase.client.BufferedMutatorImpl.mutate(BufferedMutatorImpl.java:133) at org.apache.hadoop.hbase.client.BufferedMutatorImpl.mutate(BufferedMutatorImpl.java:119) at org.apache.hadoop.hbase.client.HTable.put(HTable.java:1085) &nbbasp; at org.apache.hadoop.hive.hbase.HiveHBaseTableOutputFormat$MyRecordWriter.write(HiveHBaseTableOutputFormat.java:146) at org.apache.hadoop.hive.hbase.HiveHBaseTableOutputFormat$MyRecordWriter.write(HiveHBaseTableOutputFormat.java:117) at org.apache.hadoop.hive.ql.io.HivePassThroughRecordWriter.write(HivePassThroughRecordWriter.java:40) at org.apache.hadoop.hive.ql.exec.FileSinkOperator.processOp(FileSinkOperator.java:717) at org.apache.hadoop.hive.ql.exec.Operator.forward(Operator.java:815) at org.apache.hadoop.hive.ql.exec.SelectOperator.processOp(SelectOperator.java:84) &nsebsp; at org.apache.hadoop.hive.ql.exec.Operator.forward(Operator.java:815) at org.apache.hadoop.hive.ql.exec.GroupByOperator.forward(GroupByOperator.java:1007) at org.apache.hadoop.hive.ql.exec.GroupByOperator.processAggr(GroupByOperator.java:818) at org.apache.hadoop.hive.ql.exec.GroupByOperator.processKey(GroupByOperator.java:692) at org.apache.hadoop.hive.ql.exec.GroupByOperator.processOp(GroupByOperator.java:758) ... 8 more 03 该线上 Hbase 问题的问题原因
其实以上作业的报错日志还是比较详细的:Caused by: java.lang.IllegalArgumentException: KeyValue size too large at org.apache.hadoop.hbase.client.HTable.validatePut(HTable 线.java:1577);
即报错详细信息是:KeyValue size too large; 报错来自 Hbase (而不是 HIVE)的类,从类名看是 hbase 客户端在对待插入数据校验时发现了错误:org.apache.hadoop.hbase.client.HTable.validatePut(HTable.java:1577);
熟悉 Hbase 的小伙伴(其实不熟悉 Hbase 的小伙伴,也能从报错信息和报错类上猜到一点点),从以上信息能够猜到,是 Hbase 对每条记录的 KyeValue 的大小做了限制,当实际插入的 KeyValue 的大小超过该大小限制阈值时,就会报上述错误。
什么是 KeyValue 呢?
这涉及到 Hbase 的存储结构,Hbase 内部是通过 KeyValue 结构来存储表数据的; 大致上大家可以认为:某个 KeyValue 对应的是 Hbase 大宽表中的某行的某列; 每个 KeyVlue 内部内容包含:rowKey+CF(ColumnFamily)+CQ(Column qualifier)+Timestamp+Value; 详细说明见官方文档,截图如下:
Hbase 为什么要限制每个 KeyValue 的大小呢?
究其原因,是因为 Hbase 需要在 hdfs 存储引擎(基于分磁盘)之上提供对数据实时读写的支持; Hbase 在内部数据读写时使用了 LSM 数据结构 (Log-Structured-Merge Tree),这背后涉及到了大量对内存的使用(读数据时使用 BlockCache,写数据时使用 memStore),也涉及到了内存数据的异步 flush 和 hfile 文件的异步 compaction;(当然为了容错,又涉及到了 wal Hlog); 因为涉及到基于内存提供对数据读写的支持,所以需要限制使用的内存的总大小,由于内存 BlockCache 中缓存的数据是以Block 为单位的,而Block内部存储的是一个个 KeyV上问alue, 所以从细节来讲也需要限制每个 KeyValue的大小; 这里的 Block 是 Hbase的概念,不是 HDFS Block; (Hbase 每个 Block 的默认大小是 64KB, HDFS 每个BLOCK的默认大小一般是 128MB); 04 该线上 Hbase 问题的扩展知识-不同 Hbase 版本相关的参数
Hbase 作为一个流行的 Nosql数据库,推出十多年来,目前有多个经典版本:
0.98;-- 该版本比较老了,部分遗留线上应用还有使用该版本的; 1.2.x;(1.4.x) -- 1.2.x 和 1.4.x 都是1.x 系列下用的比较多的稳定版本 2.1.x(2.4.x) -- 2.1.x 和 2.4.x 都是2.x系列下用的比较多的稳定版本 3.0.0-alpha-1 -- 3.x 系列目前还不是稳定版
对应该 “KeyValue size too large” 问题,不同版本推出了不同的相关参数:
在hbase-1.4 以前的版本中,(包括hbase 1.2.0-cdh5.14.2 和 hbase 1.2.0-cdh5.16.2),只有一个客户端参数 hbase.client.keyvalue.maxsize; 在 hbase-1.4 及以后版本中,除了该客户端参数 hbase.client.keyvalue.maxsize,还有一个服务端参数 hbase.server.keyvalue.maxsize;
其实,由于笔者并没有持续跟进 HBASE 社区对 feautre 和 issue相关的讨论(大部分使用者可能都不会),所以也是在查阅不同版本的官方文档时留意到了上述细节,然后通过在本地 IDEA 中使用 git->show history 对比不同版本 HBASE 中 hbase-default.xml 的源码,进而确认到了JIRA记录号,并在JIRA中确认了这点:
正如该 JIRA 中描述:
HBASE-18043:
For sake of service protectio题的n we should not give absolute trust to clients regarding resource limits that can impact stability, like cell size limits. We should add a server side configuration that sets a hard limit for individual cell size that cannot be overridden by the client. We can keep the client side check, because it's expensive to reject a RPC that has already come in.
所以,不同版本中遇到不同情况,可能会包的错误主要有两个:
情况1:Hbase KeyValue size too large 情况2:Cell with size 25000046 exceeds limit of 10485760 bytes
报错情况一和报错情况而,问题原因如下:
报错情况一:没有配置客户端参数 hbase.client.keyvalue.maxsize,且实际插入的 keyvalue 的大小超过了该客户端参数的默认大小限制;
报错情况二:程序设置调大了客户端参数 hbase.client.keyvalue.maxsize,但没有调大服务端参数 hbase.server.keyvalue.maxsize,且实际插入的 keyvalue 小于该客户端参数,但大于该服务端参数:
报错情况二,某次作业日志:
Exception in thread "main"org.apache.hadoop.hbase.DoNotRetryIOException: org.apache.hadoop.hbase.DoNotRetryIOException: Cell分析 with size 25000046 exceeds limit of 10485760 bytes at org.apache.hadoop.hbase.regionserver.RSRpcServices.checkCellSizeLimit(RSRpcServices.java:944) at org.apache.hadoop.hbase.regionserver.RSRpcServices.mutate(RSRpcServices.java:2792) at org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos$ClientService$2.callBlockingMethod(ClientProtos.java:42000) at org.apache.hadoop.hbase.ipc.RpcServer.call(RpcServer.java:413) at org.apache.hadoop.hbase.ipc.CallRunner.run(CallRunner.java:130) at org.apache.hadoop.hbase.ipc.RpcExecutor$Handler.run(RpcExecutor.j和解ava:324) at org.apache.hadoop.hbase.ipc.RpcExecutor$Handler.run(RpcExecutor.java:304)
报错情况二,某次报错截图:
05 该线上 Hbase 问题的解决方案
知道了问题原因,其实解决方法也就呼之欲出了,在确认要插入的业务数据没有异常,确实需要调大 keyvalue 限制的阈值时,大体总结下,有以下解决办法:
方法一:修改配置文件 hbase-site.xml, 调大客户端参数 hbase.client.keyvalue.maxsize 的值; 方法二:如果使用了 HBASE JAVA API, 可以修改代码使用 configuration 对象修改此客户端参数的默认配置:Configuration conf = HBaseConfiguration.create(); conf.set("hbase.client.keyvalue.maxsize","20971520"); 方法三:如果使用了 HIVE,可以在客户端覆盖该客户端参数:set hbase.client.keyvalue.maxsize=0; (hive sql中) 说明:一般该客户端参数和服务端参数,默认值应该配置一样;当更改服务端参数时,需要重启服务端才能生效;当更改客户端参数时,不同客户端设置的值可以不同,且不需要重启服务端; 在CDH中配置更改截图如下:
06 该线上 Hbase 问题的技术背景 Hbase 内部是通过 KeyValue 结构来存储表数据的,某个 KeyValue 对应的是 Hbase 大宽表中的某行的某列,每个 KeyVlue 内部内容包含:rowKey+CF(ColumnFamily)+CQ(Column qualifier)+Timestamp+Value; hbase 在进行 PUT 操作的时记录候,会逐个检查要插入的每个列 (keyvalue cell) 的大小,当其大小大于 maxKeyValueSize 时,就会抛出异常,拒绝写入; maxKeyValueSize 大小相关的参数,在hbase-1.4 以前的版本中,(包括hbase 1.2.0-cdh5.14.2 和 hbase 1.2.0-cdh5.16.2),只有一个客户端参数 hbase.client.keyvalue.maxsize; maxKeyValueSize 大小相关的参数,在 hbase-1.4 及以后版本中,除了该客户端参数 hbase.client.keyvalue.maxsize,还有一个服务端参数 hbase.server.keyvalue.maxsize; Hbase 限制每个 KeyValue 大小的原因,主要在于: Hbase 需要在 hdfs 存储引擎(基于分磁盘)之上提供对数据实时读写的支持,因此 Hbase 在内部数据读写时使用了 LSM 数据结构 (Log-Structured-Merge Tree),这背后涉及到了大量对内存的使用(读数据时使用 BlockCache,写数据时使用 memStore),也涉及到了内存数据的异步 flush 和 hfile 文件的异步 compaction;(当然为了容错,又涉及到了 wal Hlog); 因为涉及到基于内存提供对数据读写的支持,所以需要限制使用的内存的总大小,由于内存 BlockCache 中缓存的数据是以Block 为单位的,而Block内部存储的是一个个 KeyValue, 所以从细节来讲也需要限制每个 KeyValue的大小; 这里的 Block 是 Hbase的概念,不是 HDFS Block; (Hbase 每个 Block 的默认大小是 64KB, HDFS 每个BLOCK的默认大小一般是 128MB); 该客户端参数 hbase.client.keyvalue.maxsize,和服务端参数hbase.server.一次keyvalue.maxsize,集群级别的默认配置推荐保持一致,且不推荐在集群级别配置这两个参数为0或更小(即禁用大小检查),因为此时可能会造成某个cell 存很大的数据比如 1G,此时集群性能就会大打折扣; 服务端参数只能在服务端进行配置,且配置后要重启服务端的 hbase, 其做用是 “This is a safety setting to protect the server from OOM situations.”; 客户端参数可以在服务端进行全局默认配置,也可以在客户端进行定制配置,不同客户端设置的值可以不同,但不能大于服务端的值,且客户端配置后不需要重启服务端,其含义是:一个KeyValue实例的最大size(一个KeyValue在io时是不能进一步分割的); 客户端参数,具体需要设置为多大,需要根据业务允许的最大keyValue是多少来进行配置,默认是10M,一般是以默认值10M为基础,如果有客户遇到以上报错,且实际的KEYVALUE也不是业务脏数据,就调大1倍到20MB看看; 在通过hive外表使用 HBaseStorageHandler 向 HBASE 写数据时,可以直接在beeline中更改配置客户端参数 (set hbase.client.keyvalue.maxsize=10485760),不需要重启hbase服务端; 在探究某个开源组件的某个类或配置文件,在不同版本的变化历史时,可以通过 git clone 在本地克隆创建 git repository,然后在本地 IDEA 中通过使用命令 git->show history 对比不同版本中类或配置文件的,进而确认到 JIRA 记录号,并在JIRA中确认相关细节; 更多关于hbase数据存储结构的细节: hbase 中的数据,可以有多个columnFamily, 每个 columnFamily 内部可以有多个column;
每个 ColumnsFamily 落 H地到hdfs上的文件是 hfile/storeFile, storeFile 是由 data block组成的(block 是IO和压缩的基本单位,默认64KB);(Within an HFile, HBase cells are stored in data blocks as a sequence of KeyValues;KeyValue instances are aggregated into blocks, which are indexed and Indexes also have to be stored; Blocksize is configurable on a per-ColumnFamily basis); 每个 block 内部存储了多个columns,每个column都是以 keyvalue 的形式存储的,每个keyvalue的大小受hbase.clent.keyvalue.maxsize/hbase.server.keyvalue.maxsize限制的; compression and DATA BLOCK ENCODING doesn't help with the cell size check, as compress and data block encoding happens when flush memstore to hfile and compaction of hfile; HBase supports several different compression algorithms which can be enabled on a ColumnFamily. Data block encoding attempts to limit duplication of information in keys, taking advantage of some of the fundamental designs and patterns of HBase, such as sorted row keys and the schema obaf a given table. Compressors reduce the size of large, opaque byte arrays in cells, and can significantly reduce the storage space needed to store uncompressed data.
(责任编辑:新品推荐)
- 标签: 影石创新 影翎A1 刘靖康 全景无人机
- 标签: 曦华科技 AI芯片 港交所上市 招股书
- 标签: 中国移动 5G网络 AIoT平台 卫星通信
- 全球AI需求大爆发!京东云承诺:全系核心产品不涨价
- 阿里云无影AI云电脑亮相:体验超传统PC,可畅玩《黑神话:悟空》
- 标签: AI 用友
- 微软智能云在华新增数据中心区域正式启用 看好中国市场机会
- 标签: 百川智能 M3 Plus 海纳百川 证据锚定
- 标签: 天数智芯 天垓Gen2 智铠系列 AI算力解决方案
- 标签: 特斯拉 马斯克 电动汽车 交付量
- 标签: 广汽集团 广汽传祺 广汽埃安 广汽昊铂
- 标签: Temu 拼多多 土耳其 跨境电商
- 标签: Nothing 随心所语 许奇 Phone(4a)
- 云计算开发:Python练习实例-使用按位异或 ^
- 英国监管机构担忧云计算市场竞争,计划审查微软和亚马逊市场地位
- 云计算开发:Python内置函数-staticmethod() 函数详解
- 淘宝天猫推出 AI 识别技术,打击恶意退款伪造图像
- 苹果或将对手机完全无线化 车载有线配置或将受到一定影响
- 美国劳工委员会官员建议重新进行亚马逊工会化投票
- AI训练数据争夺战升级:Datacurve融资1500万美元,用赏金猎人模式抢夺高质量数据
- https://www.maigoo.com/search/?block=&q=必应蜘蛛池租用【tg:seogoogle987】,iBo views+
- https://www.tumblr.com/tagged/必应蜘蛛池租用【tg:seogoogle987】,wND views+
- https://www.tycoonstory.com/?s=必应蜘蛛池租用【tg:seogoogle987】,HOB views+
- https://www.olx.ba/pretraga?trazilica=必应蜘蛛池租用【tg:seogoogle987】,i1g views+
- https://www.qidulp.com/search?query=必应蜘蛛池租用【tg:seogoogle987】,JZy views+
- https://www.mygrandma.com/Items.aspx?search=必应蜘蛛池租用【tg:seogoogle987】,tly views+
- https://www.ng-slo.si/si/iskanje?cat=114&q=必应蜘蛛池租用【tg:seogoogle987】,6IM views+
- https://www.repetto.jp/item/search?action=QUERY&q=必应蜘蛛池租用【tg:seogoogle987】,gjH views+
- https://www.sic-info.org/vi/?s=必应蜘蛛池租用【tg:seogoogle987】,oIS views+
- https://www.sbaad.no/search/必应蜘蛛池租用【tg:seogoogle987】,0UK views+
