Hbase限流 -- HBase Quota调研

1 背景

HBase的生产环境中,每个业务之间的重要性是不一致的,每个业务的数据量、读写需求也不一致,一个集群中往往有很多个业务,有的同学可以执行一个耗时的scan操作,整个集群的资源被大量占用,其它非常重要的业务就被挤压资源,造成一系列的事故。而当请求量异常大时,也会造成RegionServer不稳定。基于这些问题,hbase提出了Quotas,它可以限制namespace,user,table这些级别,涉及read/write以及storage的限制,包括限制单位时间的条数、size以及表或者namesapce的存储等。

本文重点介绍HBase中的资源限制方案Quotas,主要对其使用方式、实现原理进行介绍,对hbase1.2版本的限流和2.1版本的限流进行比较,最后指出当前hbase quota存在的问题。

2 使用方式介绍

2.1 Quotas的启用

Quotas默认是关闭的,如果需要使用时,只需要在hbase-site.xml配置中增加hbase.quota.enabled=true即可,相关的配置如下:

<property>

    <name>hbase.quota.enabled</name>

    <value>true</value>

</property>

<property>

    <name>hbase.quota.rate.limiter</name>

    <value>org.apache.hadoop.hbase.quotas.AverageIntervalRateLimiter</value>

</property>

2.2 Quota语法

  • Quota主要针对用户、namespace以及表的QPS和请求大小进行限制;
  • THROTTLE_TYPE可以取值read、write或者read+write(默认)
  • 时间单位可以是:sec、min、hour、day
  • size限制单位表示:B(字节)、K(千字节)、M(兆字节)、G(千兆字节)、T(兆兆字节)、P(PB级);
  • 请求数的限制表示为整数,后跟字符串 req、req/time 或者 size/time,例如:10req/day或者100P/hour;
  • table或namespace的数量表示为整数,比如一个namespace限制创建多少张表;

配置的维度

Limit限制的类型

请求Size还是请求的次数

单位时间

NAMESPACE

USER

TABLE

USER+NAMESPACE

USER+TABLE

READ

WRITE

READ+WRITE

请求次数:req

请求Size:bkmgtp

sec, min, hour, day

2.3 Setting Request Quotas

你可以事先设置quota,也可以在运行时修改阈值。默认修改后5分钟生效。该时间可以通过hbase-site.xml中的hbase.quota.refresh.period配置项进行修改,该配置项的单位是毫秒。

设置quota的示例:

#将用户u1限制为每秒10个请求

hbase> set_quota TYPE => THROTTLE, USER => 'u1', LIMIT => '10req/sec' 

#将用户u1限制为每秒10个读取请求

hbase> set_quota TYPE => THROTTLE, THROTTLE_TYPE => READ, USER => 'u1', LIMIT => '10req/sec' 

#将用户u1限制为每天10 M

hbase> set_quota TYPE => THROTTLE, USER => 'u1', LIMIT => '10M/day' 

#将用户u1限制为每秒10 M写入大小

hbase> set_quota TYPE => THROTTLE, THROTTLE_TYPE => WRITE, USER => 'u1', LIMIT => '10M/sec' 

# 将用户u1限制为每分钟5k在表t2

hbase> set_quota TYPE => THROTTLE, USER => 'u1', TABLE => 't2', LIMIT => '5K/min' 

#在表t2上将用户u1限制为每秒10次读取请求

hbase> set_quota TYPE => THROTTLE, THROTTLE_TYPE => READ, USER => 'u1', TABLE => 't2', LIMIT => '10req/sec' 

# 在用户名u1上删除用户u1的现有限制ns2

hbase> set_quota TYPE => THROTTLE, USER => 'u1', NAMESPACE => 'ns2', LIMIT => NONE 

#限制所有用户在命名空间ns1上每小时发出10个请求

hbase> set_quota TYPE => THROTTLE, NAMESPACE => 'ns1', LIMIT => '10req/hour' 

