互联网技术 / 互联网资讯 · 2024年3月19日 0

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,详细报错截屏和日志如下:

Hbase 线上问题的分析和解决案例

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}

Caused by: oRg.Apache.hadoop.Hive.ql.Metadata.HiveException: java.lang.IllegalARguMentException: KeyValue size too laRge

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 线上问题的分析和解决案例

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&Rdquo; 问题,不同版本推出了不同的相关参数:

在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中确认了这点:

Hbase 线上问题的分析和解决案例

Hbase 线上问题的分析和解决案例

Hbase 线上问题的分析和解决案例

正如该 JIRA 中描述:

HBASE-18043:

FoR sake of seRvice Protection 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)

报错情况二,某次报错截图:

Hbase 线上问题的分析和解决案例

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中配置更改截图如下:

Hbase 线上问题的分析和解决案例

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,集群级别的默认配置推荐保持一致,且不推荐在集群…