1、RPC重试配置要点

在Hbase客户端通信过程中,可能会碰到一下几种异常导致重试:

        1、待访问的Region所在的Regionserver宕机,region发生迁移,但是由于客户端缓存未发生更新,会将请求发送至旧的Regionserver。此时为获取到region,重试发起RPC。

2、服务端负载过大,单次RPC超时,客户端后续将继续重试,直到RPC成功或者超过客户容忍最大延迟。

3、访问meta表活着zookeeper异常。

下面了解一下Hbase常见的超时参数:

hbase.rpc.timeout

单次PRC超时时间,一旦单词RPC超过该时间,上层将收到Timeout Exception。默认60000ms

hbase.client.retries.number

调用API时最多容许发生多少次RPC重试操作。默认35次

hbase.client.pause

连续两次RPC重试之间的休眠时间,默认100ms。

但是需要注意的是,Hbase重试休眠时间是按照随机规避算法计算的,若此配置值为100,第一次重试前休眠100ms,第二次200等等。

所以在设置容许最大重试次数设置为35时,后续的重试会长时间卡在休眠阶段。

hbase.client.operation.timeout

表单词API超市时间,默认值为1200000ms。

注意,get/put/delete等操作称之为一次API操作,一次API操作会有多次RPC重试,这个时间限制的是总超时时常。

假设业务要求单词Hbase去请求延迟不超过1s那么应该如何设置上述4个参数?

首先hbase.client.operation.timeout总超时时常应该设置为1s。在SSD集群上,如果集群参数设置合适且集群服务正常,基本可以保证延迟在100ms以内。因此hbase.rpc.timeout设置为100ms。hbase.client.pause使用默认值,可以在1s内执行大概4次的RPC重试。hbase.client.retries.number这个值可以比这个大一些,保证可以重试更多次。

2、CAS接口是Region级别串行执行的,吞吐受限。

        Hbase客户端一些重要的 CAS(Compare And Swap)接口,

hbase数据倾斜怎么处理 hbase常见问题_hbase

        这些接口在高并发的场景下,能够很好的保证读取与写入的原子性。例如,有多个分布式客户端同时更新一个计数器。可以通过increment接口保证任意时刻只有一个客户端成功执行count++操作。

        需要注意的是,这些CAS接口在RegionServer上是Region级别串行的。也就是Region内部的多个CAS操作是严格串行的。

以checkAndPut为例,简要说明下CAS运行步骤:

        1、服务端拿到Region的行锁(rowlock),避免出现两个县城同时修改一行数据。

        2、等待该Region内所有的写入事务都成功提交并在mvcc上可见。

        3、通过Get操作拿到需要check的行数据,进行条件检查。若条件不符合,终止CAS。

        4、将put成功的数据持久化。

        5、释放步骤一得到的锁。

关键在于第二步,必须要等所有正在写入的事务成功提交并在mvcc上可见。由于hbase写入完成时,即先释放行锁,在Sync WAL,最后推mvcc。所以如果跳过第二步,则可能拿不带最新的数据。

        例如两个客户端同时进行increment操作。客户端A读取到x=100,累加,x=101。此时如果a释放锁,但A的put操作mvcc仍不可见。客户端B读取到的仍为x=100,再执行累加。这样执行了两次increment但是却只改变一次。

        因此,对那些依赖CAS(incremnet/append这样的读后写原子操作)的服务。需要意识到这个操作的吞吐是受限制的,因为在region级别上严格串行。

        在Hbase 2.X版已经调整设置,对同一个Region内不同行可以并行执行CAS,大大提升了Region内的CAS吞吐。

3、Scan Filter设置

        Hbase作为一个数据库系统,提供了多样化的查询过滤手段。通过设置不同规则的Filter,大量无效数据可以在服务端内部过滤,相比直接返回全表数据到客户端再由客户端过滤,要高效很多。但是Hbase的Filter也有很多局限,使用不当会对集群产生很大负担。

        1、PrefixFilter:将rowkey前缀为指定字符串的数据都过滤并返回给用户。