# 在表t1 上将所有用户限制为每小时10T

hbase> set_quota TYPE => THROTTLE, TABLE => 't1', LIMIT => '10T/hour' 

#从用户u1中删除所有现有限制

hbase> set_quota TYPE => THROTTLE, USER => 'u1', LIMIT => NONE 

#列出命名空间ns2中用户u1的所有

quotahbase> list_quotas USER => 'u1, NAMESPACE => 'ns2' 

#列出名称空间ns2的所有quota

hbase> list_quotas NAMESPACE => 'ns2' 

#列出表t1的所有quotas

hbase> list_quotas TABLE => 't1' 

# list all quotas

hbase> list_quotas

可以通过应用GLOBAL_BYPASS属性来设置全局限制并从限制中排除用户或表

#列出命名空间ns1的限制

hbase> set_quota NAMESPACE => 'ns1', LIMIT => '100req/min'

#用户u1不受限制               

hbase> set_quota USER => 'u1', GLOBAL_BYPASS => true                    

2.4 Limiting Tables Per Namespace

# 创建一个最多包含5个表的命名空间

hbase> create_namespace 'ns1', {'hbase.namespace.quota.maxtables'=>'5'} 

# 更改现有命名空间,最多包含8个表

hbase> alter_namespace 'ns2', {METHOD => 'set', 'hbase.namespace.quota.maxtables'=>'8'} 

# 显示命名空间的quota信息

hbase> describe_namespace 'ns2' 

# 更改现有命名空间以删除quota

hbase> alter_namespace 'ns2', {METHOD => 'unset', NAME=>'hbase.namespace.quota.maxtables'}

2.5 Limiting Regions Per Namespace

# 创建一个最多包含10个region的命名空间

