深度剖析HBase负载均衡和性能指标
Posted 大数据和云计算技术
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度剖析HBase负载均衡和性能指标相关的知识,希望对你有一定的参考价值。
1.概述
在分布式系统中,负载均衡是一个非常重要的功能,HBase通过Region的数量实现负载均衡,即通过hbase.master.loadbalancer.class实现自定义负载均衡算法。下面将为大家剖析HBase负载均衡的相关内容以及性能指标。
2.内容
HBase系统负载均衡是一个周期性的操作,通过负载均衡来均匀分配Region到各个RegionServer上,通过hbase.balancer.period属性来控制负载均衡的时间间隔,默认是5分钟。触发负载均衡操作是有条件的,但是如果发生以下情况则不会触发负载均衡操作:
负载均衡自动操作balance_switch关闭,即:balance_switch false;
HBase Master节点正在初始化操作;
HBase集群中正在执行RIT,即Region正在迁移中;
HBase集群正在处理离线的RegionServer;
2.1 负载均衡算法
HBase执行负载均衡操作的时候,如何判断各个RegionServer节点上的Region个数是否均衡,这里通过以下步骤来判断:
计算均衡值的区间范围,通过总Region个数以及RegionServer节点个数,算出平均Region个数,然后在此基础上计算最小值和最大值;
遍历超过Region最大值的RegionServer节点,将该节点上的Region值迁移出去,直到该节点的Region个数小于等于最大值的Region;
遍历低于Region最小值的RegionServer节点,分配集群中的Region到这些RegionServer上,直到大于等于最小值的Region;
负责上述操作,直到集群中所有的RegionServer上的Region个数在最小值与最大值之间,集群才算到达负载均衡,之后,即使再次手动执行均衡命令,HBase底层逻辑判断会执行忽略操作。
2.2 算法流程实例
下面笔者通过实际的应用场景来给大家剖析HBase负载均衡算法的实现流程。举个例子,假如我们当前有一个5台节点规模的HBase集群(包含Master和RegionServer),其中2台Master和3台RegionServer组成,每台RegionServer上的Region个数,如图所示:
执行负载均衡操作之前,首先计算集群中总的Region个数,当前实例中集群中的Region总个数为175+56+99=330,然后计算每个RegionServer需要容纳的Region平均值,计算结果:
平均值(110) = 总Region个数(330) / RegionServers总数(3)
计算最小值和最大值来判断HBase集群是否需要进行负载均衡操作,计算公式:
# hbase.regions.slop 权重值,默认为0.2
最小值 = Math.floor(平均值 * (1-0.2))
最大值 = Math.ceil(平均值 * (1+0.2))
HBase集群如果判断各个RegionServer中的最小Region个数大于计算后的最小值,并且最大Region个数小于最大值,这是直接返回不会触发负载均衡操作。根据实例中给出的Region数,计算得出最小值Region为88,最大值Region为120。
由于实例中RegionServer2的Region个数为56,小于最小值Region数88,而RegionServer1的Region个数为175,大于了最大值Region数120,所以需要负载均衡操作。
HBase系统提供管理员命令来操作负载均衡,具体操作命令:
# 使用hbase shell命令进入到HBase控制台,然后开启自动执行负载均衡
hbase(main):001:0> balance_switch true
balance_switch命令底层实现balance_switch.rb和admin.rb文件源码:
module Shell
module Commands
class BalanceSwitch < Command
def help
<<-EOF
Enable/Disable balancer. Returns previous balancer state.
Examples:
hbase> balance_switch true
hbase> balance_switch false
EOF
end
def command(enableDisable)
prev_state = admin.balance_switch(enableDisable) ? 'true' : 'false'
formatter.row(["Previous balancer state : #{prev_state}"])
prev_state
end
end
end
end
#----------------------------------------------------------------------------------------------
# Enable/disable balancer
# Returns previous balancer switch setting.
def balance_switch(enableDisable)
@admin.setBalancerRunning(
java.lang.Boolean.valueOf(enableDisable), java.lang.Boolean.valueOf(false)
)
end
此命令输出的是之前负载均衡器balancer的开关设置,再看balance_switch命令处理实现源码:
/**
* Assigns balancer switch according to BalanceSwitchMode
* @param b new balancer switch
* @param mode BalanceSwitchMode
* @return old balancer switch
*/
boolean switchBalancer(final boolean b, BalanceSwitchMode mode) throws IOException {
boolean oldValue = master.loadBalancerTracker.isBalancerOn();
boolean newValue = b;
try {
if (master.cpHost != null) {
master.cpHost.preBalanceSwitch(newValue);
}
try {
if (mode == BalanceSwitchMode.SYNC) {
synchronized (master.getLoadBalancer()) {
master.loadBalancerTracker.setBalancerOn(newValue);
}
} else {
master.loadBalancerTracker.setBalancerOn(newValue);
}
} catch (KeeperException ke) {
throw new IOException(ke);
}
LOG.info(master.getClientIdAuditPrefix() + " set balanceSwitch=" + newValue);
if (master.cpHost != null) {
master.cpHost.postBalanceSwitch(oldValue, newValue);
}
master.getLoadBalancer().updateBalancerStatus(newValue);
} catch (IOException ioe) {
LOG.warn("Error flipping balance switch", ioe);
}
return oldValue;
}
此时HBase负载均衡自动操作就开启完毕,但是如果我们需要立即均衡集群的Region个数怎么办?这里HBase也提供管理命令,通过balancer命令来实现,操作命令:
hbase(main):001:0> balancer
balancer命令实现查看balancer.rb和admin.rb文件源码:
module Shell
module Commands
class Balancer < Command
def help
<<-EOF
Trigger the cluster balancer. Returns true if balancer ran and was able to
tell the region servers to unassign all the regions to balance (the re-assignment itself is async).
Otherwise false (Will not run if regions in transition).
Parameter tells master whether we should force balance even if there is region in transition.
WARNING: For experts only. Forcing a balance may do more damage than repair
when assignment is confused
Examples:
hbase> balancer
hbase> balancer "force"
EOF
end
def command(force = nil)
force_balancer = 'false'
if force == 'force'
force_balancer = 'true'
elsif !force.nil?
raise ArgumentError, "Invalid argument #{force}."
end
formatter.row([admin.balancer(force_balancer) ? 'true' : 'false'])
end
end
end
end
#----------------------------------------------------------------------------------------------
# Requests a cluster balance
# Returns true if balancer ran
def balancer(force)
@admin.balancer(java.lang.Boolean.valueOf(force))
end
该命令通过调用负载均衡器balancer的balanceCluster()方法生成负载均衡计划执行集群的负载均衡操作,Master实现负载均衡底层源码:
public boolean balance(boolean force) throws IOException {
// if master not initialized, don't run balancer.
if (!isInitialized()) {
LOG.debug("Master has not been initialized, don't run balancer.");
return false;
}
if (isInMaintenanceMode()) {
LOG.info("Master is in maintenanceMode mode, don't run balancer.");
return false;
}
int maxRegionsInTransition = getMaxRegionsInTransition();
synchronized (this.balancer) {
// If balance not true, don't run balancer.
if (!this.loadBalancerTracker.isBalancerOn()) return false;
...
boolean isByTable = getConfiguration().getBoolean("hbase.master.loadbalance.bytable", false);
Map<TableName, Map<ServerName, List<RegionInfo>>> assignmentsByTable =
this.assignmentManager.getRegionStates().getAssignmentsByTable(!isByTable);
List<RegionPlan> plans = new ArrayList<>();
//Give the balancer the current cluster state.
this.balancer.setClusterMetrics(getClusterMetricsWithoutCoprocessor());
this.balancer.setClusterLoad(assignmentsByTable);
for (Map<ServerName, List<RegionInfo>> serverMap : assignmentsByTable.values()) {
serverMap.keySet().removeAll(this.serverManager.getDrainingServersList());
}
for (Entry<TableName, Map<ServerName, List<RegionInfo>>> e : assignmentsByTable.entrySet()) {
List<RegionPlan> partialPlans = this.balancer.balanceCluster(e.getKey(), e.getValue());
if (partialPlans != null) plans.addAll(partialPlans);
}
long balanceStartTime = System.currentTimeMillis();
long cutoffTime = balanceStartTime + this.maxBlancingTime;
int rpCount = 0; // number of RegionPlans balanced so far
if (plans != null && !plans.isEmpty()) {
int balanceInterval = this.maxBlancingTime / plans.size();
LOG.info("Balancer plans size is " + plans.size() + ", the balance interval is "
+ balanceInterval + " ms, and the max number regions in transition is "
+ maxRegionsInTransition);
for (RegionPlan plan: plans) {
LOG.info("balance " + plan);
//TODO: bulk assign
this.assignmentManager.moveAsync(plan);
rpCount++;
balanceThrottling(balanceStartTime + rpCount * balanceInterval, maxRegionsInTransition,
cutoffTime);
// if performing next balance exceeds cutoff time, exit the loop
if (rpCount < plans.size() && System.currentTimeMillis() > cutoffTime) {
// TODO: After balance, there should not be a cutoff time (keeping it as
// a security net for now)
LOG.debug("No more balancing till next balance run; maxBalanceTime="
+ this.maxBlancingTime);
break;
}
}
}
...
}
// If LoadBalancer did not generate any plans, it means the cluster is already balanced.
// Return true indicating a success.
return true;
}
但是这样每次手动执行,每次均衡的个数不一定能满足要求,那么我们可以通过封装该命令,用脚本来调度执行,具体实现代码:
#! /bin/bash
num=$1
echo "[`date "+%Y-%m-%d %H:%M:%S"`] INFO : RegionServer Start Balancer..."
if [ ! -n "$num" ]; then
echo "[`date "+%Y-%m-%d %H:%M:%S"`] INFO : Default Balancer 20 Times."
num=20
elif [[ $num == *[!0-9]* ]]; then
echo "[`date "+%Y-%m-%d %H:%M:%S"`] INFO : Input [$num] Times Must Be Number."
exit 1
else
echo "[`date "+%Y-%m-%d %H:%M:%S"`] INFO : User-Defined Balancer [$num] Times."
fi
for (( i=1; i<=$num; i++ ))
do
echo "[`date "+%Y-%m-%d %H:%M:%S"`] INFO : Balancer [$i] Times,Total [$num] Times."
echo "balancer"|hbase shell
sleep 5
done
此脚本默认执行20次,可以通过输入整型参数来自定义执行次数。
当HBase集群检查完所有的RegionServer上的Region个数已打要求,那么此时集群的负载均衡操作就已经完成了。如果没有达到要求,可以再次执行上述脚本,直到所有的Region个数在最小值和最大值之间为止。当HBase集群中所有的RegionServer完成负载均衡后,实例中的各个RegionServer上的Region个数分布,如图所示:
此时各个RegionServer节点上的Region个数均在最小值和最大值范围内,HBase集群各个RegionServer节点上的Region处理均衡状态。
3.性能指标
HBase系统有一个非常重要的性能指标,那就是集群处理请求的延时。HBase系统为了反应集群内部处理请求所耗费的时间提供一个工具类即:
org.apache.hadoop.hbase.tool.Canary
此类主要用户检查HBase系统的耗时状态。如果不知道使用方法,通过help命令来查看具体的用法,操作命令:
hbase org.apache.hadoop.hbase.tool.Canary -help
(1)查看集群中每个表中每个Region的耗时情况
hbase org.apache.hadoop.hbase.tool.Canary
(2)查看money表中每个Region的耗时情况,多个表之间使用空格分割
# 查看money表和person表
hbase org.apache.hadoop.hbase.tool.Canary money person
(3)查看每个RegionServer的耗时情况
hbase org.apache.hadoop.hbase.tool.Canary -regionserver dn1
通常情况下我们比较关注每个RegionServer节点的耗时情况,将该命令封装一下,然后打印集群中每个RegionServer的耗时情况,脚本实现:
#########################################################
# 将捕获的RS耗时,写入到InfluxDB中进行存储,用于绘制历史趋势图
#########################################################
#!/bin/bash
post_influxdb_write='http://influxdb:8086/write?db=telegraf_rs'
source /home/hadoop/.bash_profile
for i in `cat rs.list`
do
timespanStr=`(hbase org.apache.hadoop.hbase.tool.Canary -regionserver $i 2>&1) | grep tool.Canary`
timespanMs=`echo $timespanStr|awk -F ' ' '{print $NF}'`
timespan=`echo $timespanMs|awk -F "ms" '{print $1}'`
echo `date +'%Y-%m-%d %H:%M:%S'` INFO : RegionServer $i delay $timespanMs .
currentTime=`date "+%Y-%m-%d %H:%M:%S"`
currentTimeStamp=`date -d "$currentTime" +%s`
insert_sql="regionsever,host=$i value=$timespan ${currentTimeStamp}000000000"
#echo $insert_sql
curl -i -X POST "$post_influxdb_write" --data-binary "$insert_sql"
done
exit
4.总结
维护HBase集群,比如重启某几个RegionServer节点后,可能会发送Region不均衡的情况,这时如果开启自动均衡后,需要立即使当前集群上其他RegionServer上的Region处于均衡状态,那么就可以使用手动均衡操作。另外,HBase集群各个RegionServer的耗时情况,能够反映当前集群的健康状态。
猜你喜欢
大数据和云计算技术周报(第56期)
加入技术讨论群
《大数据和云计算技术》社区群人数已经3000+,欢迎大家加下面助手微信,拉大家进群,自由交流。
欢迎大家通过二维码打赏支持技术社区(英雄请留名,社区感谢您,打赏次数超过108+):
以上是关于深度剖析HBase负载均衡和性能指标的主要内容,如果未能解决你的问题,请参考以下文章