hbase数据倾斜怎么处理 hbase常见问题_数据_02

        注意,这个Scan虽然能得到预期的效果,但并不高效。Scan会一条条扫描,直到读取到前缀的行为止。所以可以通过设置起始位置来跳过大量打无效数据。Scan会首先定位到这个开始位置,之后从这个位置向后扫描。

hbase数据倾斜怎么处理 hbase常见问题_hbase数据倾斜怎么处理_03

         2、PageFilter:数据分页。

        在Hbase里,Filter状态全部都是Region内有效的,也就是说,Scan一旦从一个region切换到另一个Region,之前的Filter的内部状态就无效了。在限制分页数据量时,计数器从一个region到另一个region会被清零。这样分野查到的数据就会多于分页的数据量。

        3、SingleColumnPageFilter

hbase数据倾斜怎么处理 hbase常见问题_hbase_04

 这个例子表示将列簇为family,列尾qualifier值为value的cell返回给用户。但是实际上,对那些不包含family:qualifier列的行,也会默认返回给用户。

hbase数据倾斜怎么处理 hbase常见问题_hbase_05

 可以设置上面的参数,可以过滤掉不包含family:qualifier的数据。

另外,当设置了filterMissing为true,和其他Filter组合成FilterList时,可能导致返回结果不正确。因为SingleColumnPageFilter会便利行数据中的每一个cell,才能确认是否过滤,但是在filterList中,其他Filter返回NEXT_ROW会直接跳过某个列簇的数据,导致SingleColumnPageFilter无法遍历一行中所有的cell。

4、少量写和批量写。

        Hbase对写入操作非常友好,但是当有大批量的数据要写入Hbase时,仍会遇到瓶颈。Hbase提供了3中常见的数据写入API

table.put(put)

但行数据写入API,数据先写WAL,然后写Memstore,一旦写满就flush到磁盘。

默认每次写入都需要执行一次RPC和磁盘持久化。

这样写入吞吐受限于磁盘网络以及flush速度。但是能保证每次写入数据都能持久化道磁盘,最重要的是能保证put的原子性。

table.put(Lits<Put> puts)

批量数据插入,在客户端缓存put,达到一定数量后,通过一次RPC发送到客户端,一次性写WAL和Memstore。

这样磁盘开销会降低,但是RPC时长会延长。

如果List<put>内部数据分布在多个Region内,则不能保证数据原子性,因为Hbase不提供跨Region的多行事务,也就是说这些数据可能有部分失败,失败的数据会经历多次重试。

bulkload

通过Hbase提供的工具直接将数据写成Hfile,将这些Hfile加载到对应的Reion下的CF内。在生成Hfile是不会产生RPC调用,只有load时才会调用一次RPC,这是一种完全离线的快速写入方式。

bulkload是最快的批量写入方式某同事不会对线上集群产生巨大压力。在load完hfile之后,CF内部会进行合并Compaction,合并是异步且可限速的。

        在集群之间数据的同步上,可以使用此方式。把备份集群的数据导一个快照拷贝到异常集群,然后通过CopyTable工具扫快照生成Hfile,最后bulkload到异常集群,完成数据修复。

        另一种场景是,用户写入大量数据后发现splitkeys不合适,想重新设置,这样也可以通过Snapshot生成Hfile在bulkload的方式生成新表。

5、业务发现请求延迟很高,但是Hbase服务器延迟正常。

        这种情况一般需要观察Hbase客户端监控和日志,一般来说有以下几种常见问题:

        1、Hbase客户端所在进程JavaGC。

        2、业务进程所在机器的CPU或者网络负载过高。

        3、Hbase客户端层面的bug,概率不大,但也不排除。

6、Batch数据量过大,可能导致MultiActionResultTooLarge异常

        Batch中的操作过多,可能导致一次RPC读取的Block数据量很多,容易造成Hbase的RegionServer出现OOM,活着长时间的FullGC。因此Hbase的RegionServer会限制每次请求的Blick总字节数,超过则会报错。此时,客户端最好控制每次Batch的操作个数,以免服务端为单词RPC消耗太多内存。