NoSQL中负载均衡系统如何解决热点问题,提高可用性?
一、背景
表格存储(原名OTS)是一款阿里自研的NoSQL多租户分布式数据库,本文主要会分享在表格存储中,负载均衡系统如何解决热点问题。
1、表格存储架构
下图是表格存储系统最基本的一个架构图:
实际上,表格存储还有很多其他的模块,这里我们主要看下和本文内容相关的部分,并且也是最核心的一部分。
从下往上看,表格存储是基于飞天内核的产品,飞天内核主要提供了分布式共享存储、分布式锁服务、通信组件等基础功能。
然后上面是表格存储的引擎部分,主要由worker和master组成,一个集群中有少量的master和大量的worker,master负责管理worker的状态,并将partition调度到各个worker上提供对外的服务。
再上层是前端组件,提供统一的http服务,并把这些请求转发到worker上。
2、负载均衡相关的背景
在表格存储系统中,我们会对用户数据按分片键进行切分,切分之后的一个分片,我们叫做partition,它是表格存储系统里面,调度的基本单元,所有调度都是基于partition的。
partition可以做如下操作:
- move:把partition从一台机器迁移到另外一台机器上进行服务。
- split:把一个partition分裂成两个partition。
- merge:把两个partition合并成一个partition。
- group:group是隔离partition的主要手段,举个例子:它一般是先将一批worker加入到指定group中,然后将instance、table或者partition也加入到该group,完成这些操作后,系统会将属于指定group的instance、table的所有partition和显示指定group的partition都固定调度在该group的worker上,达到隔离的目的。说简单一点就是我给一些用户分配指定的机器,这些机器专门给这些用户服务。
上述的所有对partition的操作,包括move、split、merge、group,都是秒级别的。
3、热点问题
在NoSQL多租户系统中经常遇到的热点问题,主要分为以下两类:
1)用户访问热点
用户访问热点又分为合理的突发式访问热点,以及不合理的突发式访问热点:
- 合理的突发式访问指的是,用户的表设计合理,只是业务量突然上涨导致的,比如说大促。
- 不合理的访问热点指的是,用户的表设计不合理,在这个基础上,业务量上涨导致的热点。
2)机器热点
机器的热点问题指的是,该机器的cpu,网络流量由于某些原因突然变高,该机器资源成为瓶颈,导致的热点。
通常,热点问题很难处理,主要有如下原因:
- 定位难:系统中的信息统计不够全,导致了出现热点问题,很难定位,只能靠猜。
- 解决难:即使定位了问题,有可能还很难处理,主要原因是系统中处理热点的手段不足。
- 人工处理慢:即使能定位,也能解决,但是处理时间太长,严重影响服务的可用性。
在表格存储系统中,对上述几个难点,我们都有对应的手段来解决。针对信息不全,定位难的问题,我们系统中有详细的partition级别统计信息,并且秒级别的partition move、split、merge、group功能也能很好地处理问题。
最后,我们开发了一套负载均衡系统,它能收集信息、分析信息、解决问题,做到热点问题快速自动化解决,不需要人工参与。
二、负载均衡系统
接下来我们来看看表格存储中的负载均衡系统是如何自动化解决问题的。首先介绍负载均衡系统的架构,然后分模块来详细阐述各个模块的功能。
1、负载均衡架构
下图是负载均衡系统的架构图:
它主要包括LBAgent和LBMaster两个角色,其中LBAgent和worker进程部署在同一台机器上,它负责收集这台机器上的所有信息指标,包括worker进程和其他相关的进程。收集之后,在内存中维护近期的数据,同时把数据异步地写到外部存储系统中,在图中我们叫做MetricStore。
然后往上一层的模块是OpsServer,它是很薄的一层封装,主要提供了所有命令的http服务。
再往上是LBMaster。LBMaster中的collector模块通过OpsServer实时收集LBAgent的数据,并把近期的数据维护在内存中,这个模块我们叫做MetricsTable,它主要提供各种数据聚合和top排序的功能。
在线分析模块(OnlineAnalyzer)会实时分析MetricsTable中的近期数据,来检测是否有热点等异常的问题。如果有,则对这些信息进行进一步分析,来产生相应的解决action,并把这些action交给执行模块(Executor)。执行模块通过OpsServer把相应的action发送给worker或者master,由worker或者master执行action,最终解决热点问题。
同时,LBMaster还有一个离线分析模块(OfflineAnalyzer),这个模块主要从外部存储系统MetricStore中读取信息,并对这些信息进行分析,以检测系统中是否有潜在问题,如果有,则对这些问题产生相对应的action,同样通过OpsServer交给worker或者master来执行,最终解决潜在的问题,做到防患于未然。
无论是在线分析模块还是离线分析模块,分析出的结果和action都会写入到一个外部存储系统中,这里叫做ResultDataStore,主要为了人工或者系统对这些action做进一步的分析。
最后,LBMaster还提供一个白屏化的管控平台,这个管控平台能够实时查询LBMaster中的各种数据,同时也可以通过它来发送人工运维命令。
2、信息收集模块
信息收集模块有两个重点:
- 信息尽可能地全。
- 不能影响主路径的性能。
在表格存储系统中,任何一个模块处理请求时,都会顺便收集该模块的相关信息,这些信息会随着请求一起流动。如下图:
在图中,经过rpc模块,就会收集rpc模块中的统计信息;经过m1、m2模块时,也会一起收集m1、m2模块的信息;最终在返回用户前,异步地把信息推送到一个后台计算模块,这个模块会在后台用很少量的资源来汇总这些信息,并定期把信息推送给LBAgent。
由于这个后台计算模块,不在主路径上执行,是异步执行的,并且只占用少量的资源,可能只有一个核的cpu,所以对主路径的性能影响极小。
通过这种方式,我们既保证了能收集到各个模块的信息,同时尽可能地减少了对主路径性能的影响。
3、LBAgent模块
LBAgent模块主要有三个功能:
- 收集单机上的所有信息。
- 对这些信息进行预聚合。
- 异步地持久化所有信息。
如图所示:
LBAgent端的接收信息模块不仅会收到worker进程的信息,还会收到系统中其他相关进程的信息。收到信息之后,LBAgent在内存中维护近期收集的信息,同时异步地将信息持久化到外部存储系统(MetricStore)中,以保存更长时间。LBMaster通过接口周期性地获取LBAgent内存中维护的信息。
4、LBMaster模块
LBMater模块是负载均衡系统最核心的模块。它的主要功能是:
- 收集集群所有信息。
- 多维度信息的top查询,包括但不限于错误率、延时、qps等信息。
- 分析信息、产生action、执⾏action。
- 自我反馈策略的有效性。
我们结合下图来看:
collector负责收集信息,MetricsTable负责多维度信息的top查询,OnlineAnalyzer和OfflineAnalyzer模块分别分析在线实时信息和离线信息,ActionExecutor模块负责执行分析模块产出的action。
在action执行完成之后,ActionEvaluation模块会比较action执行前后的信息变化来判断这个action的效果,通过这种方式来反馈该action是否真正解决了问题。
此外,LBMaster还有一个配置相关的模块,各个模块都有灵活的配置,配置存储在外部存储系统中,配置模块会读取这些信息,然后同步给所有模块。所有模块的配置都支持实时地动态更新。
最后总结下,LBMaster的有如下特点:
- 冷热数据存储分离:其中热数据存储在内存中,保留最近小时级别的数据,冷数据存储在外部存储系统中,可以按需保留数月甚至几年。
- 离线在线模块分离:从架构图中可以看到,离线模块和在线模块的路径不会相互影响。
- 配置灵活、动态加载:LBMaster支持灵活的配置,并且能够不升级动态加载。
- 白屏化操作及信息展示:LBMaster中的所有信息都支持白屏化的展示,并且还可以白屏化发送运维命令给LBMaster,LBMaster会执行这些运维命令。
- 高可用:由于LBMaster在整个负载均衡系统中起着核心的作用,所以它还要做到高可用。
接下来,我们从在线和离线两方面来看下负载均衡处理问题的情况。
5、在线分析路径
首先看在线分析路径。在线分析主要是分析短期信息,发现问题,最终解决问题,它主要有如下特点:
- 数据实时性要求极高,分析频率高,秒级别发现并处理问题。
- 数据量小、全部维护在内存表中。
- 主路径不依赖任何外部系统。
我们从架构图来看在线分析路径,可以发现:
- 整个数据流路径,从worker到LBAgent最后到LBMaster,以及控制流路径从LBMaster到worker、master,除了分析的结果会异步地写外部存储系统外,不涉及任何外部系统。
- 并且分析结果写外部存储系统的失败也不影响主路径的执行,它是一个异步的操作。所有这些设计都是为了满足实时性的要求。
在表格存储系统中有很多在分析的策略,下面举两个例子:
例1:热点问题导致读写队列满报错
- 首先,负载均衡系统分析信息会发现worker1的队列被打满,报错,到达了单partition的服务瓶颈。
- 然后进一步分析发现可以做split来解决这个问题,因此负载均衡系统发出split partition1的action,action通过worker和master执行后,partition1被切分为partition11和partition12,并调度到两台机器上服务。通过这种方式解决了热点问题。
例2:机器资源满导致的问题
负载均衡系统分析信息发现worker1的资源被打满,然后开始分析原因,最后发现是partition2导致的,进一步分析发现partition2的访问模式有问题。
比如说是单partitionkey的访问,或者顺序写访问,这种访问模式,split不能解决问题,所以负载均衡系统发出隔离partition2的action,action执行后,partition2被单独隔离到一台机器上服务。
此时,partition2不影响其他任何用户,并且也独享整体机器的资源,系统给它提供了最大的服务能力。
6、离线分析路径
与在线分析路径恰好相反的是,离线分析主要是分析长期信息,发现潜在的问题,并最终消除这些潜在问题,做到防患于未然。和在线路径相比,它的特点是:
- 数据实时性要求低,分析频率低,小时级别发现并处理问题。
- 由于数据量大,信息维护在外部存储系统中。
- 计算量大,所以分析的时候可以依赖外部分析系统。
从架构图来看,离线分析路径的数据来源于外部存储系统,并且由于分析的数据量很大,它会先借助外部分析系统做初步的分析,然后把分析结果写入到一张结果表中。
最后LBMaster的离线分析模块,对结果表中的信息做进一步的分析,然后发现问题,产生action。借助外部分析系统,大大减少了LBMaster的资源消耗,也大大增加了分析的能力。
接下来简单介绍两个离线分析策略的例子:
首先是auto merge,在NoSQL系统中,有部分的partition刚开始访问量很大,所以被切分成很多partition,随后这些partition的访问量可能会很低,甚至几乎没有,那么我们就可以将这些partition进行merge,来节约系统资源。
但是,不能通过短期统计数据判断一个partition访问量低就对它做merge,因为有些partition的访问模式是周期性的,所以要通过长期统计数据来判断一个partition能否做merge。
另外一个例子是,我们可以通过对长期数据的分析来预测某些用户的访问峰值,提前做好资源的调整。
7、效果展示
接下来,展示一些负载均衡系统上线后的效果。选取的都是有明显热点的业务,所以效果都非常明显。
如下图所示,负载均衡系统上线后,读操作的错误率和延时明显降低,吞吐量明显提高:
如下图所示,负载均衡系统上线后,写操作的错误率明显降低,并且在发现热点的时候,即错误率突然升高时,能立刻处理掉:
三、总结
最后从我自己做负载均衡系统的实践中总结了几点经验:
每个模块的信息统计是根本
如果没有信息统计,或者信息统计不全,都会导致问题定位不出来或是定位错误,整个负载均衡系统都无从谈起。并且这部分的工作量绝对不小,不是很简单就能做到信息全,并且也几乎不影响性能的。
把人工处理自动化是最高效的策略
很多人刚开始都会觉得负载均衡要用到非常多的机器学习算法,这个可能是对的。
但是对于前期来说,我们把人工处理方式来进行自动化处理,可能就能解决90%以上的线上问题,并不需要高大上的机器学习算法。在经过这个阶段之后,一些难点问题,或者预测性的策略方面,再去考虑机器学习的东西。
策略配置丰富,控制灵活
每个策略都要有一些阈值或者条件,这些条件都不能写死在系统中,都要由配置的方式来传入,因为线上的情况差异非常大,只有这样才能有机会针对不同的业务、不同的场景进行配置定制。
系统快速迭代,支持差异化配置
负载均衡系统是一个要求快速迭代的系统,比如今天发现线上一类问题,就需要尽快写出策略上线,来解决线上的问题。
再者,由于每个业务的特点不同,访问模式的差异非常大,对可用性的要求也会有很大区别。
所以这里就需要非常灵活的配置,对于不同的业务,也许是同一个策略都会需要不同的配置才能达对这个业务而言的最优效果。
Q & A
Q1:请问表的统计信息都统计些什么?既然有工作者队列,为什么还需要担心处理热点问题?
A1:在系统中,有部分队列不是独享的,可能是整个进程所有partition都共享的,如果一个partition出现了热点访问,占用了所有的资源,可能会导致这台机器上所有partition的访问都受到影响。
Q2:那么不采用hash环的分布式策略,比起明确分区键值有什么坏处?为什么要选用后者?
A2:hash分片最大的问题是,一旦确定之后动态调整比较困难,基于分片键的方式,能比较容易做到动态调整,比如split。而hash分片,如果刚开始分片有问题,后续再调整就比较困难。
Q3:我们这边用的是RabbitMQ,没有想过要另找一套的思路。当时自己创建这个的时候有没有参考别的解决方案?然后如何抉择的?
A3:你这里的队列服务可能和我说的不太一样。如果你们是基于队列服务做得系统,那么队列服务相关的负载均衡你们基本上就无能为力,要看队列服务这个产品来做,如果你们自己的系统本身也有热点问题,那么本次分享应该对你有所帮助。
欢迎工作一到五年的Java工程师朋友们加入Java程序员开发: 721575865
群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!