hbase> create_namespace 'ns1', {'hbase.namespace.quota.maxregions'=>'10' 

# 显示命名空间hbase的quota信息

hbase> describe_namespace 'ns1' 

# 更改现有命名空间最多有20个表

hbase> alter_namespace 'ns2', {METHOD => 'set', 'hbase.namespace.quota.maxregions'=>'20'} 

# 更改现有命名空间以删除quota

hbase> alter_namespace 'ns2', {METHOD => 'unset', NAME=> 'hbase.namespace.quota.maxregions'}

2.6 Space Quotas(HBase2.X)

HBASE-16961引入了一种新的HBase Quotas来利用:filesystem quotas。这些'space'qutas能限制namespace和table的size大小。如果不加限制的话,用户可以无限制的往表中写数据,极端的情况是,大量的数据写入,造成没有空间可以,hbase无法预写日志或者同步数据。

可以通过set_quota和list_quota的HBase shell命令设置空间配额。空间配额的TYPE是SPACE,并且具有LIMIT和POLICY两个属性。LIMIT是一个字符串,指的是表或命名空间最大占用的空间量。例如有效的LIMIT可以是'10G','2T',或'256M'。POLICY指当配额对象的使用量超过配额时,HBase采取的行动,具体包括以下几种:

POLICY

含义

NO_INSERTS

No new data may be written (e.g. Put, Increment, Append)

NO_WRITES

Same as NO_INSERTS but Deletes are also disallowed

NO_WRITES_COMPACTIONS

Same as NO_WRITES but compactions are also disallowed

This policy only prevents user-submitted compactions. System can still run compactions

DISABLE

The table(s) are disabled, preventing all read/write access

Space quota的例子:

# 在表't1'上限制最大1GB, 超过时禁止Puts/Increments/Appends

hbase> set_quota TYPE => SPACE, TABLE => 't1', LIMIT => '1G', POLICY => NO_INSERTS 

# 在'ns1'上限制最大50TB, 超过时禁止Puts/Increments/Appends/Deletes

hbase> set_quota TYPE => SPACE, NAMESPACE => 'ns1', LIMIT => '50T', POLICY => NO_WRITES 

# 在表't3'上限制最大2TB, 超过时禁止writes and compactions

hbase> set_quota TYPE => SPACE, TABLE => 't3', LIMIT => '2T', POLICY => NO_WRITES_COMPACTIONS 

# 在表't2'上限制最大50GB, 超过时disabling the table

hbase> set_quota TYPE => SPACE, TABLE => 't2', LIMIT => '50G', POLICY => DISABLE

2.7 Table and Namespace space quotas

hbase> create_namespace 'ns1'

hbase> create 'ns1:t1'

hbase> create 'ns1:t2'

hbase> create 'ns1:t3'

hbase> set_quota TYPE => SPACE, NAMESPACE => 'ns1', LIMIT => '100T', POLICY => NO_INSERTS

hbase> set_quota TYPE => SPACE, TABLE => 'ns1:t2', LIMIT => '200G', POLICY => NO_WRITES

hbase> set_quota TYPE => SPACE, TABLE => 'ns1:t3', LIMIT => '20T', POLICY => NO_WRITES

在上面的场景中,ns1不允许命名空间中的表在文件系统上消耗超过100TB的空间。表'ns1:t2'的大小仅允许为200GB,并且当使用率超过此限制时将禁止所有写入。表'ns1:t3'的大小允许增长到20TB,超过时将禁止所有写入和删除。由于'ns1:t1'上没有表配额,它的限制是100TB减去'ns1:t2'和'ns1:t3'的当前使用量。

3 实现原理

3.1 request quota的实现过程

 

3.2 Limiting Tables/ Regions Per Namespace的实现过程

 

3.3 HBase2.1 的space quota的实现过程

 

4 1.2版本与2.1版本的比较

2.1版本比1.2版本新增了一个Space Quota的功能。

5 存在的问题与改进

通过查看了官网和源码,发现hbase的quota可能存在以下的不足(是否不足待讨论),并针对这些不足,提了改进意见,如下表所示:

序号

存在的问题

改进措施

1

Request Quota中,如果达到限流条件,读写请求直接被拒绝

可以增加一个配置项,如果达到限流,该请求可以直接拒绝,也可以重新加入一个自定义的队列,由一个定时任务将限流异常请求回塞到读写请求队列。

2

put/get一次请求,request num就会加1,而scan请求的request num是按照一次完整的scan发送的rpc的次数进行累加的,一次rpc请求数加1

因为scan的一次rpc读取的数据条数肯定远远大于put/get,是否需要给scan加一个权重

3

Hbase quota对hbase的系统表未做限流

是否需要对meta表做限流

4只提供read和write两种类型的粗粒度限流,read中没有区分开get和scan是否需要区分,前一版本的限流的改进就是将read和scan进行了区分

6 附录

6.1 SimpleRpcScheduler初始化读写队列和读写线程数的逻辑

  • 相关的配置

<property>

    <name>hbase.regionserver.handler.count</name>

    <value>128</value>

<description>默认为30,服务器端用来处理用户请求的线程数。生产线上通常需要将该值调到100~200。

</description>

  </property>

  <property>

    <name>hbase.ipc.server.callqueue.handler.factor</name>

    <value>0.1</value>

<description>为0则共享全部队列,默认是0,假如该值为0.1,那么服务器就会设置handler.count * 0.1 = 30 * 0.1 = 3个队列

</description>

  </property>

  <property>

    <name>hbase.ipc.server.callqueue.read.ratio</name>

    <value>0.5</value>

<description>默认为0,服务器端设置读写业务分别占用的队列百分比以及handler百分比。假如该值为0.5,表示读写各占一半队列,同时各占一半handler

</description>

  </property>

  <property>

    <name>hbase.ipc.server.callqueue.scan.ratio</name>

    <value>0.2</value>

  </property>

计算方法:

  • count 是100
  • 队列个数是100 * 0.1 = 10
  • 写队列 10 * 0.5 = 5
  • 读队列总值 10 * 0.5 = 5
  • get 队列 5 - 5 * 0.2 = 4
  • scan队列 5 * 0.2 = 1

2)对应的代码

 

 

