hmater负责把region均匀到各个region server 。hmaster中有一个线程任务是专门处理负责均衡的,默认每隔5分钟执行一次。

每次负载均衡操作可以分为两步:

  • 生成负载均衡计划表
  • Assignment Manager 类执行计划表

负载均衡方法入口

 

以下代码的Hbase版本为0.96.2

在org.apache.hadoop.hbase.master.balancer.BalancerChore中

  1. public BalancerChore(HMaster master) {
  2.  
  3. super(master.getServerName() + "-BalancerChore",
  4.  
  5. master.getConfiguration().getInt("hbase.balancer.period", 300000),
  6.  
  7. master);//hbase.balancer.period 为负载均衡方法执行的周期,毫秒为单位,hbaser-site.xml中可以设置
  8.  
  9. this.master = master;
  10.  
  11. }
  12.  
  13. @Override
  14.  
  15. protected void chore() {
  16. try {
  17.  
  18. master.balance();//执行负载均衡方法
  19.  
  20. } catch (IOException e) {
  21.  
  22. LOG.error("Failed to balance.", e);
  23.  
  24. }
  25. }
  26.  
  27. //执行负载均衡的入口。
  28.  
  29. public void run() {
  30. try {
  31. boolean initialChoreComplete = false;
  32. while (!this.stopper.isStopped()) {//stopper是Hmaster service,这里判断Hmaster是否是正常状态。
  33. long startTime = System.currentTimeMillis();
  34. try {
  35. if (!initialChoreComplete) {
  36. initialChoreComplete = initialChore();//在循环开始前,执行初始化方法,这里默认返回true;
  37. } else {
  38. chore();//执行负载均衡方法
  39. }
  40. } catch (Exception e) {
  41. if (this.stopper.isStopped()) {
  42. continue;
  43. }
  44. }
  45. this.sleeper.sleep(startTime);
  46. }
  47. ...
  48. }

负载均衡代码:

  1. org.apache.hadoop.hbase.master.HMaster
  2.  
  3. public boolean balance() throws IOException {
  4. // 如果master没有被初始化,则不执行初始化操作
  5. if (!this.initialized) {
  6. LOG.debug("Master has not been initialized, don't run balancer.");
  7. return false;
  8. }
  9.  
  10. //只能同时跑一个负载均衡方法
  11. if (!this.loadBalancerTracker.isBalancerOn()) return false;
  12. // Do this call outside of synchronized block.
  13. int maximumBalanceTime = getBalancerCutoffTime();
  14. synchronized (this.balancer) {
  15. //如果有region处于splitting状态,则不跑负载均衡方法。
  16. if (this.assignmentManager.getRegionStates().isRegionsInTransition()) {
  17. Map<String, RegionState> regionsInTransition =
  18. this.assignmentManager.getRegionStates().getRegionsInTransition();
  19. ...
  20. return false;
  21. }
  22.  
  23. if (this.serverManager.areDeadServersInProgress()) {//如果有挂掉的region server则不执行负载均衡。
  24. LOG.debug("Not running balancer because processing dead regionserver(s): " +
  25. this.serverManager.getDeadServers());
  26. return false;
  27. }
  28. ...
  29. Map<TableName, Map<ServerName, List<HRegionInfo>>> assignmentsByTable =
  30. this.assignmentManager.getRegionStates().getAssignmentsByTable();//获取table下面的region server 和region。
  31. List<RegionPlan> plans = new ArrayList<RegionPlan>();
  32. //Give the balancer the current cluster state.
  33. this.balancer.setClusterStatus(getClusterStatus());//设置当前集群的状态
  34. for (Map<ServerName, List<HRegionInfo>> assignments : assignmentsByTable.values()) {//可以看到,负载均衡方法是以每个table作为负载均衡的依据的。
  35. List<RegionPlan> partialPlans = this.balancer.balanceCluster(assignments);//获取负载均衡计划表
  36. if (partialPlans != null) plans.addAll(partialPlans);
  37. }
  38. ...
  39.  
  40. if (plans != null && !plans.isEmpty()) {
  41. for (RegionPlan plan: plans) {
  42. ...
  43. this.assignmentManager.balance(plan);//根据执行计划表的迁移内容。
  44. ...
  45. }
  46. }
  47. }
  48.  
  49. // If LoadBalancer did not generate any plans, it means the cluster is already balanced.
  50. // Return true indicating a success.
  51. return true;
  52. }

从代码可以看到负载均衡是根据每个table来的

在以下几种状态下,负载平衡方法不会执行:

  • 如果master没有被初始化
  • 当前已经有负载均衡方法在跑了
  • 当前有region处于splitting状态
  • 当前集群中有挂掉的region server

生成RegionPlan表:

org.apache.hadoop.hbase.master.balancer.StochasticLoadBalancer

生成regionPlan表用的StochasticLoadBalancer. balanceCluster(Map<ServerName, List<HRegionInfo>> clusterState)这个方法,这个方法比较特别也比较有意思,首先,StochasticLoadBalancer 有一套计算某一table下cluster load(集群负载)评分的算法,得出的值越低表明负载越合理。这套算法是根据以下几个维度来计算得出的:

  • Region Load //每个regin server 的region 数目
  • Table Load
  • Data Locality //数据本地性
  • Memstore Sizes //memstore大小
  • Storefile Sizes

首先对单个region server 根据上面5个维度计算得出评分x(0<=x<=1),然后把同一table下所有region server评分加起来,就是当前table的cluster load评分。这个评分越低表明越合理。

然后它还有三种调节cluster load 的方法:

  • RandomRegionPicker
  • LoadPicker
  • LocalityPicker

RandomRegionPicker 随机交换策略。在虚拟cluster中(虚拟cluster只作为记录用,不会涉及实际的region 迁移操作。cluster包含某个table下所有的region server的相关信息,以及region server下的regions.)随机选出两个region server ,然后分别在region server 中在 随机获取一个region,然后这两个region server下的region交换一下,然后再计算评分,如果得出的评分较低的话,表明这两个region 交换是有利于集群的负载均衡的,保留这个改变。否则,还原到之前的状态,两个region再交换下region server 。其中拥有比较少regions的region server 可能随机出一个空,实际情况,就是变成了迁移region,不再是交换region。

LoadPicker ,region数目均衡策略。在虚拟cluster中,首先获取region数目最多和最少的两个region server ,这样能使两个region server 最终的region数目更加的平均。后面的流程和上面的一样。

LocalityPicker ,本地性最强的均衡策略。本地性的意思是,Hbase底层的数据其实是存放在HDFS上面的,如果某个region的数据文件存放在某个region server 的比例比其他的region server 都要高,那么称这个region server是该region的最高本地性region server 。在该策略中,首先随机出一个region server 以及其下面的region 。然后找到这个region本地性最高的region server 。本地性最高的region server再随机出一个region server。这两个region server 后面的流程和上面的一样。

具体流程如下:

0. 是否需要进行负载均衡,是根据当前region server拥有的region数目来判断的

  1. protected boolean needsBalance(ClusterLoadState cs) {
  2. ...
  3. float average = cs.getLoadAverage(); // for logging 获取cluster中region server平均拥有的region数目
  4. int floor = (int) Math.floor(average * (1 - slop));//slop默认是0.2,可接受范围的最低值
  5. int ceiling = (int) Math.ceil(average * (1 + slop));//最高值
  6. if (!(cs.getMaxLoad() > ceiling || cs.getMinLoad() < floor)) {//如果cluster的最多和最少region的region server不在范围内,返回false表明需要进行负载均衡算法。
  7. ...
  8. return false;
  9. }
  10. return true;
  11. }

1.计算当前cluster的分数。简单来说是这样的,在每一个维度中,计算region server 的cost值,最终根据 (权重*cost值) 加起来的就是总得分,这得分越小表示越均衡,每个region server之间的差异越小。这个cost值是由cluster的(最大差值/(当前差值-最小差值))得出的。

  1. /* 计算cluster的总得分*/
  2.  
  3. protected double computeCost(Cluster cluster, double previousCost) {
  4. double total = 0;for (CostFunction c:costFunctions) {//CostFunction 根据某个维度计算分数 ,costFunctions的实现见下面代码。
  5. if (c.getMultiplier() <= 0) {//multiplier是权重。
  6. continue;
  7. }
  8. total += c.getMultiplier() * c.cost(cluster);//权重*当前维度的评分
  9. if (total > previousCost) {
  10. return total;
  11. }
  12. }
  13. return total;
  14. }
  15.  
  16. //costFunctions 初始化
  17. regionLoadFunctions = new CostFromRegionLoadFunction[] {
  18. new ReadRequestCostFunction(conf),//读请求维度评分
  19. new WriteRequestCostFunction(conf),//写请求维度评分
  20. new MemstoreSizeCostFunction(conf),//memstore 大小维度评分
  21. new StoreFileCostFunction(conf)//StoreFile 维度评分
  22. };
  23.  
  24. costFunctions = new CostFunction[]{
  25. new RegionCountSkewCostFunction(conf),//region 数目 维度评分
  26. new MoveCostFunction(conf),//迁移region 维度评分
  27. localityCost,//本地相关 维度评分
  28. new TableSkewCostFunction(conf), //表 维度评分
  29. regionLoadFunctions[0],
  30. regionLoadFunctions[1],
  31. regionLoadFunctions[2],
  32. regionLoadFunctions[3],
  33. };

取其中RegionCountSkewCostFunction 作为例子:

  1. public static class RegionCountSkewCostFunction extends CostFunction {
  2. private static final String REGION_COUNT_SKEW_COST_KEY =
  3. "hbase.master.balancer.stochastic.regionCountCost";
  4. private static final float DEFAULT_REGION_COUNT_SKEW_COST = 500;//默认权重为500
  5. private double[] stats = null;
  6. RegionCountSkewCostFunction(Configuration conf) {
  7. super(conf);
  8. // Load multiplier should be the greatest as it is the most general way to balance data.
  9. this.setMultiplier(conf.getFloat(REGION_COUNT_SKEW_COST_KEY, DEFAULT_REGION_COUNT_SKEW_COST));//设置权重
  10. }
  11. @Override
  12. double cost(Cluster cluster) {
  13. if (stats == null || stats.length != cluster.numServers) {
  14. stats = new double[cluster.numServers];
  15. }
  16. for (int i =0; i < cluster.numServers; i++) {
  17. stats[i] = cluster.regionsPerServer[i].length;//当前维度是根据每个region server 的region数目作为评分标准。
  18. }
  19. return costFromArray(stats);
  20. }
  21. }
  22.  
  23. protected double costFromArray(double[] stats) {//根据某一维度,每个region server计算出来的评分
  24. double totalCost = 0;
  25. double total = getSum(stats);//计算总分
  26. double mean = total/((double)stats.length);//获取每个region server的平均评分
  27. double count = stats.length;//region server的总数
  28. // Compute max as if all region servers had 0 and one had the sum of all costs. This must be
  29. // a zero sum cost for this to make sense.
  30. //这里假设最坏的情况为(count-1)的region server的评分为0,剩下的一个region server 占有了所有的分数,也就是负载非常不均衡,全部压力都压到同一台region server上面了。计算出最大的差值max。
  31.  
  32. double max = ((count - 1) * mean) + (total - mean);
  33. for (double n : stats) {//计算当前的差值
  34. double diff = Math.abs(mean - n);
  35. totalCost += diff;
  36. }
  37.  
  38. double scaled = scale(0, max, totalCost);//(最大差值/(当前差值-最小差值))
  39. return scaled;
  40. }

2.设置循环的次数和cluster的region server 的总数和region总数有关。最大值mapSteps为1000000。

long computedMaxSteps = Math.min(this.maxSteps, ((long)cluster.numRegions * (long)this.stepsPerRegion * (long)cluster.numServers));

3,4,5,6随机出一个策略,就是上面讲到的 RandomRegionPicker,LoadPicker,LocalityPicker 交换或迁移一次region再计算评分。如果评分比之前要低保留,否则还原。

7,8,9循环进行直到结束,产出List<RegionPlan>。交给assignment manager实际执行迁移region的操作。regionPlan的格式是这样子的:

  1. RegionPlan rp = new RegionPlan(region, initialServer, newServer); //initialServer的region需要迁移到newServer

到此,负载均衡算法结束。在Hbase 0.94的版本里面,默认的负载均衡算法是使用SimpleLoadBalancer类,balanceCluster主要思路上,平均每个region server的region数目,维度相对来说比较单一,在StochasticLoadBalancer 中考虑的维度比较多,在0.96版本里面StochasticLoadBalancer作为了默认的负载均衡的算法的实现。https://issues.apache.org/jira/browse/HBASE-5959 这个patch的评论能看到StochasticLoadBalancer的提交的过程。

Hbase负载均衡流程以及源码的更多相关文章

  1. 客户端负载均衡Ribbon之源码解析

    什么是负载均衡器? 假设有一个分布式系统,该系统由在不同计算机上运行的许多服务组成.但是,当用户数量很大时,通常会为服务创建多个副本.每个副本都在另一台计算机上运行.此时,出现 "Load ...

  2. ribbon负载均衡循环策略源码

    (原) 在用ribbon负载均衡取eureka注册中心中的地址时,默认采用循环策略,例如商品服务有3个,分别为URL1,URL2,URL3,那么在客户端第一次取时,会取到URL1,第二次取时取到URL ...

  3. 深度剖析HBase负载均衡和性能指标

    深度剖析HBase负载均衡和性能指标 在分布式系统中,负载均衡是一个非常重要的功能,HBase通过Region的数量实现负载均衡,即通过hbase.master.loadbalancer.class实 ...

  4. 剖析HBase负载均衡和性能指标

    1.概述 在分布式系统中,负载均衡是一个非常重要的功能,在HBase中通过Region的数量来实现负载均衡,HBase中可以通过hbase.master.loadbalancer.class来实现自定 ...

  5. Android 全面插件化 RePlugin 流程与源码解析

    转自 Android 全面插件化 RePlugin 流程与源码解析 RePlugin,360开源的全面插件化框架,按照官网说的,其目的是“尽可能多的让模块变成插件”,并在很稳定的前提下,尽可能像开发普 ...

  6. Spark Streaming运行流程及源码解析(一)

    本系列主要描述Spark Streaming的运行流程,然后对每个流程的源码分别进行解析 之前总听同事说Spark源码有多么棒,咱也不知道,就是疯狂点头.今天也来撸一下Spark源码. 对Spark的 ...

  7. SpringMVC执行流程及源码分析

    SpringMVC流程及源码分析 前言 ​ 学了一遍SpringMVC以后,想着做一个总结,复习一下.复习写下面的总结的时候才发现,其实自己学的并不彻底.牢固.也没有学全,视频跟书本是要结合起来一起, ...

  8. HDFS追本溯源:HDFS操作的逻辑流程与源码解析

    本文主要介绍5个典型的HDFS流程,这些流程充分体现了HDFS实体间IPC接口和stream接口之间的配合. 1. Client和NN Client到NN有大量的元数据操作,比如修改文件名,在给定目录 ...

  9. Android应用层View绘制流程与源码分析

    1  背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...

随机推荐

  1. InnoSetup打包exe安装应用程序,并添加卸载图标 转

    http://blog.csdn.net/guoquanyou/article/details/7445773 InnoSetup真是一个非常棒的工具.给我的印象就是非常的精干.所以,该工具已经一步步 ...

  2. arcgis 栅格计算器(Spatial Analyst/Raster Calculator)

    栅格计算器中用得到$$相关函数 $$NROWS: the number of rows in the analysis window (行数)$$NCOLS: the number of column ...

  3. iOS 模态视图

    模态视图不是专门的某个类,而是通过视图控制器的presentViewController方法弹出的视图,我们称为模态视图. 模态视图出现的场景一般是临时弹出的窗口,譬如:登录窗口: 模态视图弹出时通过 ...

  4. 推荐一个代码生成工具:freemarker

    freemarker:http://freemarker.org/ 还有velocity:http://velocity.apache.org/

  5. 为什么需要auto_ptr_ref

    这几天开始拜读侯捷先生和孟岩先生的译作<C++标准程序库:自修教程与参考手册> .两位先生确实译功上乘,读得很顺.但是读到P55页关于auto_ptr_ref的讨论,却百思不得其解:为什么 ...

  6. 10gR2-11gR1,11gR2如何干净的清除并重建OCR和表决磁盘

    下面分别讨论10gR2-11gR1和11gR2干净的清除并重建OCR和表决磁盘的方法. 一.10gR2-11gR1干净的清除并重建OCR和表决磁盘的方法 参考METALINK文章:ID 399482. ...

  7. 玩转JS插件系列

    说明:本系列文章只是通过学习JS插件源码来巩固自己的JS知识,不涉及任何商业目的,如有侵犯版权请尽快告知 一.UI 背景 对话框和灯箱 筛选及排序 反馈 弹出层 悬停 布局 图表 加载 圆边 滚动 标 ...

  8. 问题:glGenBuffers()函数没有定义怎么办

    链接glew.lib库,#include <gl/glew.h>. glew是opengl 的扩展库

  9. BZOJ1407_NOI2002_荒岛野人_Savage_C++

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1407 题解:http://www.cnblogs.com/hadilo/p/5951091.h ...

  10. 算法杂货铺——分类算法之朴素贝叶斯分类(Naive Bayesian classification)

    算法杂货铺——分类算法之朴素贝叶斯分类(Naive Bayesian classification) 0.写在前面的话 我个人一直很喜欢算法一类的东西,在我看来算法是人类智慧的精华,其中蕴含着无与伦比 ...