Apache HBase平衡器架构
推荐超级课程:
@TOC
介绍
Apache HBase是一个开源的非关系型分布式数据库,它模仿了Google的Bigtable,并且是用Java编写的。它是Apache软件基金会Apache Hadoop项目的一部分,并且运行在HDFS之上,为Hadoop提供类似Bigtable的能力。
在分布式数据库上下文中,负载平衡器是一个软件组件,它确保负载在节点之间均匀分布,以按照配置确保资源利用率。在下面的图表中,您可以看到在平衡之前表的区域(分片)分布不均,而在平衡之后则均匀分布。这种平衡后的设置确保了每个regionserver接收到的请求数量是相等的,因为托管的区域(分片)数量是相同的。
Apache HBase自带了一些预打包的负载平衡器,例如FavoredStochasticBalancer ,SimpleLoadBalancer ,StochasticLoadbalancer ,RSGroupBasedLoadBalancer 等。这些生产级别的负载平衡器都使用StochasticLoadbalancer作为底层实现,并根据各自的负载平衡器重写了一些功能。在本文中,我们将讨论HBase平衡器的一般架构,特别是StochasticLoadbalancer 。
Apache HBase背景
HBase术语
让我们熟悉一些在阅读过程中可能会经常出现的HBase术语。
表(Tables):HBase表以键值对的形式组织数据行。行按字典顺序排序,最低顺序的行首先出现在表中。每个表被划分为一个或多个区域。
区域(Region):每个表被划分为一系列不相交的连续子集,称为区域。区域是表的基本可用性和分布元素。每个区域都有表中行的按字典顺序排序的子集。
RegionServer:是一个进程,它将区域托管在内存中,服务于客户端rpc调用,其数据通过datanodes存储在hdfs中。
Hmaster:是HBase中的主节点,它协调regionserver执行某些集群范围内的活动,如区域分配、平衡等。
HBase架构
HMaster是HBase的一个中心组件,负责确保HBase集群处于健康状态,其职责包括:
- 将区域分配到regionserver
- 跟踪regionserver的健康状况
- 平衡regionserver的资源利用率
- 清理工作,以避免集群中临时数据的堆积RegionServer负责处理客户端API调用,如puts、gets、updates、deletes等。在任意时刻,每个RegionServer托管一组明确的区域,并且只负责这些托管区域的CRUD操作。根据集群动态变化的需要,如添加新的RegionServer、移除现有的RegionServer等,这些托管的区域可能会发生变化。
负载平衡器:Apache HBase是一个自动分片、自动平衡的水平可扩展键值存储。负载平衡器负责保持集群健康并在不断变化的环境中优化资源利用。
自动分片 — 每个表被划分为称为区域(分片)的不相交连续子集。如果区域(分片)增长足够大,它可以被分裂为两个区域,如果两个相邻的区域收缩足够小(由于TTL过期或数据删除),可以合并为一个区域。这种动态设置的特性需要一个平衡器来确保集群能够适应集群的变化动态。
自动平衡 — 具有水平可扩展能力,添加或移除RegionServer应该自动处理,每个RegionServer都得到适当利用。这可以通过将区域自动移动到新的RegionServer并从移除的RegionServer中移出来实现。这里就是平衡器发挥作用的地方。
Apache HBase平衡器目标
在像HBase这样的分布式数据库上下文中,负载平衡器确保根据定义的配置适当利用工作进程,如RegionServer。
负载分配:
RegionServer的负载可以根据以下参数定义:
- RegionServer接收的API请求与其他RegionServer相比较
- 每个RegionServer每张表托管的区域数量
- 集群中每个机架托管的区域数量
- 每个RegionServer托管的数据量与其他RegionServer相比较
根据特定集群部署的需要,可以配置集群以确保在RegionServer之间分配负载(上述一个或多个定义)。
例如:在上面的图表中——每个RegionServer都有表A的3个区域和表B的7个区域,每个RegionServer总共有10个区域。平衡器应该提供杠杆,以便通过配置根据部署需要控制负载分配。
区域分配:
分配管理器负责将区域分配到RegionServer,这在集群运行的生命周期中持续发生,如表的创建/删除、平衡器运行、区域分裂/合并等。每当分配管理器开始行动时,平衡器的责任是提供正确的计划,以确保维护配置的负载分配标准。
Apache HBase平衡器架构
StochasticLoadbalancer 是Apache HBase预打包的最佳努力平衡器,它具有根据部署的Apache HBase集群需求进行扩展和配置的能力。StochasticLoadbalancer 运行一个模拟,将当前集群状态作为输入,希望满足上一节定义的平衡器目标,并输出新的集群状态。在模拟过程中,平衡器可以使用的一些杠杆包括成本函数和候选生成器,以及一些配置,如迭代次数、不平衡成本阈值等。这些细节将在本节的后面部分详细讨论。
成本函数:是一个平衡器函数,当被调用时会输出一个介于0和1之间的成本值,0表示最平衡,1表示最不平衡,针对特定的标准,如局部性、区域在RegionServer之间的分布等。HBase预打包了大量的成本函数,您可以在这里 找到它们。成本函数的一些例子包括ServerLocalityCostFunction、RackLocalityCostFunction。也可以添加自定义成本函数,您可以在这篇博客 中找到一些详细示例。
候选生成器:是一个平衡器函数,当被调用时会输出以下两种操作之一:
[请提供候选生成器的后续内容,以便我可以完成翻译。]* 在RegionServer rs1上的区域r1与RegionServer rs2上的区域r2进行交换
- 将RegionServer rs1上的区域r1移动到RegionServer rs2
这些操作都有可能改善集群的平衡,即减少特定标准的成本。HBase预打包了大量的候选生成器,您可以在这里 找到它们。候选生成器的一些例子包括RandomCandidateGenerator、LoadCandidateGenerator、LocalityCandidateGenerator。可以添加自定义候选生成器,您可以在这篇博客 中找到一些详细示例。
架构
流程图详情
集群状态: BalancerClusterState 是HBase集群的内存表示,包含所有集群的RegionServer、主机、机架、区域、表等。这被视为计算集群平衡程度和模拟平衡器算法以了解该计划是否可以在应用后改善集群平衡的准确表示。
计算成本: 成本不过是集群平衡好坏的指标。成本越低,平衡越好;成本越高,平衡越差。平衡器配置中配置了一个或多个成本函数及其权重。一些例子包括
- WriteRequestCostFunction :指示写请求是否倾斜,如果倾斜则给出较高值,如果平衡则给出较低值。默认权重为0,因为它默认不启用
- ServerLocalityCostFunction :指示特定区域在RegionServer上托管的数据是否本地可用还是需要从远程获取。较低的成本表示本地可用的数据量较大,较高的成本表示本地可用的数据量较少。默认配置的权重为25
要计算任何时间点的成本,成本函数会遍历每个配置的成本函数,根据该成本函数的算法获取成本,并乘以其配置的权重,然后将所有成本函数的这些值相加。这里的代码参考
迭代次数: 每个平衡器针对每个表运行固定数量的迭代,并乐观地认为可以改善集群的平衡。这些迭代次数是从公式计算得出的 — numRegions x stepsPerRegion (默认800) x numServers
下一步操作: 在每次迭代中,算法会识别一个操作,该操作识别需要改变的集群状态以改善集群的平衡。操作描述在这里 并如下所示
- 在配置的候选生成器中按比例选择一个,与对应的配置成本函数的成本相关。目标是更频繁地选择候选生成器,以更高的概率减少成本。一些例子包括
- LocalityBasedCandidateGenerator :这个候选生成器返回一个操作,该操作选择两个本地性较低的区域并尝试交换它们,这可以改善两个区域的本地性,或者将一个区域从RegionServer移动到另一个RegionServer,从而改善该区域的本地性。
- LoadCandidateGenerator :这个候选生成器有函数来选择负载最重的RegionServer和负载最轻的RegionServer,因此可以返回一个操作,将一个区域从负载最重的服务器移动到负载最轻的服务器。
执行操作:从前一步中选择一个操作,并将选中的操作应用到上面描述的集群状态上。通过应用的操作,平衡器算法能够计算成本,以查看更改是否改善了集群的平衡。
撤销操作:上述描述的操作应用后,如果集群的平衡没有改善,这一步将撤销操作,回到应用操作之前的状态。原则上,这会将集群恢复到应用操作之前的状态。
生成区域计划:区域计划 是表示将一个区域从一个RegionServer移动到另一个RegionServer的实体。从前面的步骤中,平衡器在平衡运行的周期内改变内存中的集群状态,以改善集群的平衡,如果这个集群状态在运行结束时改善了集群的平衡,则必须将其应用于生产集群,这将生成区域计划 列表,作为平衡器函数的输出。
Apache HBase平衡器性能
HBase平衡器默认每隔5分钟 运行一次,可以配置为不同的间隔,也可以通过平衡器命令按需触发。每次平衡器运行并不一定会导致状态改变,只有当不平衡程度超过配置的阈值 时,才会发生状态改变。
在大型Apache HBase部署中,平衡器的性能非常重要,尤其是如果集群有许多rsgroups和多个表。有几个参数会影响平衡运行的性能。其中一些包括:
- 最大迭代次数:每个平衡器运行都会经历一系列特定的迭代,每次迭代都会进行一定程度的平衡。每个步骤都需要一定的时间,因此定义了平衡器的性能。
- 集群不平衡:集群不平衡程度越高,每次迭代需要投入的努力就越大,以达到更好的平衡状态,因此对平衡器性能的影响也越大。
- 区域数量:区域数量越多,执行能够改善平衡的操作的概率就越高,因此对平衡器性能的影响也越大。
- RegionServer数量:RegionServer数量越多,执行能够改善平衡的操作的概率就越高,因此对平衡器性能的影响也越大。
平衡器的性能通过以下参数衡量:
- 最小区域移动:考虑到Apache HBase是一个一致性存储,意味着区域移动将会在非常短的时间内不可用。平衡器的性能将通过从一个RegionServer移动到另一个RegionServer的最少区域数量来衡量,从而减少对客户API调用的影响。
- 最低最终成本:成本就是集群的平衡程度。平衡器的目标是尽可能地将最终集群状态保持在最佳平衡状态。这将确保下次运行平衡器时,由于非常小的失衡,不会再次被调用。
一旦平衡器根据配置生成这些区域移动计划,这些计划就会被交给分配管理器 。分配管理器将根据平衡器共享的计划移动区域,并在过程中限制任何时刻不可用的区域数量。您可以从这里 的代码中阅读更多细节。
以下部分将通过示例展示上述性能因素。
示例:
场景1:
集群配置
- RegionServer数量:8
- 区域数量:45
- 最大迭代次数:1000000
- 成本阈值:0.04
运行详情
- 平衡器运行:30秒
- 计划应用:2分钟41秒
- 运行的总迭代次数:96858
- 初始成本:0.09706592766966382 — 大于成本阈值(0.04)
- 最终成本:0.00756644910689012 — 小于成本阈值(0.04)
- 区域移动:24
更多详细信息可以在日志中找到
[请提供日志内容或链接以供进一步查看]
2025-01-15T13:24:59,505 INFO [RpcServer.default.FPRWQ.Fifo.read.handler=79,queue=69,port=16000] balancer.StochasticLoadBalancer: 因为集群有空闲服务器,正在运行平衡器。函数成本=RegionCountSkewCostFunction : (乘数=1000.0, 不平衡=0.11749633049682573, 需要平衡); MoveCostFunction : (乘数=7.0, 不平衡=0.0); ServerLocalityCostFunction : (乘数=25.0, 不平衡=0.0317494613441357); RackLocalityCostFunction : (乘数=15.0, 不平衡=0.03156492165548885); TableSkewCostFunction : (乘数=35.0, 不平衡=0.11749633049682573, 需要平衡); RegionReplicaHostCostFunction : (不需要); RegionReplicaRackCostFunction : (不需要); ReadRequestCostFunction : (乘数=5.0, 不平衡=0.4880731811586446, 需要平衡); WriteRequestCostFunction : (乘数=5.0, 不平衡=0.4440815926682966, 需要平衡); MemStoreSizeCostFunction : (乘数=5.0, 不平衡=0.1448451182915061, 需要平衡); StoreFileCostFunction : (乘数=5.0, 不平衡=0.15045621507200435, 需要平衡); FavoredDeadNodeCostFunction : (乘数=400.0, 不平衡=0.0); FavoredNodeImbalanceCostFunction : (乘数=100.0, 不平衡=0.26486423168198797, 需要平衡);
2025-01-15T13:24:59,505 INFO [RpcServer.default.FPRWQ.Fifo.read.handler=79,queue=69,port=16000] balancer.StochasticLoadBalancer: 开始 StochasticLoadBalancer.balancer,初始加权平均不平衡=0.09706592766966382,函数成本=RegionCountSkewCostFunction : (乘数=1000.0, 不平衡=0.11749633049682573, 需要平衡); MoveCostFunction : (乘数=7.0, 不平衡=0.0); ServerLocalityCostFunction : (乘数=25.0, 不平衡=0.0317494613441357); RackLocalityCostFunction : (乘数=15.0, 不平衡=0.03156492165548885); TableSkewCostFunction : (乘数=35.0, 不平衡=0.11749633049682573, 需要平衡); RegionReplicaHostCostFunction : (不需要); RegionReplicaRackCostFunction : (不需要); ReadRequestCostFunction : (乘数=5.0, 不平衡=0.4880731811586446, 需要平衡); WriteRequestCostFunction : (乘数=5.0, 不平衡=0.4440815926682966, 需要平衡); MemStoreSizeCostFunction : (乘数=5.0, 不平衡=0.1448451182915061, 需要平衡); StoreFileCostFunction : (乘数=5.0, 不平衡=0.15045621507200435, 需要平衡); FavoredDeadNodeCostFunction : (乘数=400.0, 不平衡=0.0); FavoredNodeImbalanceCostFunction : (乘数=100.0, 不平衡=0.26486423168198797, 需要平衡); 计算的最大步骤数=1000000
2025-01-15T13:25:29,505 INFO [RpcServer.default.FPRWQ.Fifo.read.handler=79,queue=69,port=16000] balancer.StochasticLoadBalancer: 完成计算新的移动计划。计算耗时30001毫秒,尝试了96858次不同的迭代。找到一个解决方案,移动了24个区域;从不平衡的0.09706592766966382降低到新的不平衡的0.00756644910689012。函数成本=RegionCountSkewCostFunction : (乘数=1000.0, 不平衡=0.0); MoveCostFunction : (乘数=7.0, 不平衡=0.0); ServerLocalityCostFunction : (乘数=25.0, 不平衡=0.00129180719124955); RackLocalityCostFunction : (乘数=15.0, 不平衡=2.154169598744815E-5); TableSkewCostFunction : (乘数=35.0, 不平衡=0.0); RegionReplicaHostCostFunction : (不需要); RegionReplicaRackCostFunction : (不需要); ReadRequestCostFunction : (乘数=5.0, 不平衡=0.4880731811586446, 需要平衡); WriteRequestCostFunction : (乘数=5.0, 不平衡=0.4404013422882793, 需要平衡); MemStoreSizeCostFunction : (乘数=5.0, 不平衡=0.03698265906832484); StoreFileCostFunction : (乘数=5.0, 不平衡=0.00893377731407148); FavoredDeadNodeCostFunction : (乘数=400.0, 不平衡=0.0); FavoredNodeImbalanceCostFunction : (乘数=100.0, 不平衡=0.07216878364870322, 需要平衡);
场景2:
集群配置
- RegionServer数量:20
- 区域数量:145
- 最大迭代次数:1000000
- 成本阈值:0.04
运行详情
- 平衡器运行:30秒
- 计划应用:3分钟 55秒
- 运行的总迭代次数:286186
- 初始成本:0.051534985526077624 — 大于成本阈值(0.04)
- 最终成本:0.00451554446302049 — 小于成本阈值(0.04)
- 区域移动:72
更多详细信息可以在日志中找到
2025-01-15T12:35:25,316 INFO [RpcServer.default.FPRWQ.Fifo.read.handler=58,queue=58,port=16000] balancer.StochasticLoadBalancer: 因为集群有空闲服务器,正在运行平衡器。函数成本=RegionCountSkewCostFunction : (乘数=1000.0, 不平衡=0.045157153600191915, 需要平衡); MoveCostFunction : (乘数=7.0, 不平衡=0.0); ServerLocalityCostFunction : (乘数=25.0, 不平衡=0.0); RackLocalityCostFunction : (乘数=15.0, 不平衡=0.0); TableSkewCostFunction : (乘数=35.0, 不平衡=0.045157153600191915, 需要平衡); RegionReplicaHostCostFunction : (不需要); RegionReplicaRackCostFunction : (不需要); ReadRequestCostFunction : (乘数=5.0, 不平衡=0.4880731811586446, 需要平衡); WriteRequestCostFunction : (乘数=5.0, 不平衡=0.4404013422882793, 需要平衡); MemStoreSizeCostFunction : (乘数=5.0, 不平衡=0.03698265906832484); StoreFileCostFunction : (乘数=5.0, 不平衡=0.00893377731407148); FavoredDeadNodeCostFunction : (乘数=400.0, 不平衡=0.0); FavoredNodeImbalanceCostFunction : (乘数=100.0, 不平衡=0.07216878364870322, 需要平衡);
```n : (乘数=35.0, 不平衡=0.09843482696369105, 需要平衡); RegionReplicaHostCostFunction : (不需要); RegionReplicaRackCostFunction : (不需要); ReadRequestCostFunction : (乘数=5.0, 不平衡=0.07299710811422996, 需要平衡); WriteRequestCostFunction : (乘数=5.0, 不平衡=0.06444159540603787, 需要平衡); MemStoreSizeCostFunction : (乘数=5.0, 不平衡=0.0663331405323534, 需要平衡); StoreFileCostFunction : (乘数=5.0, 不平衡=0.06994543556027737, 需要平衡); FavoredDeadNodeCostFunction : (乘数=400.0, 不平衡=0.0); FavoredNodeImbalanceCostFunction : (乘数=100.0, 不平衡=0.32588087870790766, 需要平衡);
2025-01-15T12:35:25,316 INFO [RpcServer.default.FPRWQ.Fifo.read.handler=58,queue=58,port=16000] balancer.StochasticLoadBalancer: 启动 StochasticLoadBalancer.balancer,初始加权平均不平衡=0.051534985526077624,函数成本=RegionCountSkewCostFunction : (乘数=1000.0, 不平衡=0.045157153600191915, 需要平衡); MoveCostFunction : (乘数=7.0, 不平衡=0.0); ServerLocalityCostFunction : (乘数=25.0, 不平衡=0.0); RackLocalityCostFunction : (乘数=15.0, 不平衡=0.0); TableSkewCostFunction : (乘数=35.0, 不平衡=0.09843482696369105, 需要平衡); RegionReplicaHostCostFunction : (不需要); RegionReplicaRackCostFunction : (不需要); ReadRequestCostFunction : (乘数=5.0, 不平衡=0.07299710811422996, 需要平衡); WriteRequestCostFunction : (乘数=5.0, 不平衡=0.06444159540603787, 需要平衡); MemStoreSizeCostFunction : (乘数=5.0, 不平衡=0.0663331405323534, 需要平衡); StoreFileCostFunction : (乘数=5.0, 不平衡=0.06994543556027737, 需要平衡); FavoredDeadNodeCostFunction : (乘数=400.0, 不平衡=0.0); FavoredNodeImbalanceCostFunction : (乘数=100.0, 不平衡=0.32588087870790766, 需要平衡); 计算的最大步骤数=2320000
2025-01-15T12:35:55,314 INFO [RpcServer.default.FPRWQ.Fifo.read.handler=58,queue=58,port=16000] balancer.StochasticLoadBalancer: 完成计算新的移动计划。计算耗时30001毫秒,尝试了286186个不同的迭代。找到了一个移动72个区域的解决方案;从计算的不平衡0.051534985526077624降低到新的不平衡0.00451554446302049。函数成本=RegionCountSkewCostFunction : (乘数=1000.0, 不平衡=0.0); MoveCostFunction : (乘数=7.0, 不平衡=0.0); ServerLocalityCostFunction : (乘数=25.0, 不平衡=-4.263256414560601E-14); RackLocalityCostFunction : (乘数=15.0, 不平衡=0.0); TableSkewCostFunction : (乘数=35.0, 不平衡=0.0); RegionReplicaHostCostFunction : (不需要); RegionReplicaRackCostFunction : (不需要); ReadRequestCostFunction : (乘数=5.0, 不平衡=0.042606873332493696, 需要平衡); WriteRequestCostFunction : (乘数=5.0, 不平衡=0.021935418851234647); MemStoreSizeCostFunction : (乘数=5.0, 不平衡=0.02556733220584559); StoreFileCostFunction : (乘数=5.0, 不平衡=0.02333748822907091); FavoredDeadNodeCostFunction : (乘数=400.0, 不平衡=0.0); FavoredNodeImbalanceCostFunction : (乘数=100.0, 不平衡=0.06666666666666667, 需要平衡);
结论
平衡器是支持自动分片和自动平衡的数据库的核心组件之一。设计平衡器需要考虑多个方面,包括性能、可扩展性、可配置性、简单性等等。Apache HBase在这些非功能性要求之间取得了很好的平衡,并且仍然功能性满足平衡器的需求。