6.2 HBase Quota未对系统表做限流

 

 

6.3 HBase space quota相关的参数

参数

含义

默认值

hbase.master.quotas.snapshot.chore.period

定期扫描表存储大小线程的执行间隔时间

1000 * 60 * 5(5分钟)

hbase.master.quotas.snapshot.chore.delay

定期扫描表存储大小线程在多久后执行

1000L * 60L(1分钟)

hbase.master.quotas.snapshot.chore.timeunit

上述两个时间的单位

ms

hbase.regionserver.quotas.policy.refresher.chore.period

Space quota的定时线程的执行间隔时间

1000 * 60 * 1(1分钟)

hbase.regionserver.quotas.policy.refresher.chore.delay

Space quota的定时线程在多久后执行

1000L * 15L(15秒)

hbase.regionserver.quotas.policy.refresher.chore.timeunit

上述两个时间的单位

ms

6.4 一次HBase Scan请求中RPC请求的次数

一次scan请求中RPC的次数与三个参数有关

参数

含义

默认值

setCaching()

单次RPC请求的数据条数

Integer.MAX_VALUE

setBatch()

单次RPC请求的数据列数量

默认一个result包含某row所有的列

setMaxResultSize

单次RPC请求的返回数据量大小

2G

计算公式:

Result 返回的个数 =(row数 * 每行的列数)/ Min(每行列数,Batch大小)

RPC 返回的个数  = (row数 * 每行的列数)/ Min(每行列数,Batch大小) / Caching大小

setCaching 是用来控制rpc的个数,setMaxResultSize 从单次rpc返回的所有Result的总大小来控制某次rpc是否结束。

6.5 Quota判断的相关的代码

对数据请求的quotas限制在region server上进行。RSRpcServices收到RPC请求后,对于get,mutate,scan操作,其执行的RegionServerQuotaManager的checkQuota如下:

 

multi(multiple actions on a table: get, mutate, and/or execCoprocessor)操作,调用RegionServerQuotaManager的如下checkQuota方法进行检查:

 

我们可以看到上面两个方法都调用了同一个checkQuota方法,只是输入参数不同

 

从上面代码中可以看到,这里最重要的两个方法就是getQuota和checkQuota。我们先看getQuota做了什么?

 

简单解释一下代码就是:首先,从Region Server的quotaCache中拿到关于此用户的UserQuotaState。然后从UserQuotaState中提取QuotaLimiter。最后新建一个OperationQuota返回。

再来看DefaultOperationQuota的checkQuota方法:

 

方法中,首先估计各个操作的大小,这个估计的方法很简单,如下面代码所示。不管操作类型是什么,计算方法都一样:

 

回到DefaultOperationQuota的checkQuota方法,最重要的是limiter.checkQuota(writeConsumed, readConsumed)这段代码。进入TimeBasedLimiter的checkQuota()方法:

 

这个函数会把所有的limiter都检查一遍,但凡有一个Limiter不满足条件,就抛出异常。如果所有的判断条件都通过,下面就走到了limiter.grabQuota(writeConsumed, readConsumed)这一步。这个方法的详细代码如下:

 

至此,各个操作的Quota检查就算完成了。但是,在哪调整真正的读写消费量呢?RSRPCServices的get,mutate,scan和multi方法会调用checkQuota方法。拿get方法举例:

 

 

这段代码很长,我们只看与Quota相关的部分。checkQuota部分上面已经介绍了,方法最后有两句代码quota.addGetResult(r)quota.close(),它们是做什么用的呢?先看quota.addGetResult(r)所调用的方法:

 

从代码里不难看出,这是在计算get操作所返回的结果的真正大小,也就是真正的消费量。

直接看quota.close()所调用的方法:

最后进行的步骤就是把真正消费量和估计消费量之间的差额给补齐,多退少补。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值